Add BiometricPromptService
The change introduces the following:
- BiometricPrompt communicatates with BiometricPromptService (new)
system service. The service does the decision making for which
biometric modality to use.
- As a result, a lot of logic is moved from <Biometric>Manager
to BiometricPrompt. FingerprintManager now does not care about
BiometricPrompt logic anymore (reverts several P changes).
Face, and all future <Biometric>Service interfaces must be protected by
the signature-only MANAGE_BIOMETRIC permission. Settings, SystemUI, and
BiometricPromptService are their only clients.
Bug: 72825012
Test: BiometricPromptDemo works
Test: Keyguard works
Test: Settings works
Change-Id: I2b7d6eff81bc07950202c50e592d733032523bf0
diff --git a/Android.bp b/Android.bp
index 759014f..39da11f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -151,6 +151,8 @@
":libcamera_client_framework_aidl",
"core/java/android/hardware/IConsumerIrService.aidl",
"core/java/android/hardware/ISerialManager.aidl",
+ "core/java/android/hardware/biometrics/IBiometricPromptService.aidl",
+ "core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricPromptReceiver.aidl",
"core/java/android/hardware/biometrics/IBiometricServiceLockoutResetCallback.aidl",
"core/java/android/hardware/display/IDisplayManager.aidl",
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a352e84..4fb21f3 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -3672,6 +3672,15 @@
public static final String AUDIO_SERVICE = "audio";
/**
+ * Use with {@link #getSystemService(String)}
+ *
+ * @hide
+ * @see #getSystemService(String)
+ * @see com.android.server.biometrics.BiometricPromptService
+ */
+ public static final String BIOMETRIC_PROMPT_SERVICE = "biometric_prompt";
+
+ /**
* Use with {@link #getSystemService(String)} to retrieve a
* {@link android.hardware.fingerprint.FingerprintManager} for handling management
* of fingerprints.
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index ac2f1ce..7d8ff4c 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2281,6 +2281,14 @@
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device has biometric hardware to perform iris authentication.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_IRIS = "android.hardware.iris";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports portrait orientation
* screens. For backwards compatibility, you can assume that if neither
* this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports
diff --git a/core/java/android/hardware/biometrics/BiometricAuthenticator.java b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
index e7c5116..59195dc 100644
--- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java
+++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java
@@ -161,12 +161,6 @@
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {}
/**
- * Called when a biometric is recognized.
- * @param result An object containing authentication-related data
- */
- public void onAuthenticationSucceeded(AuthenticationResult result) {}
-
- /**
* Called when a biometric is valid but not recognized.
*/
public void onAuthenticationFailed() {}
@@ -179,6 +173,29 @@
};
/**
+ * @return true if the biometric hardware is detected.
+ */
+ default boolean isHardwareDetected() {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ /**
+ * @return true if the user has enrolled templates for this biometric.
+ */
+ default boolean hasEnrolledTemplates() {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ /**
+ * @param error
+ * @param vendorCode
+ * @return the error string associated with this error
+ */
+ default String getErrorString(int error, int vendorCode) {
+ throw new UnsupportedOperationException("Stub!");
+ }
+
+ /**
* This call warms up the hardware and starts scanning for valid biometrics. It terminates
* when {@link AuthenticationCallback#onAuthenticationError(int,
* CharSequence)} is called or when {@link AuthenticationCallback#onAuthenticationSucceeded(
@@ -198,10 +215,12 @@
* @param executor An executor to handle callback events
* @param callback An object to receive authentication events
*/
- void authenticate(@NonNull CryptoObject crypto,
+ default void authenticate(@NonNull CryptoObject crypto,
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull AuthenticationCallback callback);
+ @NonNull AuthenticationCallback callback) {
+ throw new UnsupportedOperationException("Stub!");
+ }
/**
* This call warms up the hardware and starts scanning for valid biometrics. It terminates
@@ -221,7 +240,9 @@
* @param executor An executor to handle callback events
* @param callback An object to receive authentication events
*/
- void authenticate(@NonNull CancellationSignal cancel,
+ default void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
- @NonNull AuthenticationCallback callback);
+ @NonNull AuthenticationCallback callback) {
+ throw new UnsupportedOperationException("Stub!");
+ }
}
diff --git a/core/java/android/hardware/biometrics/BiometricConstants.java b/core/java/android/hardware/biometrics/BiometricConstants.java
index 5cf8f45..3612e9d 100644
--- a/core/java/android/hardware/biometrics/BiometricConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricConstants.java
@@ -172,5 +172,5 @@
/**
* @hide
*/
- int BIOMETRICT_ACQUIRED_VENDOR_BASE = 1000;
+ int BIOMETRIC_ACQUIRED_VENDOR_BASE = 1000;
}
diff --git a/core/java/android/hardware/biometrics/BiometricFaceConstants.java b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
index 4aa1e76..c788bc5 100644
--- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java
+++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java
@@ -229,7 +229,8 @@
*
* @hide
*/
- public static final int FACE_ACQUIRED_VENDOR = 13;
+ public static final int FACE_ACQUIRED_VENDOR = 14;
+
/**
* @hide
*/
diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java
index 02bcff5..1cca27d 100644
--- a/core/java/android/hardware/biometrics/BiometricPrompt.java
+++ b/core/java/android/hardware/biometrics/BiometricPrompt.java
@@ -20,14 +20,20 @@
import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.content.DialogInterface;
-import android.content.pm.PackageManager;
-import android.hardware.fingerprint.FingerprintManager;
+import android.os.Binder;
import android.os.Bundle;
import android.os.CancellationSignal;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.internal.R;
import java.security.Signature;
import java.util.concurrent.Executor;
@@ -40,6 +46,8 @@
*/
public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants {
+ private static final String TAG = "BiometricPrompt";
+
/**
* @hide
*/
@@ -208,11 +216,23 @@
}
}
- private PackageManager mPackageManager;
- private FingerprintManager mFingerprintManager;
- private Bundle mBundle;
- private ButtonInfo mPositiveButtonInfo;
- private ButtonInfo mNegativeButtonInfo;
+ private class OnAuthenticationCancelListener implements CancellationSignal.OnCancelListener {
+ @Override
+ public void onCancel() {
+ cancelAuthentication();
+ }
+ }
+
+ private final IBinder mToken = new Binder();
+ private final Context mContext;
+ private final IBiometricPromptService mService;
+ private final Bundle mBundle;
+ private final ButtonInfo mPositiveButtonInfo;
+ private final ButtonInfo mNegativeButtonInfo;
+
+ private CryptoObject mCryptoObject;
+ private Executor mExecutor;
+ private AuthenticationCallback mAuthenticationCallback;
IBiometricPromptReceiver mDialogReceiver = new IBiometricPromptReceiver.Stub() {
@Override
@@ -230,13 +250,48 @@
}
};
+ IBiometricPromptServiceReceiver mBiometricPromptServiceReceiver =
+ new IBiometricPromptServiceReceiver.Stub() {
+
+ @Override
+ public void onAuthenticationSucceeded(long deviceId) throws RemoteException {
+ mExecutor.execute(() -> {
+ final AuthenticationResult result = new AuthenticationResult(mCryptoObject);
+ mAuthenticationCallback.onAuthenticationSucceeded(result);
+ });
+ }
+
+ @Override
+ public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationFailed();
+ });
+ }
+
+ @Override
+ public void onError(long deviceId, int error, String message)
+ throws RemoteException {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationError(error, message);
+ });
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquireInfo, String message) {
+ mExecutor.execute(() -> {
+ mAuthenticationCallback.onAuthenticationHelp(acquireInfo, message);
+ });
+ }
+ };
+
private BiometricPrompt(Context context, Bundle bundle,
ButtonInfo positiveButtonInfo, ButtonInfo negativeButtonInfo) {
+ mContext = context;
mBundle = bundle;
mPositiveButtonInfo = positiveButtonInfo;
mNegativeButtonInfo = negativeButtonInfo;
- mFingerprintManager = context.getSystemService(FingerprintManager.class);
- mPackageManager = context.getPackageManager();
+ mService = IBiometricPromptService.Stub.asInterface(
+ ServiceManager.getService(Context.BIOMETRIC_PROMPT_SERVICE));
}
/**
@@ -290,13 +345,12 @@
/**
* Authentication result
* @param crypto
- * @param identifier
- * @param userId
* @hide
*/
- public AuthenticationResult(CryptoObject crypto, Identifier identifier,
- int userId) {
- super(crypto, identifier, userId);
+ public AuthenticationResult(CryptoObject crypto) {
+ // For compatibility, this extends from common base class as FingerprintManager does.
+ // Identifier and userId is not used for BiometricPrompt.
+ super(crypto, null /* identifier */, 0 /* userId */);
}
/**
* Obtain the crypto object associated with this transaction
@@ -353,53 +407,6 @@
*/
@Override
public void onAuthenticationAcquired(int acquireInfo) {}
-
- /**
- * @param result An object containing authentication-related data
- * @hide
- */
- @Override
- public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
- onAuthenticationSucceeded(new AuthenticationResult(
- (CryptoObject) result.getCryptoObject(),
- result.getId(),
- result.getUserId()));
- }
- }
-
- /**
- * @param crypto Object associated with the call
- * @param cancel An object that can be used to cancel authentication
- * @param executor An executor to handle callback events
- * @param callback An object to receive authentication events
- * @hide
- */
- @Override
- public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
- @NonNull CancellationSignal cancel,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- if (!(callback instanceof BiometricPrompt.AuthenticationCallback)) {
- throw new IllegalArgumentException("Callback cannot be casted");
- }
- authenticate(crypto, cancel, executor, (AuthenticationCallback) callback);
- }
-
- /**
- *
- * @param cancel An object that can be used to cancel authentication
- * @param executor An executor to handle callback events
- * @param callback An object to receive authentication events
- * @hide
- */
- @Override
- public void authenticate(@NonNull CancellationSignal cancel,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- if (!(callback instanceof BiometricPrompt.AuthenticationCallback)) {
- throw new IllegalArgumentException("Callback cannot be casted");
- }
- authenticate(cancel, executor, (AuthenticationCallback) callback);
}
/**
@@ -430,11 +437,19 @@
@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback) {
- if (handlePreAuthenticationErrors(callback, executor)) {
- return;
+ if (crypto == null) {
+ throw new IllegalArgumentException("Must supply a crypto object");
}
- mFingerprintManager.authenticate(crypto, cancel, mBundle, executor, mDialogReceiver,
- callback);
+ if (cancel == null) {
+ throw new IllegalArgumentException("Must supply a cancellation signal");
+ }
+ if (executor == null) {
+ throw new IllegalArgumentException("Must supply an executor");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply a callback");
+ }
+ authenticateInternal(crypto, cancel, executor, callback);
}
/**
@@ -462,34 +477,53 @@
public void authenticate(@NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback) {
- if (handlePreAuthenticationErrors(callback, executor)) {
- return;
+ if (cancel == null) {
+ throw new IllegalArgumentException("Must supply a cancellation signal");
}
- mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback);
+ if (executor == null) {
+ throw new IllegalArgumentException("Must supply an executor");
+ }
+ if (callback == null) {
+ throw new IllegalArgumentException("Must supply a callback");
+ }
+ authenticateInternal(null /* crypto */, cancel, executor, callback);
}
- private boolean handlePreAuthenticationErrors(AuthenticationCallback callback,
- Executor executor) {
- if (!mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
- sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_NOT_PRESENT, callback,
- executor);
- return true;
- } else if (!mFingerprintManager.isHardwareDetected()) {
- sendError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE, callback,
- executor);
- return true;
- } else if (!mFingerprintManager.hasEnrolledFingerprints()) {
- sendError(BiometricPrompt.BIOMETRIC_ERROR_NO_BIOMETRICS, callback,
- executor);
- return true;
+ private void cancelAuthentication() {
+ if (mService != null) {
+ try {
+ mService.cancelAuthentication(mToken, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to cancel authentication", e);
+ }
}
- return false;
}
- private void sendError(int error, AuthenticationCallback callback, Executor executor) {
- executor.execute(() -> {
- callback.onAuthenticationError(error, mFingerprintManager.getErrorString(
- error, 0 /* vendorCode */));
- });
+ private void authenticateInternal(@Nullable CryptoObject crypto,
+ @NonNull CancellationSignal cancel,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull AuthenticationCallback callback) {
+ try {
+ if (cancel.isCanceled()) {
+ Log.w(TAG, "Authentication already canceled");
+ return;
+ } else {
+ cancel.setOnCancelListener(new OnAuthenticationCancelListener());
+ }
+
+ mCryptoObject = crypto;
+ mExecutor = executor;
+ mAuthenticationCallback = callback;
+ final long sessionId = crypto != null ? crypto.getOpId() : 0;
+ mService.authenticate(mToken, sessionId, mContext.getUserId(),
+ mBiometricPromptServiceReceiver, 0 /* flags */, mContext.getOpPackageName(),
+ mBundle, mDialogReceiver);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Remote exception while authenticating", e);
+ mExecutor.execute(() -> {
+ callback.onAuthenticationError(BiometricPrompt.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ mContext.getString(R.string.biometric_error_hw_unavailable));
+ });
+ }
}
}
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptService.aidl b/core/java/android/hardware/biometrics/IBiometricPromptService.aidl
new file mode 100644
index 0000000..2c93579
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricPromptService.aidl
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2018 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.os.Bundle;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricPromptServiceReceiver;
+
+/**
+ * Communication channel from BiometricPrompt to BiometricPromptService. The interface does not
+ * expose specific biometric modalities. The system will use the default biometric for apps. On
+ * devices with more than one, the choice is dictated by user preference in Settings.
+ * @hide
+ */
+interface IBiometricPromptService {
+ // Requests authentication. The service choose the appropriate biometric to use, and show
+ // the corresponding BiometricDialog.
+ void authenticate(IBinder token, long sessionId, int userId,
+ IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+
+ // Cancel authentication for the given sessionId
+ void cancelAuthentication(IBinder token, String opPackageName);
+}
\ No newline at end of file
diff --git a/core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl b/core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl
new file mode 100644
index 0000000..1ef6c52
--- /dev/null
+++ b/core/java/android/hardware/biometrics/IBiometricPromptServiceReceiver.aidl
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2018 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.BiometricSourceType;
+import android.os.Bundle;
+import android.os.UserHandle;
+
+/**
+ * Communication channel from the BiometricPromptService back to BiometricPrompt.
+ * @hide
+ */
+oneway interface IBiometricPromptServiceReceiver {
+ void onAuthenticationSucceeded(long deviceId);
+ void onAuthenticationFailed(long deviceId);
+ void onError(long deviceId, int error, String message);
+ void onAcquired(long deviceId, int acquiredInfo, String message);
+}
diff --git a/core/java/android/hardware/face/FaceManager.java b/core/java/android/hardware/face/FaceManager.java
index 6a3dd7d..b3b962f 100644
--- a/core/java/android/hardware/face/FaceManager.java
+++ b/core/java/android/hardware/face/FaceManager.java
@@ -17,10 +17,9 @@
package android.hardware.face;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_FACE;
-import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
-import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
@@ -28,13 +27,11 @@
import android.app.ActivityManager;
import android.content.Context;
import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricFaceConstants;
-import android.hardware.biometrics.BiometricPrompt;
import android.hardware.biometrics.CryptoObject;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
@@ -50,15 +47,13 @@
import com.android.internal.R;
import java.util.List;
-import java.util.concurrent.Executor;
/**
* A class that coordinates access to the face authentication hardware.
* @hide
*/
@SystemService(Context.FACE_SERVICE)
-public class FaceManager implements BiometricFaceConstants {
-
+public class FaceManager implements BiometricAuthenticator, BiometricFaceConstants {
private static final String TAG = "FaceManager";
private static final boolean DEBUG = true;
@@ -72,13 +67,12 @@
private IFaceService mService;
private final Context mContext;
private IBinder mToken = new Binder();
- private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
+ private AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private CryptoObject mCryptoObject;
private Face mRemovalFace;
private Handler mHandler;
- private Executor mExecutor;
private IFaceServiceReceiver mServiceReceiver = new IFaceServiceReceiver.Stub() {
@@ -147,7 +141,7 @@
* @throws IllegalStateException if the crypto primitive is not initialized.
* @hide
*/
- @RequiresPermission(USE_BIOMETRIC)
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel,
int flags, @NonNull AuthenticationCallback callback, @Nullable Handler handler) {
if (callback == null) {
@@ -170,7 +164,7 @@
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, mServiceReceiver, flags,
- mContext.getOpPackageName(), null /* bundle */, null /* receiver */);
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Log.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
@@ -195,108 +189,6 @@
}
/**
- * This method invokes the BiometricPrompt.
- */
- private void authenticateWithPrompt(@Nullable android.hardware.biometrics.CryptoObject crypto,
- @NonNull CancellationSignal cancel,
- @NonNull Bundle bundle,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull IBiometricPromptReceiver receiver,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- mCryptoObject = crypto;
- if (cancel.isCanceled()) {
- Slog.w(TAG, "authentication already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
- }
-
- if (mService != null) {
- try {
- mExecutor = executor;
- mAuthenticationCallback = callback;
- final long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, mServiceReceiver,
- 0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
- } catch (RemoteException e) {
- Slog.w(TAG, "Remote exception while authenticating", e);
- mExecutor.execute(() -> {
- callback.onAuthenticationError(FACE_ERROR_HW_UNAVAILABLE,
- getErrorString(FACE_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
- });
- }
- }
- }
-
- /**
- * Private method, see {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
- * BiometricPrompt.AuthenticationCallback)}
- * @param cancel
- * @param executor
- * @param callback
- * @hide
- */
- public void authenticate(
- @NonNull CancellationSignal cancel,
- @NonNull Bundle bundle,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull IBiometricPromptReceiver receiver,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- if (cancel == null) {
- throw new IllegalArgumentException("Must supply a cancellation signal");
- }
- if (bundle == null) {
- throw new IllegalArgumentException("Must supply a bundle");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Must supply an executor");
- }
- if (receiver == null) {
- throw new IllegalArgumentException("Must supply a receiver");
- }
- if (callback == null) {
- throw new IllegalArgumentException("Must supply a calback");
- }
- authenticateWithPrompt(null, cancel, bundle, executor, receiver, callback);
- }
-
- /**
- * Private method, see {@link BiometricPrompt#authenticate(BiometricPrompt.CryptoObject,
- * CancellationSignal, Executor, BiometricPrompt.AuthenticationCallback)}
- * @param crypto
- * @param cancel
- * @param executor
- * @param callback
- * @hide
- */
- public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
- @NonNull CancellationSignal cancel,
- @NonNull Bundle bundle,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull IBiometricPromptReceiver receiver,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- if (crypto == null) {
- throw new IllegalArgumentException("Must supply a crypto object");
- }
- if (cancel == null) {
- throw new IllegalArgumentException("Must supply a cancellation signal");
- }
- if (bundle == null) {
- throw new IllegalArgumentException("Must supply a bundle");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Must supply an executor");
- }
- if (receiver == null) {
- throw new IllegalArgumentException("Must supply a receiver");
- }
- if (callback == null) {
- throw new IllegalArgumentException("Must supply a callback");
- }
- authenticateWithPrompt(crypto, cancel, bundle, executor, receiver, callback);
- }
-
- /**
* Request face authentication enrollment. This call operates the face authentication hardware
* and starts capturing images. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
@@ -313,7 +205,7 @@
* @param callback an object to receive enrollment events
* @hide
*/
- @RequiresPermission(MANAGE_FACE)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public void enroll(byte[] token, CancellationSignal cancel, int flags,
int userId, EnrollmentCallback callback) {
if (userId == UserHandle.USER_CURRENT) {
@@ -355,7 +247,7 @@
*
* @hide
*/
- @RequiresPermission(MANAGE_FACE)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public long preEnroll() {
long result = 0;
if (mService != null) {
@@ -373,7 +265,7 @@
*
* @hide
*/
- @RequiresPermission(MANAGE_FACE)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public int postEnroll() {
int result = 0;
if (mService != null) {
@@ -392,7 +284,7 @@
*
* @hide
*/
- @RequiresPermission(MANAGE_FACE)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public void setActiveUser(int userId) {
if (mService != null) {
try {
@@ -412,7 +304,7 @@
* successfully removed. May be null if no callback is required.
* @hide
*/
- @RequiresPermission(MANAGE_FACE)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public void remove(Face face, int userId, RemovalCallback callback) {
if (mService != null) {
try {
@@ -435,7 +327,7 @@
* @return the current face item
* @hide
*/
- @RequiresPermission(USE_BIOMETRIC)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public List<Face> getEnrolledFaces(int userId) {
if (mService != null) {
try {
@@ -453,7 +345,7 @@
* @return the current face item
* @hide
*/
- @RequiresPermission(USE_BIOMETRIC)
+ @RequiresPermission(MANAGE_BIOMETRIC)
public List<Face> getEnrolledFaces() {
return getEnrolledFaces(UserHandle.myUserId());
}
@@ -463,8 +355,9 @@
*
* @return true if a face is enrolled, false otherwise
*/
- @RequiresPermission(USE_BIOMETRIC)
- public boolean hasEnrolledFaces() {
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @Override
+ public boolean hasEnrolledTemplates() {
if (mService != null) {
try {
return mService.hasEnrolledFaces(
@@ -480,9 +373,9 @@
* @hide
*/
@RequiresPermission(allOf = {
- USE_BIOMETRIC,
+ USE_BIOMETRIC_INTERNAL,
INTERACT_ACROSS_USERS})
- public boolean hasEnrolledFaces(int userId) {
+ public boolean hasEnrolledTemplates(int userId) {
if (mService != null) {
try {
return mService.hasEnrolledFaces(userId, mContext.getOpPackageName());
@@ -498,7 +391,8 @@
*
* @return true if hardware is present and functional, false otherwise.
*/
- @RequiresPermission(USE_BIOMETRIC)
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
+ @Override
public boolean isHardwareDetected() {
if (mService != null) {
try {
@@ -538,6 +432,7 @@
* @param token an opaque token returned by password confirmation.
* @hide
*/
+ @RequiresPermission(MANAGE_BIOMETRIC)
public void resetTimeout(byte[] token) {
if (mService != null) {
try {
@@ -553,6 +448,7 @@
/**
* @hide
*/
+ @RequiresPermission(USE_BIOMETRIC_INTERNAL)
public void addLockoutResetCallback(final LockoutResetCallback callback) {
if (mService != null) {
try {
@@ -617,7 +513,8 @@
}
}
- private String getErrorString(int errMsg, int vendorCode) {
+ @Override
+ public String getErrorString(int errMsg, int vendorCode) {
switch (errMsg) {
case FACE_ERROR_HW_UNAVAILABLE:
return mContext.getString(
@@ -652,7 +549,10 @@
return null;
}
- private String getAcquiredString(int acquireInfo, int vendorCode) {
+ /**
+ * @hide
+ */
+ public String getAcquiredString(int acquireInfo, int vendorCode) {
switch (acquireInfo) {
case FACE_ACQUIRED_GOOD:
return null;
@@ -691,6 +591,37 @@
}
/**
+ * Used so BiometricPrompt can map the face ones onto existing public constants.
+ * @hide
+ */
+ public int getMappedAcquiredInfo(int acquireInfo, int vendorCode) {
+ switch (acquireInfo) {
+ case FACE_ACQUIRED_GOOD:
+ return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
+ case FACE_ACQUIRED_INSUFFICIENT:
+ case FACE_ACQUIRED_TOO_BRIGHT:
+ case FACE_ACQUIRED_TOO_DARK:
+ return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
+ case FACE_ACQUIRED_TOO_CLOSE:
+ case FACE_ACQUIRED_TOO_FAR:
+ case FACE_ACQUIRED_TOO_HIGH:
+ case FACE_ACQUIRED_TOO_LOW:
+ case FACE_ACQUIRED_TOO_RIGHT:
+ case FACE_ACQUIRED_TOO_LEFT:
+ return BiometricConstants.BIOMETRIC_ACQUIRED_PARTIAL;
+ case FACE_ACQUIRED_POOR_GAZE:
+ case FACE_ACQUIRED_NOT_DETECTED:
+ case FACE_ACQUIRED_TOO_MUCH_MOTION:
+ case FACE_ACQUIRED_RECALIBRATE:
+ return BiometricConstants.BIOMETRIC_ACQUIRED_INSUFFICIENT;
+ case FACE_ACQUIRED_VENDOR:
+ return BiometricConstants.BIOMETRIC_ACQUIRED_VENDOR_BASE + vendorCode;
+ default:
+ return BiometricConstants.BIOMETRIC_ACQUIRED_GOOD;
+ }
+ }
+
+ /**
* Container for callback data from {@link FaceManager#authenticate(CryptoObject,
* CancellationSignal, int, AuthenticationCallback, Handler)}.
*/
@@ -796,18 +727,6 @@
*/
public void onAuthenticationAcquired(int acquireInfo) {
}
-
- /**
- * @hide
- * @param result
- */
- @Override
- public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
- onAuthenticationSucceeded(new AuthenticationResult(
- result.getCryptoObject(),
- (Face) result.getId(), result.getUserId()));
- }
-
}
/**
@@ -988,8 +907,8 @@
private void sendAuthenticatedSucceeded(Face face, int userId) {
if (mAuthenticationCallback != null) {
- final BiometricAuthenticator.AuthenticationResult result =
- new BiometricAuthenticator.AuthenticationResult(mCryptoObject, face, userId);
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, face, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
diff --git a/core/java/android/hardware/face/IFaceService.aidl b/core/java/android/hardware/face/IFaceService.aidl
index 03bb7ae..dd995c9 100644
--- a/core/java/android/hardware/face/IFaceService.aidl
+++ b/core/java/android/hardware/face/IFaceService.aidl
@@ -17,23 +17,35 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricPromptServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.face.IFaceServiceReceiver;
import android.hardware.face.Face;
/**
- * Communication channel from client to the face service.
+ * Communication channel from client to the face service. These methods are all require the
+ * MANAGE_BIOMETRIC signature permission.
* @hide
*/
interface IFaceService {
// Authenticate the given sessionId with a face
void authenticate(IBinder token, long sessionId,
- IFaceServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+ IFaceServiceReceiver receiver, int flags, String opPackageName);
+
+ // This method invokes the BiometricDialog. The arguments are almost the same as above,
+ // but should only be called from (BiometricPromptService).
+ void authenticateFromService(IBinder token, long sessionId, int userId,
+ IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ int callingUid, int callingPid, int callingUserId);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
+ // Same as above, with extra arguments.
+ void cancelAuthenticationFromService(IBinder token, String opPackageName,
+ int callingUid, int callingPid, int callingUserId);
+
// Start face enrollment
void enroll(IBinder token, in byte [] cryptoToken, int userId, IFaceServiceReceiver receiver,
int flags, String opPackageName);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 15868f1..44b8faf 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -18,10 +18,10 @@
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
+import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC;
import static android.Manifest.permission.USE_FINGERPRINT;
-import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
@@ -34,10 +34,8 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricFingerprintConstants;
import android.hardware.biometrics.BiometricPrompt;
-import android.hardware.biometrics.IBiometricPromptReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.os.Binder;
-import android.os.Bundle;
import android.os.CancellationSignal;
import android.os.CancellationSignal.OnCancelListener;
import android.os.Handler;
@@ -66,7 +64,8 @@
@Deprecated
@SystemService(Context.FINGERPRINT_SERVICE)
@RequiresFeature(PackageManager.FEATURE_FINGERPRINT)
-public class FingerprintManager implements BiometricFingerprintConstants {
+public class FingerprintManager implements BiometricAuthenticator, BiometricFingerprintConstants {
+
private static final String TAG = "FingerprintManager";
private static final boolean DEBUG = true;
private static final int MSG_ENROLL_RESULT = 100;
@@ -80,14 +79,13 @@
private IFingerprintService mService;
private Context mContext;
private IBinder mToken = new Binder();
- private BiometricAuthenticator.AuthenticationCallback mAuthenticationCallback;
+ private AuthenticationCallback mAuthenticationCallback;
private EnrollmentCallback mEnrollmentCallback;
private RemovalCallback mRemovalCallback;
private EnumerateCallback mEnumerateCallback;
- private android.hardware.biometrics.CryptoObject mCryptoObject;
+ private CryptoObject mCryptoObject;
private Fingerprint mRemovalFingerprint;
private Handler mHandler;
- private Executor mExecutor;
private class OnEnrollCancelListener implements OnCancelListener {
@Override
@@ -250,24 +248,12 @@
*/
@Override
public void onAuthenticationAcquired(int acquireInfo) {}
-
- /**
- * @hide
- * @param result
- */
- @Override
- public void onAuthenticationSucceeded(BiometricAuthenticator.AuthenticationResult result) {
- onAuthenticationSucceeded(new AuthenticationResult(
- (CryptoObject) result.getCryptoObject(),
- (Fingerprint) result.getId(), result.getUserId()));
- }
};
/**
- * Callback structure provided to {@link FingerprintManager#enroll(long, EnrollmentCallback,
- * CancellationSignal, int). Users of {@link #FingerprintManager()}
- * must provide an implementation of this to {@link FingerprintManager#enroll(long,
- * CancellationSignal, int, EnrollmentCallback) for listening to fingerprint events.
+ * Callback structure provided to {@link FingerprintManager#enroll(byte[], CancellationSignal,
+ * int, int, EnrollmentCallback)} must provide an implementation of this for listening to
+ * fingerprint events.
*
* @hide
*/
@@ -328,9 +314,9 @@
};
/**
- * Callback structure provided to {@link FingerprintManager#enumerate(int). Users of
- * {@link #FingerprintManager()} may optionally provide an implementation of this to
- * {@link FingerprintManager#enumerate(int, int, EnumerateCallback)} for listening to
+ * Callback structure provided to {@link FingerprintManager#enumerate(int, EnumerateCallback)}.
+ * Users of{@link #FingerprintManager} may optionally provide an implementation of this to
+ * {@link FingerprintManager#enumerate(int, EnumerateCallback)} for listening to
* fingerprint template removal events.
*
* @hide
@@ -433,7 +419,7 @@
mCryptoObject = crypto;
long sessionId = crypto != null ? crypto.getOpId() : 0;
mService.authenticate(mToken, sessionId, userId, mServiceReceiver, flags,
- mContext.getOpPackageName(), null /* bundle */, null /* receiver */);
+ mContext.getOpPackageName());
} catch (RemoteException e) {
Slog.w(TAG, "Remote exception while authenticating: ", e);
if (callback != null) {
@@ -446,110 +432,6 @@
}
/**
- * Per-user version. This method invokes the BiometricPrompt.
- */
- private void authenticate(int userId,
- @Nullable android.hardware.biometrics.CryptoObject crypto,
- @NonNull CancellationSignal cancel,
- @NonNull Bundle bundle,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull IBiometricPromptReceiver receiver,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- mCryptoObject = crypto;
- if (cancel.isCanceled()) {
- Slog.w(TAG, "authentication already canceled");
- return;
- } else {
- cancel.setOnCancelListener(new OnAuthenticationCancelListener(crypto));
- }
-
- if (mService != null) {
- try {
- mExecutor = executor;
- mAuthenticationCallback = callback;
- final long sessionId = crypto != null ? crypto.getOpId() : 0;
- mService.authenticate(mToken, sessionId, userId, mServiceReceiver,
- 0 /* flags */, mContext.getOpPackageName(), bundle, receiver);
- } catch (RemoteException e) {
- Slog.w(TAG, "Remote exception while authenticating", e);
- mExecutor.execute(() -> {
- callback.onAuthenticationError(FINGERPRINT_ERROR_HW_UNAVAILABLE,
- getErrorString(FINGERPRINT_ERROR_HW_UNAVAILABLE, 0 /* vendorCode */));
- });
- }
- }
- }
-
- /**
- * Private method, see {@link BiometricPrompt#authenticate(CancellationSignal, Executor,
- * BiometricPrompt.AuthenticationCallback)}
- * @param cancel
- * @param executor
- * @param callback
- * @hide
- */
- public void authenticate(
- @NonNull CancellationSignal cancel,
- @NonNull Bundle bundle,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull IBiometricPromptReceiver receiver,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- if (cancel == null) {
- throw new IllegalArgumentException("Must supply a cancellation signal");
- }
- if (bundle == null) {
- throw new IllegalArgumentException("Must supply a bundle");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Must supply an executor");
- }
- if (receiver == null) {
- throw new IllegalArgumentException("Must supply a receiver");
- }
- if (callback == null) {
- throw new IllegalArgumentException("Must supply a calback");
- }
- authenticate(mContext.getUserId(), null, cancel, bundle, executor, receiver, callback);
- }
-
- /**
- * Private method, see {@link BiometricPrompt#authenticate(BiometricPrompt.CryptoObject,
- * CancellationSignal, Executor, BiometricPrompt.AuthenticationCallback)}
- * @param crypto
- * @param cancel
- * @param executor
- * @param callback
- * @hide
- */
- public void authenticate(@NonNull android.hardware.biometrics.CryptoObject crypto,
- @NonNull CancellationSignal cancel,
- @NonNull Bundle bundle,
- @NonNull @CallbackExecutor Executor executor,
- @NonNull IBiometricPromptReceiver receiver,
- @NonNull BiometricAuthenticator.AuthenticationCallback callback) {
- if (crypto == null) {
- throw new IllegalArgumentException("Must supply a crypto object");
- }
- if (cancel == null) {
- throw new IllegalArgumentException("Must supply a cancellation signal");
- }
- if (bundle == null) {
- throw new IllegalArgumentException("Must supply a bundle");
- }
- if (executor == null) {
- throw new IllegalArgumentException("Must supply an executor");
- }
- if (receiver == null) {
- throw new IllegalArgumentException("Must supply a receiver");
- }
- if (callback == null) {
- throw new IllegalArgumentException("Must supply a callback");
- }
- authenticate(mContext.getUserId(), crypto, cancel,
- bundle, executor, receiver, callback);
- }
-
- /**
* Request fingerprint enrollment. This call warms up the fingerprint hardware
* and starts scanning for fingerprints. Progress will be indicated by callbacks to the
* {@link EnrollmentCallback} object. It terminates when
@@ -743,6 +625,14 @@
}
/**
+ * @hide
+ */
+ @Override
+ public boolean hasEnrolledTemplates() {
+ return hasEnrolledFingerprints();
+ }
+
+ /**
* Determine if there is at least one fingerprint enrolled.
*
* @return true if at least one fingerprint is enrolled, false otherwise
@@ -785,6 +675,7 @@
*/
@Deprecated
@RequiresPermission(USE_FINGERPRINT)
+ @Override
public boolean isHardwareDetected() {
if (mService != null) {
try {
@@ -826,6 +717,7 @@
*
* @hide
*/
+ @RequiresPermission(RESET_FINGERPRINT_LOCKOUT)
public void resetTimeout(byte[] token) {
if (mService != null) {
try {
@@ -954,8 +846,8 @@
private void sendAuthenticatedSucceeded(Fingerprint fp, int userId) {
if (mAuthenticationCallback != null) {
- final BiometricAuthenticator.AuthenticationResult result =
- new BiometricAuthenticator.AuthenticationResult(mCryptoObject, fp, userId);
+ final AuthenticationResult result =
+ new AuthenticationResult(mCryptoObject, fp, userId);
mAuthenticationCallback.onAuthenticationSucceeded(result);
}
}
@@ -1042,6 +934,7 @@
/**
* @hide
*/
+ @Override
public String getErrorString(int errMsg, int vendorCode) {
switch (errMsg) {
case FINGERPRINT_ERROR_UNABLE_TO_PROCESS:
@@ -1127,47 +1020,23 @@
@Override // binder call
public void onAcquired(long deviceId, int acquireInfo, int vendorCode) {
- if (mExecutor != null) {
- mExecutor.execute(() -> {
- sendAcquiredResult(deviceId, acquireInfo, vendorCode);
- });
- } else {
- mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode,
- deviceId).sendToTarget();
- }
+ mHandler.obtainMessage(MSG_ACQUIRED, acquireInfo, vendorCode,
+ deviceId).sendToTarget();
}
@Override // binder call
public void onAuthenticationSucceeded(long deviceId, Fingerprint fp, int userId) {
- if (mExecutor != null) {
- mExecutor.execute(() -> {
- sendAuthenticatedSucceeded(fp, userId);
- });
- } else {
- mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
- }
+ mHandler.obtainMessage(MSG_AUTHENTICATION_SUCCEEDED, userId, 0, fp).sendToTarget();
}
@Override // binder call
public void onAuthenticationFailed(long deviceId) {
- if (mExecutor != null) {
- mExecutor.execute(() -> {
- sendAuthenticatedFailed();
- });
- } else {
- mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
- }
+ mHandler.obtainMessage(MSG_AUTHENTICATION_FAILED).sendToTarget();
}
@Override // binder call
public void onError(long deviceId, int error, int vendorCode) {
- if (mExecutor != null) {
- mExecutor.execute(() -> {
- sendErrorResult(deviceId, error, vendorCode);
- });
- } else {
- mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
- }
+ mHandler.obtainMessage(MSG_ERROR, error, vendorCode, deviceId).sendToTarget();
}
@Override // binder call
diff --git a/core/java/android/hardware/fingerprint/IFingerprintService.aidl b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
index 71a6420..2b2c0b7 100644
--- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl
+++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl
@@ -17,6 +17,7 @@
import android.os.Bundle;
import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricPromptServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -28,14 +29,29 @@
* @hide
*/
interface IFingerprintService {
- // Authenticate the given sessionId with a fingerprint
+ // Authenticate the given sessionId with a fingerprint. This is protected by
+ // USE_FINGERPRINT/USE_BIOMETRIC permission. This is effectively deprecated, since it only comes
+ // through FingerprintManager now.
void authenticate(IBinder token, long sessionId, int userId,
- IFingerprintServiceReceiver receiver, int flags, String opPackageName,
- in Bundle bundle, IBiometricPromptReceiver dialogReceiver);
+ IFingerprintServiceReceiver receiver, int flags, String opPackageName);
+
+ // This method invokes the BiometricDialog. The arguments are almost the same as above, except
+ // this is protected by the MANAGE_BIOMETRIC signature permission. This method should only be
+ // called from BiometricPromptService. The additional uid, pid, userId arguments should be
+ // determined by BiometricPromptService.
+ void authenticateFromService(IBinder token, long sessionId, int userId,
+ IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ in Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ int callingUid, int callingPid, int callingUserId);
// Cancel authentication for the given sessionId
void cancelAuthentication(IBinder token, String opPackageName);
+ // Same as above, except this is protected by the MANAGE_BIOMETRIC signature permission. Takes
+ // an additional uid, pid, userid.
+ void cancelAuthenticationFromService(IBinder token, String opPackageName,
+ int callingUid, int callingPid, int callingUserId);
+
// Start fingerprint enrollment
void enroll(IBinder token, in byte [] cryptoToken, int groupId, IFingerprintServiceReceiver receiver,
int flags, String opPackageName);
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index 67bdbf6..5258518 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -3748,9 +3748,13 @@
<permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT"
android:protectionLevel="signature" />
- <!-- Allows managing (adding, removing) facial templates. Reserved for the system. @hide -->
- <permission android:name="android.permission.MANAGE_FACE"
- android:protectionLevel="signature|privileged" />
+ <!-- Allows direct access to the <Biometric>Service interfaces. Reserved for the system. @hide -->
+ <permission android:name="android.permission.MANAGE_BIOMETRIC"
+ android:protectionLevel="signature" />
+
+ <!-- Allows direct access to the <Biometric>Service authentication methods. Reserved for the system. @hide -->
+ <permission android:name="android.permission.USE_BIOMETRIC_INTERNAL"
+ android:protectionLevel="signature" />
<!-- Allows an app to reset face authentication attempt counter. Reserved for the system. @hide -->
<permission android:name="android.permission.RESET_FACE_LOCKOUT"
diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml
index dfd5e81..e3b90526 100644
--- a/core/res/res/values/strings.xml
+++ b/core/res/res/values/strings.xml
@@ -1397,6 +1397,9 @@
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_mediaLocation">Allows the app to read locations from your media collection.</string>
+ <!-- Message shown when biometric hardware is not available [CHAR LIMIT=50] -->
+ <string name="biometric_error_hw_unavailable">Biometric hardware unavailable</string>
+
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
<string name="fingerprint_acquired_partial">Partial fingerprint detected. Please try again.</string>
<!-- Message shown during fingerprint acquisision when the fingerprint cannot be recognized -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 8ac2474..c6387f0 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2392,6 +2392,9 @@
<!-- From KeyguardServiceDelegate -->
<java-symbol type="string" name="config_keyguardComponent" />
+ <!-- Biometric messages -->
+ <java-symbol type="string" name="biometric_error_hw_unavailable" />
+
<!-- Fingerprint messages -->
<java-symbol type="string" name="fingerprint_error_unable_to_process" />
<java-symbol type="string" name="fingerprint_error_hw_not_available" />
diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml
index a5616d5..e31dd1e 100644
--- a/packages/SystemUI/AndroidManifest.xml
+++ b/packages/SystemUI/AndroidManifest.xml
@@ -120,7 +120,7 @@
<uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
<uses-permission android:name="android.permission.ACCESS_KEYGUARD_SECURE_STORAGE" />
<uses-permission android:name="android.permission.TRUST_LISTENER" />
- <uses-permission android:name="android.permission.USE_BIOMETRIC" />
+ <uses-permission android:name="android.permission.USE_BIOMETRIC_INTERNAL" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.RESET_FINGERPRINT_LOCKOUT" />
<uses-permission android:name="android.permission.MANAGE_SLICE_PERMISSIONS" />
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index 3e534d1..ff6a1c9 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -1597,7 +1597,7 @@
public boolean isUnlockWithFacePossible(int userId) {
return mFaceAuthenticationManager != null && mFaceAuthenticationManager.isHardwareDetected()
&& !isFaceDisabled(userId)
- && mFaceAuthenticationManager.hasEnrolledFaces(userId);
+ && mFaceAuthenticationManager.hasEnrolledTemplates(userId);
}
private void stopListeningForFingerprint() {
diff --git a/services/core/java/com/android/server/biometrics/BiometricPromptService.java b/services/core/java/com/android/server/biometrics/BiometricPromptService.java
new file mode 100644
index 0000000..29eda8b
--- /dev/null
+++ b/services/core/java/com/android/server/biometrics/BiometricPromptService.java
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.biometrics;
+
+import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_FINGERPRINT;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.hardware.biometrics.BiometricAuthenticator;
+import android.hardware.biometrics.BiometricConstants;
+import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricPromptService;
+import android.hardware.biometrics.IBiometricPromptServiceReceiver;
+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.Looper;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import com.android.internal.R;
+import com.android.internal.os.SomeArgs;
+import com.android.server.SystemService;
+
+import java.util.ArrayList;
+
+/**
+ * System service that arbitrates the modality for BiometricPrompt to use.
+ */
+public class BiometricPromptService extends SystemService {
+
+ private static final String TAG = "BiometricPromptService";
+
+ /**
+ * No biometric methods or nothing has been enrolled.
+ * Move/expose these in BiometricPrompt if we ever want to allow applications to "blacklist"
+ * modalities when calling authenticate().
+ */
+ private static final int BIOMETRIC_NONE = 0;
+
+ /**
+ * Constant representing fingerprint.
+ */
+ private static final int BIOMETRIC_FINGERPRINT = 1 << 0;
+
+ /**
+ * Constant representing iris.
+ */
+ private static final int BIOMETRIC_IRIS = 1 << 1;
+
+ /**
+ * Constant representing face.
+ */
+ private static final int BIOMETRIC_FACE = 1 << 2;
+
+ private static final int[] FEATURE_ID = {
+ BIOMETRIC_FINGERPRINT,
+ BIOMETRIC_IRIS,
+ BIOMETRIC_FACE
+ };
+
+ private final Handler mHandler;
+ private final boolean mHasFeatureFingerprint;
+ private final boolean mHasFeatureIris;
+ private final boolean mHasFeatureFace;
+
+ private IFingerprintService mFingerprintService;
+ private IFaceService mFaceService;
+
+ // Get and cache the available authenticator (manager) classes. Used since aidl doesn't support
+ // polymorphism :/
+ final ArrayList<Authenticator> mAuthenticators = new ArrayList<>();
+
+ // Cache the current service that's being used. This is the service which
+ // cancelAuthentication() must be forwarded to. This is just a cache, and the actual
+ // check (is caller the current client) is done in the <Biometric>Service.
+ // Since Settings/System (not application) is responsible for changing preference, this
+ // should be safe.
+ private int mCurrentModality;
+
+ private final class Authenticator {
+ int mType;
+ BiometricAuthenticator mAuthenticator;
+
+ Authenticator(int type, BiometricAuthenticator authenticator) {
+ mType = type;
+ mAuthenticator = authenticator;
+ }
+
+ int getType() {
+ return mType;
+ }
+
+ BiometricAuthenticator getAuthenticator() {
+ return mAuthenticator;
+ }
+ }
+
+ /**
+ * This is just a pass-through service that wraps Fingerprint, Iris, Face services. This service
+ * should not carry any state. The reality is we need to keep a tiny amount of state so that
+ * cancelAuthentication() can go to the right place.
+ */
+ private final class BiometricPromptServiceWrapper extends IBiometricPromptService.Stub {
+
+ @Override // Binder call
+ public void authenticate(IBinder token, long sessionId, int userId,
+ IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ Bundle bundle, IBiometricPromptReceiver dialogReceiver) throws RemoteException {
+ // Check the USE_BIOMETRIC permission here. In the BiometricService, check do the
+ // AppOps and foreground check.
+ checkPermission();
+
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ mHandler.post(() -> {
+ mCurrentModality = checkAndGetBiometricModality(receiver);
+
+ try {
+ // No polymorphism :(
+ if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
+ mFingerprintService.authenticateFromService(token, sessionId, userId,
+ receiver, flags, opPackageName, bundle, dialogReceiver,
+ callingUid, callingPid, callingUserId);
+ } else if (mCurrentModality == BIOMETRIC_IRIS) {
+ Slog.w(TAG, "Unsupported modality");
+ } else if (mCurrentModality == BIOMETRIC_FACE) {
+ mFaceService.authenticateFromService(token, sessionId, userId,
+ receiver, flags, opPackageName, bundle, dialogReceiver,
+ callingUid, callingPid, callingUserId);
+ } else {
+ Slog.w(TAG, "Unsupported modality");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to start authentication", e);
+ }
+ });
+ }
+
+ @Override // Binder call
+ public void cancelAuthentication(IBinder token, String opPackageName)
+ throws RemoteException {
+ checkPermission();
+
+ final int callingUid = Binder.getCallingUid();
+ final int callingPid = Binder.getCallingPid();
+ final int callingUserId = UserHandle.getCallingUserId();
+
+ mHandler.post(() -> {
+ try {
+ if (mCurrentModality == BIOMETRIC_FINGERPRINT) {
+ mFingerprintService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId);
+ } else if (mCurrentModality == BIOMETRIC_IRIS) {
+ Slog.w(TAG, "Unsupported modality");
+ } else if (mCurrentModality == BIOMETRIC_FACE) {
+ mFaceService.cancelAuthenticationFromService(token, opPackageName,
+ callingUid, callingPid, callingUserId);
+ } else {
+ Slog.w(TAG, "Unsupported modality");
+ }
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to cancel authentication");
+ }
+ });
+ }
+ }
+
+ private void checkPermission() {
+ if (getContext().checkCallingPermission(USE_FINGERPRINT)
+ != PackageManager.PERMISSION_GRANTED) {
+ getContext().enforceCallingPermission(USE_BIOMETRIC,
+ "Must have USE_BIOMETRIC permission");
+ }
+ }
+
+ /**
+ * Initializes the system service.
+ * <p>
+ * Subclasses must define a single argument constructor that accepts the context
+ * and passes it to super.
+ * </p>
+ *
+ * @param context The system server context.
+ */
+ public BiometricPromptService(Context context) {
+ super(context);
+
+ mHandler = new Handler(Looper.getMainLooper());
+
+ final PackageManager pm = context.getPackageManager();
+ mHasFeatureFingerprint = pm.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+ mHasFeatureIris = pm.hasSystemFeature(PackageManager.FEATURE_IRIS);
+ mHasFeatureFace = pm.hasSystemFeature(PackageManager.FEATURE_FACE);
+ }
+
+ @Override
+ public void onStart() {
+ // TODO: maybe get these on-demand
+ if (mHasFeatureFingerprint) {
+ mFingerprintService = IFingerprintService.Stub.asInterface(
+ ServiceManager.getService(Context.FINGERPRINT_SERVICE));
+ }
+ if (mHasFeatureFace) {
+ mFaceService = IFaceService.Stub.asInterface(
+ ServiceManager.getService(Context.FACE_SERVICE));
+ }
+
+ // Cache the authenticators
+ for (int i = 0; i < FEATURE_ID.length; i++) {
+ if (hasFeature(FEATURE_ID[i])) {
+ Authenticator authenticator =
+ new Authenticator(FEATURE_ID[i], getAuthenticator(FEATURE_ID[i]));
+ mAuthenticators.add(authenticator);
+ }
+ }
+
+ publishBinderService(Context.BIOMETRIC_PROMPT_SERVICE, new BiometricPromptServiceWrapper());
+ }
+
+ /**
+ * Checks if there are any available biometrics, and returns the modality. This method also
+ * returns errors through the callback (no biometric feature, hardware not detected, no
+ * templates enrolled, etc). This service must not start authentication if errors are sent.
+ */
+ private int checkAndGetBiometricModality(IBiometricPromptServiceReceiver receiver) {
+ int modality = BIOMETRIC_NONE;
+ final String hardwareUnavailable =
+ getContext().getString(R.string.biometric_error_hw_unavailable);
+
+ // No biometric features, send error
+ if (mAuthenticators.isEmpty()) {
+ try {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_HW_NOT_PRESENT,
+ hardwareUnavailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ return BIOMETRIC_NONE;
+ }
+
+ // Find first authenticator that's both detected and enrolled
+ boolean isHardwareDetected = false;
+ boolean hasTemplatesEnrolled = false;
+ for (int i = 0; i < mAuthenticators.size(); i++) {
+ int featureId = mAuthenticators.get(i).getType();
+ BiometricAuthenticator authenticator = mAuthenticators.get(i).getAuthenticator();
+ if (authenticator.isHardwareDetected()) {
+ isHardwareDetected = true;
+ if (authenticator.hasEnrolledTemplates()) {
+ hasTemplatesEnrolled = true;
+ modality = featureId;
+ break;
+ }
+ }
+ }
+
+ // Check error conditions
+ if (!isHardwareDetected) {
+ try {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_HW_UNAVAILABLE,
+ hardwareUnavailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ return BIOMETRIC_NONE;
+ }
+ if (!hasTemplatesEnrolled) {
+ try {
+ receiver.onError(0 /* deviceId */,
+ BiometricConstants.BIOMETRIC_ERROR_NO_BIOMETRICS,
+ hardwareUnavailable);
+ } catch (RemoteException e) {
+ Slog.e(TAG, "Unable to send error", e);
+ }
+ return BIOMETRIC_NONE;
+ }
+
+ return modality;
+ }
+
+ private BiometricAuthenticator getAuthenticator(int type) {
+ switch (type) {
+ case BIOMETRIC_FINGERPRINT:
+ return (FingerprintManager)
+ getContext().getSystemService(Context.FINGERPRINT_SERVICE);
+ case BIOMETRIC_IRIS:
+ return null;
+ case BIOMETRIC_FACE:
+ return (FaceManager)
+ getContext().getSystemService(Context.FACE_SERVICE);
+ default:
+ return null;
+ }
+ }
+
+ private boolean hasFeature(int type) {
+ switch (type) {
+ case BIOMETRIC_FINGERPRINT:
+ return mHasFeatureFingerprint;
+ case BIOMETRIC_IRIS:
+ return mHasFeatureIris;
+ case BIOMETRIC_FACE:
+ return mHasFeatureFace;
+ default:
+ return false;
+ }
+ }
+}
diff --git a/services/core/java/com/android/server/biometrics/BiometricService.java b/services/core/java/com/android/server/biometrics/BiometricService.java
index 9b22444..cc2e81f 100644
--- a/services/core/java/com/android/server/biometrics/BiometricService.java
+++ b/services/core/java/com/android/server/biometrics/BiometricService.java
@@ -333,8 +333,8 @@
* Wraps the callback interface from Service -> Manager
*/
protected interface ServiceListener {
- void onEnrollResult(BiometricAuthenticator.Identifier identifier,
- int remaining) throws RemoteException;
+ default void onEnrollResult(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {};
void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
throws RemoteException;
@@ -349,11 +349,11 @@
void onError(long deviceId, int error, int vendorCode)
throws RemoteException;
- void onRemoved(BiometricAuthenticator.Identifier identifier,
- int remaining) throws RemoteException;
+ default void onRemoved(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {};
- void onEnumerated(BiometricAuthenticator.Identifier identifier,
- int remaining) throws RemoteException;
+ default void onEnumerated(BiometricAuthenticator.Identifier identifier,
+ int remaining) throws RemoteException {};
}
/**
@@ -688,7 +688,11 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
+ authenticateInternal(client, opId, opPackageName, callingUid, callingPid, callingUserId);
+ }
+ protected void authenticateInternal(AuthenticationClientImpl client, long opId,
+ String opPackageName, int callingUid, int callingPid, int callingUserId) {
if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
callingUserId)) {
if (DEBUG) Slog.v(getTag(), "authenticate(): reject " + opPackageName);
@@ -716,7 +720,11 @@
final int callingUid = Binder.getCallingUid();
final int callingPid = Binder.getCallingPid();
final int callingUserId = UserHandle.getCallingUserId();
+ cancelAuthenticationInternal(token, opPackageName, callingUid, callingPid, callingUserId);
+ }
+ protected void cancelAuthenticationInternal(final IBinder token, final String opPackageName,
+ int callingUid, int callingPid, int callingUserId) {
if (!canUseBiometric(opPackageName, true /* foregroundOnly */, callingUid, callingPid,
callingUserId)) {
if (DEBUG) Slog.v(getTag(), "cancelAuthentication(): reject " + opPackageName);
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 c43d587..2e76406 100644
--- a/services/core/java/com/android/server/biometrics/face/FaceService.java
+++ b/services/core/java/com/android/server/biometrics/face/FaceService.java
@@ -17,9 +17,9 @@
package com.android.server.biometrics.face;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
-import static android.Manifest.permission.MANAGE_FACE;
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.RESET_FACE_LOCKOUT;
-import static android.Manifest.permission.USE_BIOMETRIC;
+import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL;
import android.app.ActivityManager;
import android.app.AppOpsManager;
@@ -28,10 +28,12 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricPromptServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.face.V1_0.IBiometricsFace;
import android.hardware.biometrics.face.V1_0.IBiometricsFaceClientCallback;
import android.hardware.face.Face;
+import android.hardware.face.FaceManager;
import android.hardware.face.IFaceService;
import android.hardware.face.IFaceServiceReceiver;
import android.os.Binder;
@@ -92,13 +94,13 @@
*/
@Override // Binder call
public long preEnroll(IBinder token) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
return startPreEnroll(token);
}
@Override // Binder call
public int postEnroll(IBinder token) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
return startPostEnroll(token);
}
@@ -106,7 +108,7 @@
public void enroll(final IBinder token, final byte[] cryptoToken, final int userId,
final IFaceServiceReceiver receiver, final int flags,
final String opPackageName) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
final EnrollClientImpl client = new EnrollClientImpl(getContext(), mDaemonWrapper,
@@ -118,39 +120,64 @@
@Override // Binder call
public void cancelEnrollment(final IBinder token) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
cancelEnrollmentInternal(token);
}
@Override // Binder call
public void authenticate(final IBinder token, final long opId,
final IFaceServiceReceiver receiver, final int flags,
- final String opPackageName, final Bundle bundle,
- final IBiometricPromptReceiver dialogReceiver) {
+ final String opPackageName) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
- mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService);
+ mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
+ null /* bundle */, null /* dialogReceiver */, mStatusBarService);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
+ public void authenticateFromService(IBinder token, long opId, int groupId,
+ IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ int callingUid, int callingPid, int callingUserId) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ final boolean restricted = true; // BiometricPrompt is always restricted
+ final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ mDaemonWrapper, mHalDeviceId, token,
+ new BiometricPromptServiceListenerImpl(receiver),
+ mCurrentUserId, 0 /* groupId */, opId, restricted, opPackageName,
+ bundle, dialogReceiver, mStatusBarService);
+ authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
+ callingUserId);
+ }
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
+ public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
+ int callingUid, int callingPid, int callingUserId) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
+ cancelAuthenticationInternal(token, opPackageName,
+ callingUid, callingPid, callingUserId);
+ }
+
+ @Override // Binder call
public void setActiveUser(final int userId) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
setActiveUserInternal(userId);
}
@Override // Binder call
public void remove(final IBinder token, final int faceId, final int userId,
final IFaceServiceReceiver receiver) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
if (token == null) {
Slog.w(TAG, "remove(): token is null");
@@ -168,7 +195,7 @@
@Override
public void enumerate(final IBinder token, final int userId,
final IFaceServiceReceiver receiver) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
final boolean restricted = isRestricted();
final EnumerateClientImpl client = new EnumerateClientImpl(getContext(), mDaemonWrapper,
@@ -180,6 +207,7 @@
@Override
public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback)
throws RemoteException {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
FaceService.super.addLockoutResetCallback(callback);
}
@@ -208,6 +236,7 @@
// TODO: refactor out common code here
@Override // Binder call
public boolean isHardwareDetected(long deviceId, String opPackageName) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
@@ -225,7 +254,7 @@
@Override // Binder call
public void rename(final int faceId, final String name) {
- checkPermission(MANAGE_FACE);
+ checkPermission(MANAGE_BIOMETRIC);
if (!isCurrentUserOrProfile(UserHandle.getCallingUserId())) {
return;
}
@@ -240,6 +269,7 @@
@Override // Binder call
public List<Face> getEnrolledFaces(int userId, String opPackageName) {
+ checkPermission(MANAGE_BIOMETRIC);
if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
@@ -251,6 +281,7 @@
@Override // Binder call
public boolean hasEnrolledFaces(int userId, String opPackageName) {
+ checkPermission(USE_BIOMETRIC_INTERNAL);
if (!canUseBiometric(opPackageName, false /* foregroundOnly */,
Binder.getCallingUid(), Binder.getCallingPid(),
UserHandle.getCallingUserId())) {
@@ -283,7 +314,7 @@
@Override // Binder call
public void resetTimeout(byte[] token) {
- checkPermission(RESET_FACE_LOCKOUT);
+ checkPermission(MANAGE_BIOMETRIC);
// TODO: confirm security token when we move timeout management into the HAL layer.
mHandler.post(mResetFailedAttemptsForCurrentUserRunnable);
}
@@ -291,6 +322,58 @@
/**
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+ * BiometricPrompt.
+ */
+ private class BiometricPromptServiceListenerImpl implements ServiceListener {
+
+ // Use FaceManager to get strings, so BiometricPrompt interface is cleaner
+ private FaceManager mFaceManager;
+ private IBiometricPromptServiceReceiver mBiometricPromptServiceReceiver;
+
+ public BiometricPromptServiceListenerImpl(IBiometricPromptServiceReceiver receiver) {
+ mBiometricPromptServiceReceiver = receiver;
+ mFaceManager = (FaceManager) getContext().getSystemService(Context.FACE_SERVICE);
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException {
+ /**
+ * Map the acquired codes onto existing {@link BiometricConstants} acquired codes.
+ */
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onAcquired(deviceId,
+ mFaceManager.getMappedAcquiredInfo(acquiredInfo, vendorCode),
+ mFaceManager.getAcquiredString(acquiredInfo, vendorCode));
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onAuthenticationSucceeded(deviceId);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onAuthenticationFailed(deviceId);
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onError(deviceId, error,
+ mFaceManager.getErrorString(error, vendorCode));
+ }
+ }
+ }
+
+ /**
+ * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* the FaceManager.
*/
private class ServiceListenerImpl implements ServiceListener {
@@ -616,12 +699,13 @@
@Override
protected String getManageBiometricPermission() {
- return MANAGE_FACE;
+ return MANAGE_BIOMETRIC;
}
@Override
protected void checkUseBiometricPermission() {
- checkPermission(USE_BIOMETRIC);
+ // noop for Face. The permission checks are all done on the incoming binder call.
+ // TODO: Perhaps do the same in FingerprintService
}
@Override
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 bbdf86c9..a25b4b4b 100644
--- a/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
+++ b/services/core/java/com/android/server/biometrics/fingerprint/FingerprintService.java
@@ -17,6 +17,7 @@
package com.android.server.biometrics.fingerprint;
import static android.Manifest.permission.INTERACT_ACROSS_USERS;
+import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_FINGERPRINT;
import static android.Manifest.permission.RESET_FINGERPRINT_LOCKOUT;
import static android.Manifest.permission.USE_BIOMETRIC;
@@ -30,10 +31,12 @@
import android.hardware.biometrics.BiometricAuthenticator;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.IBiometricPromptReceiver;
+import android.hardware.biometrics.IBiometricPromptServiceReceiver;
import android.hardware.biometrics.IBiometricServiceLockoutResetCallback;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprint;
import android.hardware.biometrics.fingerprint.V2_1.IBiometricsFingerprintClientCallback;
import android.hardware.fingerprint.Fingerprint;
+import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.IFingerprintClientActiveCallback;
import android.hardware.fingerprint.IFingerprintService;
import android.hardware.fingerprint.IFingerprintServiceReceiver;
@@ -146,23 +149,46 @@
@Override // Binder call
public void authenticate(final IBinder token, final long opId, final int groupId,
final IFingerprintServiceReceiver receiver, final int flags,
- final String opPackageName, final Bundle bundle,
- final IBiometricPromptReceiver dialogReceiver) {
+ final String opPackageName) {
final boolean restricted = isRestricted();
final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
mDaemonWrapper, mHalDeviceId, token, new ServiceListenerImpl(receiver),
- mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
- dialogReceiver, mStatusBarService);
+ mCurrentUserId, groupId, opId, restricted, opPackageName, null /* bundle */,
+ null /* dialogReceiver */, mStatusBarService);
authenticateInternal(client, opId, opPackageName);
}
@Override // Binder call
+ public void authenticateFromService(IBinder token, long opId, int groupId,
+ IBiometricPromptServiceReceiver receiver, int flags, String opPackageName,
+ Bundle bundle, IBiometricPromptReceiver dialogReceiver,
+ int callingUid, int callingPid, int callingUserId) {
+ checkPermission(MANAGE_BIOMETRIC);
+ final boolean restricted = true; // BiometricPrompt is always restricted
+ final AuthenticationClientImpl client = new AuthenticationClientImpl(getContext(),
+ mDaemonWrapper, mHalDeviceId, token,
+ new BiometricPromptServiceListenerImpl(receiver),
+ mCurrentUserId, groupId, opId, restricted, opPackageName, bundle,
+ dialogReceiver, mStatusBarService);
+ authenticateInternal(client, opId, opPackageName, callingUid, callingPid,
+ callingUserId);
+ }
+
+ @Override // Binder call
public void cancelAuthentication(final IBinder token, final String opPackageName) {
cancelAuthenticationInternal(token, opPackageName);
}
@Override // Binder call
+ public void cancelAuthenticationFromService(final IBinder token, final String opPackageName,
+ int callingUid, int callingPid, int callingUserId) {
+ checkPermission(MANAGE_BIOMETRIC);
+ cancelAuthenticationInternal(token, opPackageName,
+ callingUid, callingPid, callingUserId);
+ }
+
+ @Override // Binder call
public void setActiveUser(final int userId) {
checkPermission(MANAGE_FINGERPRINT);
setActiveUserInternal(userId);
@@ -332,6 +358,55 @@
/**
* Receives callbacks from the ClientMonitor implementations. The results are forwarded to
+ * BiometricPrompt.
+ */
+ private class BiometricPromptServiceListenerImpl implements ServiceListener {
+
+ // Use FingerprintManager to get strings, so BiometricPrompt interface is cleaner
+ private FingerprintManager mFingerprintManager;
+ private IBiometricPromptServiceReceiver mBiometricPromptServiceReceiver;
+
+ public BiometricPromptServiceListenerImpl(IBiometricPromptServiceReceiver receiver) {
+ mBiometricPromptServiceReceiver = receiver;
+ mFingerprintManager = (FingerprintManager)
+ getContext().getSystemService(Context.FINGERPRINT_SERVICE);
+ }
+
+ @Override
+ public void onAcquired(long deviceId, int acquiredInfo, int vendorCode)
+ throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onAcquired(deviceId, acquiredInfo,
+ mFingerprintManager.getAcquiredString(acquiredInfo, vendorCode));
+ }
+ }
+
+ @Override
+ public void onAuthenticationSucceeded(long deviceId,
+ BiometricAuthenticator.Identifier biometric, int userId) throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onAuthenticationSucceeded(deviceId);
+ }
+ }
+
+ @Override
+ public void onAuthenticationFailed(long deviceId) throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onAuthenticationFailed(deviceId);
+ }
+ }
+
+ @Override
+ public void onError(long deviceId, int error, int vendorCode) throws RemoteException {
+ if (mBiometricPromptServiceReceiver != null) {
+ mBiometricPromptServiceReceiver.onError(deviceId, error,
+ mFingerprintManager.getErrorString(error, vendorCode));
+ }
+ }
+ }
+
+ /**
+ * Receives callbacks from the ClientMonitor implementations. The results are forwarded to
* the FingerprintManager.
*/
private class ServiceListenerImpl implements ServiceListener {
diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java
index 2f6fca4..fd982d6 100644
--- a/services/java/com/android/server/SystemServer.java
+++ b/services/java/com/android/server/SystemServer.java
@@ -67,6 +67,7 @@
import com.android.server.am.ActivityManagerService;
import com.android.server.am.ActivityTaskManagerService;
import com.android.server.audio.AudioService;
+import com.android.server.biometrics.BiometricPromptService;
import com.android.server.broadcastradio.BroadcastRadioService;
import com.android.server.camera.CameraServiceProxy;
import com.android.server.clipboard.ClipboardService;
@@ -1552,18 +1553,30 @@
}
traceEnd();
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE)) {
+ final boolean hasFeatureFace
+ = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FACE);
+ final boolean hasFeatureFingerprint
+ = mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT);
+
+ if (hasFeatureFace) {
traceBeginAndSlog("StartFaceSensor");
mSystemServiceManager.startService(FaceService.class);
traceEnd();
}
- if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_FINGERPRINT)) {
+ if (hasFeatureFingerprint) {
traceBeginAndSlog("StartFingerprintSensor");
mSystemServiceManager.startService(FingerprintService.class);
traceEnd();
}
+ if (hasFeatureFace || hasFeatureFingerprint) {
+ // Start this service after all biometric services.
+ traceBeginAndSlog("StartBiometricPromptService");
+ mSystemServiceManager.startService(BiometricPromptService.class);
+ traceEnd();
+ }
+
traceBeginAndSlog("StartBackgroundDexOptService");
try {
BackgroundDexOptService.schedule(context);