diff options
| author | 2018-08-27 12:39:17 -0700 | |
|---|---|---|
| committer | 2018-08-30 14:49:32 -0700 | |
| commit | a24e9fd9acf0c7abbdbe40dbc2c2015d28acad49 (patch) | |
| tree | a5407268d03f9a7198161fe786670cffb6106c03 | |
| parent | 836f2cfb9eaee16458876f8ddaa2d3f778f02be1 (diff) | |
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
23 files changed, 929 insertions, 444 deletions
diff --git a/Android.bp b/Android.bp index 759014ffc936..39da11fb3057 100644 --- a/Android.bp +++ b/Android.bp @@ -151,6 +151,8 @@ java_library { ":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 a352e84b060e..4fb21f3ad5e3 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -3672,6 +3672,15 @@ public abstract class Context { 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 ac2f1ce7a1f5..7d8ff4c3d0c8 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -2281,6 +2281,14 @@ public abstract class PackageManager { /** * 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 e7c5116e5f6c..59195dc903f7 100644 --- a/core/java/android/hardware/biometrics/BiometricAuthenticator.java +++ b/core/java/android/hardware/biometrics/BiometricAuthenticator.java @@ -161,12 +161,6 @@ public interface BiometricAuthenticator { 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 @@ public interface BiometricAuthenticator { }; /** + * @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 @@ public interface BiometricAuthenticator { * @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 @@ public interface BiometricAuthenticator { * @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 5cf8f457a08d..3612e9da7423 100644 --- a/core/java/android/hardware/biometrics/BiometricConstants.java +++ b/core/java/android/hardware/biometrics/BiometricConstants.java @@ -172,5 +172,5 @@ public interface BiometricConstants { /** * @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 4aa1e760c635..c788bc527205 100644 --- a/core/java/android/hardware/biometrics/BiometricFaceConstants.java +++ b/core/java/android/hardware/biometrics/BiometricFaceConstants.java @@ -229,7 +229,8 @@ public interface BiometricFaceConstants { * * @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 02bcff546b10..1cca27d158ab 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -20,14 +20,20 @@ import static android.Manifest.permission.USE_BIOMETRIC; 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 @@ import javax.crypto.Mac; */ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstants { + private static final String TAG = "BiometricPrompt"; + /** * @hide */ @@ -208,11 +216,23 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } } - 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } }; + 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan /** * 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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan */ @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 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @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 class BiometricPrompt implements BiometricAuthenticator, BiometricConstan 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"); + } + if (executor == null) { + throw new IllegalArgumentException("Must supply an executor"); } - mFingerprintManager.authenticate(cancel, mBundle, executor, mDialogReceiver, callback); + 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 000000000000..2c93579bc8bd --- /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 000000000000..1ef6c52c1594 --- /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 6a3dd7dd0693..b3b962f0aca8 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.annotation.SystemService; 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 android.util.Slog; 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 @@ public class FaceManager implements BiometricFaceConstants { 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 @@ public class FaceManager implements BiometricFaceConstants { * @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 @@ public class FaceManager implements BiometricFaceConstants { 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 @@ public class FaceManager implements BiometricFaceConstants { } /** - * 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 @@ public class FaceManager implements BiometricFaceConstants { * @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 @@ public class FaceManager implements BiometricFaceConstants { * * @hide */ - @RequiresPermission(MANAGE_FACE) + @RequiresPermission(MANAGE_BIOMETRIC) public long preEnroll() { long result = 0; if (mService != null) { @@ -373,7 +265,7 @@ public class FaceManager implements BiometricFaceConstants { * * @hide */ - @RequiresPermission(MANAGE_FACE) + @RequiresPermission(MANAGE_BIOMETRIC) public int postEnroll() { int result = 0; if (mService != null) { @@ -392,7 +284,7 @@ public class FaceManager implements BiometricFaceConstants { * * @hide */ - @RequiresPermission(MANAGE_FACE) + @RequiresPermission(MANAGE_BIOMETRIC) public void setActiveUser(int userId) { if (mService != null) { try { @@ -412,7 +304,7 @@ public class FaceManager implements BiometricFaceConstants { * 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 @@ public class FaceManager implements BiometricFaceConstants { * @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 @@ public class FaceManager implements BiometricFaceConstants { * @return the current face item * @hide */ - @RequiresPermission(USE_BIOMETRIC) + @RequiresPermission(MANAGE_BIOMETRIC) public List<Face> getEnrolledFaces() { return getEnrolledFaces(UserHandle.myUserId()); } @@ -463,8 +355,9 @@ public class FaceManager implements BiometricFaceConstants { * * @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 @@ public class FaceManager implements BiometricFaceConstants { * @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 @@ public class FaceManager implements BiometricFaceConstants { * * @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 @@ public class FaceManager implements BiometricFaceConstants { * @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 @@ public class FaceManager implements BiometricFaceConstants { /** * @hide */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) public void addLockoutResetCallback(final LockoutResetCallback callback) { if (mService != null) { try { @@ -617,7 +513,8 @@ public class FaceManager implements BiometricFaceConstants { } } - 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 @@ public class FaceManager implements BiometricFaceConstants { 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 @@ public class FaceManager implements BiometricFaceConstants { } /** + * 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 class FaceManager implements BiometricFaceConstants { */ 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 @@ public class FaceManager implements BiometricFaceConstants { 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 03bb7aeccef3..dd995c985286 100644 --- a/core/java/android/hardware/face/IFaceService.aidl +++ b/core/java/android/hardware/face/IFaceService.aidl @@ -17,23 +17,35 @@ package android.hardware.face; 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 15868f12eb64..44b8faf0509c 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -18,10 +18,10 @@ package android.hardware.fingerprint; 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.content.pm.PackageManager; 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 @@ import javax.crypto.Mac; @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 @@ public class FingerprintManager implements BiometricFingerprintConstants { 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 @@ public class FingerprintManager implements BiometricFingerprintConstants { */ @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 @@ public class FingerprintManager implements BiometricFingerprintConstants { }; /** - * 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 @@ public class FingerprintManager implements BiometricFingerprintConstants { 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 @@ public class FingerprintManager implements BiometricFingerprintConstants { } /** - * 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 @@ public class FingerprintManager implements BiometricFingerprintConstants { } /** + * @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 @@ public class FingerprintManager implements BiometricFingerprintConstants { */ @Deprecated @RequiresPermission(USE_FINGERPRINT) + @Override public boolean isHardwareDetected() { if (mService != null) { try { @@ -826,6 +717,7 @@ public class FingerprintManager implements BiometricFingerprintConstants { * * @hide */ + @RequiresPermission(RESET_FINGERPRINT_LOCKOUT) public void resetTimeout(byte[] token) { if (mService != null) { try { @@ -954,8 +846,8 @@ public class FingerprintManager implements BiometricFingerprintConstants { 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 @@ public class FingerprintManager implements BiometricFingerprintConstants { /** * @hide */ + @Override public String getErrorString(int errMsg, int vendorCode) { switch (errMsg) { case FINGERPRINT_ERROR_UNABLE_TO_PROCESS: @@ -1127,47 +1020,23 @@ public class FingerprintManager implements BiometricFingerprintConstants { @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 71a642095148..2b2c0b7a098f 100644 --- a/core/java/android/hardware/fingerprint/IFingerprintService.aidl +++ b/core/java/android/hardware/fingerprint/IFingerprintService.aidl @@ -17,6 +17,7 @@ package android.hardware.fingerprint; 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 @@ import java.util.List; * @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 67bdbf6fe4c4..5258518db281 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 dfd5e8143d53..e3b9052654d0 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 8ac247461712..c6387f0e0881 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 a5616d5e80ae..e31dd1e6b606 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 3e534d11e56e..ff6a1c9501cc 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java @@ -1597,7 +1597,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener { 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 000000000000..29eda8ba126e --- /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 9b224446723b..cc2e81ff4306 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 @@ public abstract class BiometricService extends SystemService implements IHwBinde * 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 @@ public abstract class BiometricService extends SystemService implements IHwBinde 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 @@ public abstract class BiometricService extends SystemService implements IHwBinde 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 @@ public abstract class BiometricService extends SystemService implements IHwBinde 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 c43d587f64c7..2e76406489fe 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.content.pm.UserInfo; 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 @@ public class FaceService extends BiometricService { */ @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 class FaceService extends BiometricService { 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 @@ public class FaceService extends BiometricService { @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 @@ public class FaceService extends BiometricService { @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 @@ public class FaceService extends BiometricService { @Override public void addLockoutResetCallback(final IBiometricServiceLockoutResetCallback callback) throws RemoteException { + checkPermission(USE_BIOMETRIC_INTERNAL); FaceService.super.addLockoutResetCallback(callback); } @@ -208,6 +236,7 @@ public class FaceService extends BiometricService { // 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 @@ public class FaceService extends BiometricService { @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 @@ public class FaceService extends BiometricService { @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 @@ public class FaceService extends BiometricService { @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 @@ public class FaceService extends BiometricService { @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 @@ public class FaceService extends BiometricService { /** * 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 @@ public class FaceService extends BiometricService { @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 bbdf86c94f63..a25b4b4bdf05 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.content.pm.UserInfo; 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 @@ public class FingerprintService extends BiometricService { @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 @@ public class FingerprintService extends BiometricService { /** * 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 2f6fca44a61e..fd982d6a3240 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.accessibility.AccessibilityManagerService; 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 @@ public final class SystemServer { } 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); |