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 98b36c5c9cbfb..f6d1b7da78f04 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 80163b16ada0d..ae5e2f5979524 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2488,6 +2488,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 1135126d86fb5..80d88728ceaf0 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
@@ -125,7 +125,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
}