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:
committed by
Kevin Chyn
parent
7f2c016bdd
commit
45807b0b4a
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user