diff --git a/core/java/android/hardware/biometrics/BiometricManager.java b/core/java/android/hardware/biometrics/BiometricManager.java index 4145a7273ed28..08b1e245dc831 100644 --- a/core/java/android/hardware/biometrics/BiometricManager.java +++ b/core/java/android/hardware/biometrics/BiometricManager.java @@ -29,7 +29,6 @@ import android.annotation.SystemService; import android.annotation.TestApi; import android.content.Context; import android.os.RemoteException; -import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.util.Slog; @@ -46,6 +45,13 @@ public class BiometricManager { private static final String TAG = "BiometricManager"; + /** + * An ID that should match any biometric sensor on the device. + * + * @hide + */ + public static final int SENSOR_ID_ANY = -1; + /** * No error detected. */ @@ -139,7 +145,7 @@ public class BiometricManager { * *

This corresponds to {@link KeyProperties#AUTH_BIOMETRIC_STRONG} during key generation. * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder */ int BIOMETRIC_STRONG = 0x000F; @@ -182,7 +188,7 @@ public class BiometricManager { *

This corresponds to {@link KeyProperties#AUTH_DEVICE_CREDENTIAL} during key * generation. * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder */ int DEVICE_CREDENTIAL = 1 << 15; } diff --git a/core/java/android/hardware/biometrics/BiometricPrompt.java b/core/java/android/hardware/biometrics/BiometricPrompt.java index 76cf9b9d28b98..4f6a7c75cca60 100644 --- a/core/java/android/hardware/biometrics/BiometricPrompt.java +++ b/core/java/android/hardware/biometrics/BiometricPrompt.java @@ -36,7 +36,6 @@ import android.os.Parcel; import android.os.RemoteException; import android.os.ServiceManager; import android.security.identity.IdentityCredential; -import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyProperties; import android.text.TextUtils; import android.util.Log; @@ -325,7 +324,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * request authentication with the proper set of authenticators (e.g. match the * authenticators specified during key generation). * - * @see KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int) + * @see android.security.keystore.KeyGenParameterSpec.Builder * @see KeyProperties#AUTH_BIOMETRIC_STRONG * @see KeyProperties#AUTH_DEVICE_CREDENTIAL * @@ -364,6 +363,21 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan return this; } + /** + * If set, authenticate using the biometric sensor with the given ID. + * + * @param sensorId The ID of a biometric sensor, or -1 to allow any sensor (default). + * @return This builder. + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + @NonNull + public Builder setSensorId(int sensorId) { + mPromptInfo.setSensorId(sensorId); + return this; + } + /** * Creates a {@link BiometricPrompt}. * @@ -589,7 +603,8 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan * *

Cryptographic operations in Android can be split into two categories: auth-per-use and * time-based. This is specified during key creation via the timeout parameter of the - * {@link KeyGenParameterSpec.Builder#setUserAuthenticationParameters(int, int)} API. + * {@code setUserAuthenticationParameters(int, int)} method of {@link + * android.security.keystore.KeyGenParameterSpec.Builder}. * *

CryptoObjects are used to unlock auth-per-use keys via * {@link BiometricPrompt#authenticate(CryptoObject, CancellationSignal, Executor, @@ -778,6 +793,27 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { + authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */); + } + + /** + * Authenticates for the given user and keystore operation. + * + * @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 + * @param userId The user to authenticate + * @param operationId The keystore operation associated with authentication + * + * @hide + */ + @RequiresPermission(USE_BIOMETRIC_INTERNAL) + public void authenticateUserForOperation( + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback, + int userId, + long operationId) { if (cancel == null) { throw new IllegalArgumentException("Must supply a cancellation signal"); } @@ -787,7 +823,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan if (callback == null) { throw new IllegalArgumentException("Must supply a callback"); } - authenticateInternal(null /* crypto */, cancel, executor, callback, userId); + authenticateInternal(operationId, cancel, executor, callback, userId); } /** @@ -912,11 +948,31 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } } - private void authenticateInternal(@Nullable CryptoObject crypto, + private void authenticateInternal( + @Nullable CryptoObject crypto, @NonNull CancellationSignal cancel, @NonNull @CallbackExecutor Executor executor, @NonNull AuthenticationCallback callback, int userId) { + + mCryptoObject = crypto; + final long operationId = crypto != null ? crypto.getOpId() : 0L; + authenticateInternal(operationId, cancel, executor, callback, userId); + } + + private void authenticateInternal( + long operationId, + @NonNull CancellationSignal cancel, + @NonNull @CallbackExecutor Executor executor, + @NonNull AuthenticationCallback callback, + int userId) { + + // Ensure we don't return the wrong crypto object as an auth result. + if (mCryptoObject != null && mCryptoObject.getOpId() != operationId) { + Log.w(TAG, "CryptoObject operation ID does not match argument; setting field to null"); + mCryptoObject = null; + } + try { if (cancel.isCanceled()) { Log.w(TAG, "Authentication already canceled"); @@ -925,13 +981,11 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan cancel.setOnCancelListener(new OnAuthenticationCancelListener()); } - mCryptoObject = crypto; mExecutor = executor; mAuthenticationCallback = callback; - final long operationId = crypto != null ? crypto.getOpId() : 0; final PromptInfo promptInfo; - if (crypto != null) { + if (operationId != 0L) { // Allowed authenticators should default to BIOMETRIC_STRONG for crypto auth. // Note that we use a new PromptInfo here so as to not overwrite the application's // preference, since it is possible that the same prompt configuration be used @@ -952,10 +1006,9 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan } 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)); - }); + 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/PromptInfo.java b/core/java/android/hardware/biometrics/PromptInfo.java index c2eff7de832b9..0e99f31d3b520 100644 --- a/core/java/android/hardware/biometrics/PromptInfo.java +++ b/core/java/android/hardware/biometrics/PromptInfo.java @@ -40,6 +40,7 @@ public class PromptInfo implements Parcelable { private @BiometricManager.Authenticators.Types int mAuthenticators; private boolean mDisallowBiometricsIfPolicyExists; private boolean mReceiveSystemEvents; + private int mSensorId = -1; public PromptInfo() { @@ -59,6 +60,7 @@ public class PromptInfo implements Parcelable { mAuthenticators = in.readInt(); mDisallowBiometricsIfPolicyExists = in.readBoolean(); mReceiveSystemEvents = in.readBoolean(); + mSensorId = in.readInt(); } public static final Creator CREATOR = new Creator() { @@ -93,6 +95,7 @@ public class PromptInfo implements Parcelable { dest.writeInt(mAuthenticators); dest.writeBoolean(mDisallowBiometricsIfPolicyExists); dest.writeBoolean(mReceiveSystemEvents); + dest.writeInt(mSensorId); } public boolean containsPrivateApiConfigurations() { @@ -166,6 +169,10 @@ public class PromptInfo implements Parcelable { mReceiveSystemEvents = receiveSystemEvents; } + public void setSensorId(int sensorId) { + mSensorId = sensorId; + } + // Getters public CharSequence getTitle() { @@ -226,4 +233,8 @@ public class PromptInfo implements Parcelable { public boolean isReceiveSystemEvents() { return mReceiveSystemEvents; } + + public int getSensorId() { + return mSensorId; + } } diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java index d932865314652..66b9600941ae2 100644 --- a/core/java/android/hardware/fingerprint/FingerprintManager.java +++ b/core/java/android/hardware/fingerprint/FingerprintManager.java @@ -24,7 +24,6 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresFeature; @@ -56,8 +55,6 @@ import android.security.identity.IdentityCredential; import android.util.Slog; import android.view.Surface; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.security.Signature; import java.util.ArrayList; import java.util.List; @@ -98,13 +95,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ public static final int SENSOR_ID_ANY = -1; - /** - * @hide - */ - @IntDef({SENSOR_ID_ANY}) - @Retention(RetentionPolicy.SOURCE) - public @interface SensorId {} - private IFingerprintService mService; private Context mContext; private IBinder mToken = new Binder(); @@ -508,8 +498,8 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(anyOf = {USE_BIOMETRIC, USE_FINGERPRINT}) public void authenticate(@Nullable CryptoObject crypto, @Nullable CancellationSignal cancel, - @NonNull AuthenticationCallback callback, Handler handler, @SensorId int sensorId, - int userId) { + @NonNull AuthenticationCallback callback, Handler handler, int sensorId, int userId) { + if (callback == null) { throw new IllegalArgumentException("Must supply an authentication callback"); } @@ -653,15 +643,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void generateChallenge(int userId, GenerateChallengeCallback callback) { - final List fingerprintSensorProperties = - getSensorPropertiesInternal(); - if (fingerprintSensorProperties.isEmpty()) { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + if (sensorProps == null) { Slog.e(TAG, "No sensors"); return; } - - final int sensorId = fingerprintSensorProperties.get(0).sensorId; - generateChallenge(sensorId, userId, callback); + generateChallenge(sensorProps.sensorId, userId, callback); } /** @@ -681,18 +668,18 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing */ @RequiresPermission(MANAGE_FINGERPRINT) public void revokeChallenge(int userId, long challenge) { - if (mService != null) try { - final List fingerprintSensorProperties = - getSensorPropertiesInternal(); - if (fingerprintSensorProperties.isEmpty()) { - Slog.e(TAG, "No sensors"); - return; + if (mService != null) { + try { + final FingerprintSensorPropertiesInternal sensorProps = getFirstFingerprintSensor(); + if (sensorProps == null) { + Slog.e(TAG, "No sensors"); + return; + } + mService.revokeChallenge(mToken, sensorProps.sensorId, userId, + mContext.getOpPackageName(), challenge); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); } - final int sensorId = fingerprintSensorProperties.get(0).sensorId; - mService.revokeChallenge(mToken, sensorId, userId, mContext.getOpPackageName(), - challenge); - } catch (RemoteException e) { - throw e.rethrowFromSystemServer(); } } @@ -1161,6 +1148,12 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing } } + @Nullable + private FingerprintSensorPropertiesInternal getFirstFingerprintSensor() { + final List allSensors = getSensorPropertiesInternal(); + return allSensors.isEmpty() ? null : allSensors.get(0); + } + private void cancelEnrollment() { if (mService != null) try { mService.cancelEnrollment(mToken); diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 996fbb3665064..13d0c1bbe5ec0 100644 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -1585,6 +1585,8 @@ Finger %d + + Use your fingerprint to continue diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index e2271d17f29c1..43d1db0c4d330 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -2487,6 +2487,7 @@ + diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index 055270ddf0b87..7d06dd6ab0855 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -23,7 +23,6 @@ import static android.hardware.biometrics.BiometricManager.Authenticators; import android.annotation.Nullable; import android.app.ActivityManager; import android.app.ActivityTaskManager; -import android.app.IActivityTaskManager; import android.app.TaskStackListener; import android.content.BroadcastReceiver; import android.content.Context; @@ -137,7 +136,8 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks, mActivityTaskManager.getTasks(1); if (!runningTasks.isEmpty()) { final String topPackage = runningTasks.get(0).topActivity.getPackageName(); - if (!topPackage.contentEquals(clientPackage)) { + if (!topPackage.contentEquals(clientPackage) + && !Utils.isSystem(mContext, clientPackage)) { Log.w(TAG, "Evicting client due to: " + topPackage); mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog = null; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java index fd5e85a953ad7..076c7cbe3937b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/Utils.java @@ -16,13 +16,16 @@ package com.android.systemui.biometrics; +import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.hardware.biometrics.BiometricManager.Authenticators; import static android.view.accessibility.AccessibilityEvent.CONTENT_CHANGE_TYPE_SUBTREE; import android.annotation.IntDef; +import android.annotation.NonNull; import android.annotation.Nullable; import android.app.admin.DevicePolicyManager; import android.content.Context; +import android.content.pm.PackageManager; import android.hardware.biometrics.PromptInfo; import android.hardware.biometrics.SensorPropertiesInternal; import android.os.UserManager; @@ -116,4 +119,10 @@ public class Utils { return false; } + + static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) { + final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + return hasPermission && "android".equals(clientPackage); + } } diff --git a/services/core/java/com/android/server/biometrics/PreAuthInfo.java b/services/core/java/com/android/server/biometrics/PreAuthInfo.java index 6905b3da9bc40..6851d7148191b 100644 --- a/services/core/java/com/android/server/biometrics/PreAuthInfo.java +++ b/services/core/java/com/android/server/biometrics/PreAuthInfo.java @@ -90,6 +90,7 @@ class PreAuthInfo { int userId, PromptInfo promptInfo, String opPackageName, boolean checkDevicePolicyManager) throws RemoteException { + final boolean confirmationRequested = promptInfo.isConfirmationRequested(); final boolean biometricRequested = Utils.isBiometricRequested(promptInfo); final int requestedStrength = Utils.getPublicBiometricStrength(promptInfo); @@ -111,7 +112,7 @@ class PreAuthInfo { @AuthenticatorStatus int status = getStatusForBiometricAuthenticator( devicePolicyManager, settingObserver, sensor, userId, opPackageName, - checkDevicePolicyManager, requestedStrength); + checkDevicePolicyManager, requestedStrength, promptInfo.getSensorId()); Slog.d(TAG, "Package: " + opPackageName + " Sensor ID: " + sensor.id @@ -141,7 +142,11 @@ class PreAuthInfo { DevicePolicyManager devicePolicyManager, BiometricService.SettingObserver settingObserver, BiometricSensor sensor, int userId, String opPackageName, - boolean checkDevicePolicyManager, int requestedStrength) { + boolean checkDevicePolicyManager, int requestedStrength, int requestedSensorId) { + + if (requestedSensorId != BiometricManager.SENSOR_ID_ANY && sensor.id != requestedSensorId) { + return BIOMETRIC_NO_HARDWARE; + } final boolean wasStrongEnough = Utils.isAtLeastStrength(sensor.oemStrength, requestedStrength); diff --git a/services/core/java/com/android/server/biometrics/Utils.java b/services/core/java/com/android/server/biometrics/Utils.java index d87af4280ca35..5cd0bbfa45006 100644 --- a/services/core/java/com/android/server/biometrics/Utils.java +++ b/services/core/java/com/android/server/biometrics/Utils.java @@ -399,10 +399,15 @@ public class Utils { } } - public static boolean isKeyguard(Context context, String clientPackage) { - final boolean hasPermission = context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) - == PackageManager.PERMISSION_GRANTED; - + /** + * Checks if a client package matches Keyguard and can perform internal biometric operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against Keyguard. + * @return Whether the given package matches Keyguard. + */ + public static boolean isKeyguard(@NonNull Context context, @Nullable String clientPackage) { + final boolean hasPermission = hasInternalPermission(context); final ComponentName keyguardComponent = ComponentName.unflattenFromString( context.getResources().getString(R.string.config_keyguardComponent)); final String keyguardPackage = keyguardComponent != null @@ -410,6 +415,34 @@ public class Utils { return hasPermission && keyguardPackage != null && keyguardPackage.equals(clientPackage); } + /** + * Checks if a client package matches the Android system and can perform internal biometric + * operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against the Android system. + * @return Whether the given package matches the Android system. + */ + public static boolean isSystem(@NonNull Context context, @Nullable String clientPackage) { + return hasInternalPermission(context) && "android".equals(clientPackage); + } + + /** + * Checks if a client package matches Settings and can perform internal biometric operations. + * + * @param context The system context. + * @param clientPackage The name of the package to be checked against Settings. + * @return Whether the given package matches Settings. + */ + public static boolean isSettings(@NonNull Context context, @Nullable String clientPackage) { + return hasInternalPermission(context) && "com.android.settings".equals(clientPackage); + } + + private static boolean hasInternalPermission(@NonNull Context context) { + return context.checkCallingOrSelfPermission(USE_BIOMETRIC_INTERNAL) + == PackageManager.PERMISSION_GRANTED; + } + public static String getClientName(@Nullable BaseClientMonitor client) { return client != null ? client.getClass().getSimpleName() : "null"; } diff --git a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java index 14433fb0ea9a3..0536e78e58f66 100644 --- a/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java +++ b/services/core/java/com/android/server/biometrics/sensors/AuthenticationClient.java @@ -149,9 +149,10 @@ public abstract class AuthenticationClient extends AcquisitionClient pm.incrementAuthForUser(getTargetUserId(), authenticated); } - // Ensure authentication only succeeds if the client activity is on top or is keyguard. + // Ensure authentication only succeeds if the client activity is on top. boolean isBackgroundAuth = false; - if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())) { + if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString()) + && !Utils.isSystem(getContext(), getOwnerString())) { final List tasks = mActivityTaskManager.getTasks(1); if (tasks == null || tasks.isEmpty()) { @@ -166,7 +167,7 @@ public abstract class AuthenticationClient extends AcquisitionClient final String topPackage = topActivity.getPackageName(); if (!topPackage.contentEquals(getOwnerString())) { Slog.e(TAG, "Background authentication detected, top: " + topPackage - + ", client: " + this); + + ", client: " + getOwnerString()); isBackgroundAuth = true; } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java index 0265cb93ac8bf..686f9f525426e 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintService.java @@ -25,6 +25,10 @@ import static android.Manifest.permission.USE_BIOMETRIC; import static android.Manifest.permission.USE_BIOMETRIC_INTERNAL; import static android.Manifest.permission.USE_FINGERPRINT; import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_VENDOR; +import static android.hardware.biometrics.SensorProperties.STRENGTH_STRONG; import android.annotation.NonNull; import android.annotation.Nullable; @@ -32,6 +36,7 @@ import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.hardware.biometrics.BiometricManager; +import android.hardware.biometrics.BiometricPrompt; import android.hardware.biometrics.BiometricsProtoEnums; import android.hardware.biometrics.IBiometricSensorReceiver; import android.hardware.biometrics.IBiometricService; @@ -49,6 +54,7 @@ import android.hardware.fingerprint.IFingerprintServiceReceiver; import android.hardware.fingerprint.IUdfpsOverlayController; import android.os.Binder; import android.os.Build; +import android.os.CancellationSignal; import android.os.Handler; import android.os.IBinder; import android.os.Process; @@ -80,6 +86,7 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.concurrent.Executor; /** * A service to manage multiple clients that want to access the fingerprint HAL API. @@ -219,8 +226,8 @@ public class FingerprintService extends SystemService implements BiometricServic @SuppressWarnings("deprecation") @Override // Binder call public void authenticate(final IBinder token, final long operationId, - @FingerprintManager.SensorId final int sensorId, final int userId, - final IFingerprintServiceReceiver receiver, final String opPackageName) { + final int sensorId, final int userId, final IFingerprintServiceReceiver receiver, + final String opPackageName) { final int callingUid = Binder.getCallingUid(); final int callingPid = Binder.getCallingPid(); final int callingUserId = UserHandle.getCallingUserId(); @@ -236,7 +243,7 @@ public class FingerprintService extends SystemService implements BiometricServic final boolean isKeyguard = Utils.isKeyguard(getContext(), opPackageName); // Clear calling identity when checking LockPatternUtils for StrongAuth flags. - final long identity = Binder.clearCallingIdentity(); + long identity = Binder.clearCallingIdentity(); try { if (isKeyguard && Utils.isUserEncryptedOrLockdown(mLockPatternUtils, userId)) { // If this happens, something in KeyguardUpdateMonitor is wrong. @@ -266,9 +273,101 @@ public class FingerprintService extends SystemService implements BiometricServic return; } - provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, - 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, - restricted, statsClient, isKeyguard); + final FingerprintSensorPropertiesInternal sensorProps = + provider.second.getSensorProperties(sensorId); + if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) + && sensorProps != null && sensorProps.isAnyUdfpsType()) { + identity = Binder.clearCallingIdentity(); + try { + authenticateWithPrompt(operationId, sensorProps, userId, receiver); + } finally { + Binder.restoreCallingIdentity(identity); + } + } else { + provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, + 0 /* cookie */, new ClientMonitorCallbackConverter(receiver), opPackageName, + restricted, statsClient, isKeyguard); + } + } + + private void authenticateWithPrompt( + final long operationId, + @NonNull final FingerprintSensorPropertiesInternal props, + final int userId, + final IFingerprintServiceReceiver receiver) { + + final Context context = getUiContext(); + final Executor executor = context.getMainExecutor(); + + final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) + .setTitle(context.getString(R.string.biometric_dialog_default_title)) + .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle)) + .setNegativeButton( + context.getString(R.string.cancel), + executor, + (dialog, which) -> { + try { + receiver.onError( + FINGERPRINT_ERROR_USER_CANCELED, 0 /* vendorCode */); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in negative button onClick()", e); + } + }) + .setSensorId(props.sensorId) + .build(); + + final BiometricPrompt.AuthenticationCallback promptCallback = + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationError(int errorCode, CharSequence errString) { + try { + if (FingerprintUtils.isKnownErrorCode(errorCode)) { + receiver.onError(errorCode, 0 /* vendorCode */); + } else { + receiver.onError(FINGERPRINT_ERROR_VENDOR, errorCode); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationError()", e); + } + } + + @Override + public void onAuthenticationSucceeded( + BiometricPrompt.AuthenticationResult result) { + final Fingerprint fingerprint = new Fingerprint("", 0, 0L); + final boolean isStrong = props.sensorStrength == STRENGTH_STRONG; + try { + receiver.onAuthenticationSucceeded(fingerprint, userId, isStrong); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationSucceeded()", e); + } + } + + @Override + public void onAuthenticationFailed() { + try { + receiver.onAuthenticationFailed(); + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationFailed()", e); + } + } + + @Override + public void onAuthenticationAcquired(int acquireInfo) { + try { + if (FingerprintUtils.isKnownAcquiredCode(acquireInfo)) { + receiver.onAcquired(acquireInfo, 0 /* vendorCode */); + } else { + receiver.onAcquired(FINGERPRINT_ACQUIRED_VENDOR, acquireInfo); + } + } catch (RemoteException e) { + Slog.e(TAG, "Remote exception in onAuthenticationAcquired()", e); + } + } + }; + + biometricPrompt.authenticateUserForOperation( + new CancellationSignal(), executor, promptCallback, userId, operationId); } @Override @@ -374,6 +473,7 @@ public class FingerprintService extends SystemService implements BiometricServic @Override // Binder call public void cancelAuthenticationFromService(final int sensorId, final IBinder token, final String opPackageName) { + Utils.checkPermission(getContext(), MANAGE_BIOMETRIC); final ServiceProvider provider = getProviderForSensor(sensorId); diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java index dc6fd3a1b26de..d69151da55f67 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/FingerprintUtils.java @@ -16,8 +16,18 @@ package com.android.server.biometrics.sensors.fingerprint; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_IMAGER_DIRTY; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_INSUFFICIENT; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_PARTIAL; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_FAST; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_TOO_SLOW; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_VENDOR; + import android.annotation.Nullable; import android.content.Context; +import android.hardware.biometrics.fingerprint.V2_1.FingerprintError; import android.hardware.fingerprint.Fingerprint; import android.text.TextUtils; import android.util.SparseArray; @@ -138,5 +148,51 @@ public class FingerprintUtils implements BiometricUtils { return state; } } + + /** + * Checks if the given error code corresponds to a known fingerprint error. + * + * @param errorCode The error code to be checked. + * @return Whether the error code corresponds to a known error. + */ + public static boolean isKnownErrorCode(int errorCode) { + switch (errorCode) { + case FingerprintError.ERROR_HW_UNAVAILABLE: + case FingerprintError.ERROR_UNABLE_TO_PROCESS: + case FingerprintError.ERROR_TIMEOUT: + case FingerprintError.ERROR_NO_SPACE: + case FingerprintError.ERROR_CANCELED: + case FingerprintError.ERROR_UNABLE_TO_REMOVE: + case FingerprintError.ERROR_LOCKOUT: + case FingerprintError.ERROR_VENDOR: + return true; + + default: + return false; + } + } + + /** + * Checks if the given acquired code corresponds to a known fingerprint error. + * + * @param acquiredCode The acquired code to be checked. + * @return Whether the acquired code corresponds to a known error. + */ + public static boolean isKnownAcquiredCode(int acquiredCode) { + switch (acquiredCode) { + case FINGERPRINT_ACQUIRED_GOOD: + case FINGERPRINT_ACQUIRED_PARTIAL: + case FINGERPRINT_ACQUIRED_INSUFFICIENT: + case FINGERPRINT_ACQUIRED_IMAGER_DIRTY: + case FINGERPRINT_ACQUIRED_TOO_SLOW: + case FINGERPRINT_ACQUIRED_TOO_FAST: + case FINGERPRINT_ACQUIRED_VENDOR: + case FINGERPRINT_ACQUIRED_START: + return true; + + default: + return false; + } + } } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java index f8450245a18de..fec2c4670439a 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintProvider.java @@ -96,7 +96,8 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi Slog.e(getTag(), "Task stack changed for client: " + client); continue; } - if (Utils.isKeyguard(mContext, client.getOwnerString())) { + if (Utils.isKeyguard(mContext, client.getOwnerString()) + || Utils.isSystem(mContext, client.getOwnerString())) { continue; // Keyguard is always allowed } diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java index acc575fb19731..0fee0a2e81468 100644 --- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java +++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/Fingerprint21.java @@ -122,7 +122,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider Slog.e(TAG, "Task stack changed for client: " + client); return; } - if (Utils.isKeyguard(mContext, client.getOwnerString())) { + if (Utils.isKeyguard(mContext, client.getOwnerString()) + || Utils.isSystem(mContext, client.getOwnerString())) { return; // Keyguard is always allowed }