Make FingerprintManager show BiometricPrompt for UDFPS

For apps that are still using FingerprintManager, have the system show
BiometricPrompt when authenticating with a UDFPS sensor. This will
ensure that the user is directed to authenticate on the correct part of
the screen, rather than requiring the app to draw an authentication UI.

Test: FingerprintManager test app, Keyguard, and Settings.

Bug: 174490952
Change-Id: I60ba2dce983812996f6baa44557533575051e476
Merged-In: I60ba2dce983812996f6baa44557533575051e476
This commit is contained in:
Curtis Belmonte
2021-01-08 15:55:25 -08:00
committed by Kevin Chyn
parent 7f2c016bdd
commit 45807b0b4a
15 changed files with 335 additions and 63 deletions

View File

@@ -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 {
*
* <p>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 {
* <p>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;
}

View File

@@ -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
*
* <p>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}.
*
* <p>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)));
}
}
}

View File

@@ -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<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -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;
}
}

View File

@@ -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<FingerprintSensorPropertiesInternal> 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<FingerprintSensorPropertiesInternal> 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<FingerprintSensorPropertiesInternal> allSensors = getSensorPropertiesInternal();
return allSensors.isEmpty() ? null : allSensors.get(0);
}
private void cancelEnrollment() {
if (mService != null) try {
mService.cancelEnrollment(mToken);

View File

@@ -1585,6 +1585,8 @@
<!-- Template to be used to name enrolled fingerprints by default. -->
<string name="fingerprint_name_template">Finger <xliff:g id="fingerId" example="1">%d</xliff:g></string>
<!-- Subtitle shown on the system-provided biometric dialog, asking the user to authenticate with their fingerprint. [CHAR LIMIT=70] -->
<string name="fingerprint_dialog_default_subtitle">Use your fingerprint to continue</string>
<!-- Array containing custom error messages from vendor. Vendor is expected to add and translate these strings -->
<string-array name="fingerprint_error_vendor">

View File

@@ -2488,6 +2488,7 @@
<java-symbol type="string" name="fingerprint_error_lockout" />
<java-symbol type="string" name="fingerprint_error_lockout_permanent" />
<java-symbol type="string" name="fingerprint_name_template" />
<java-symbol type="string" name="fingerprint_dialog_default_subtitle" />
<java-symbol type="string" name="fingerprint_authenticated" />
<java-symbol type="string" name="fingerprint_error_no_fingerprints" />
<java-symbol type="string" name="fingerprint_error_hw_not_present" />

View File

@@ -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;

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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";
}

View File

@@ -149,9 +149,10 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
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<ActivityManager.RunningTaskInfo> tasks =
mActivityTaskManager.getTasks(1);
if (tasks == null || tasks.isEmpty()) {
@@ -166,7 +167,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
final String topPackage = topActivity.getPackageName();
if (!topPackage.contentEquals(getOwnerString())) {
Slog.e(TAG, "Background authentication detected, top: " + topPackage
+ ", client: " + this);
+ ", client: " + getOwnerString());
isBackgroundAuth = true;
}
}

View File

@@ -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);

View File

@@ -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<Fingerprint> {
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;
}
}
}

View File

@@ -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
}

View File

@@ -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
}