Add BiometricPrompt.Builder#setAllowBackgroundAuthentication TestApi

By default, auth via BiometricPrompt is not allowed unless the
caller is foreground. However, for cases like CTS, which may request
auth from the test itself (and not a test activity, which has no
way of getting access to protected TestApis), auth is requested
from background.

This would also allow us to easily add regression tests for the
security bug b/159249069

Bug: 163058911
Test: atest CtsBiometricsTestCases
Test: atest com.android.server.biometrics
Change-Id: I74bfdcd7989aa9256d1bf10eefae354983b42e6c
This commit is contained in:
Kevin Chyn
2021-03-18 12:31:38 -07:00
parent f8e775a357
commit 8115bfc791
26 changed files with 97 additions and 40 deletions

View File

@@ -1028,9 +1028,11 @@ package android.hardware.biometrics {
public class BiometricPrompt {
method @NonNull public java.util.List<java.lang.Integer> getAllowedSensorIds();
method public boolean isAllowBackgroundAuthentication();
}
public static class BiometricPrompt.Builder {
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowBackgroundAuthentication(boolean);
method @NonNull @RequiresPermission(anyOf={android.Manifest.permission.TEST_BIOMETRIC, "android.permission.USE_BIOMETRIC_INTERNAL"}) public android.hardware.biometrics.BiometricPrompt.Builder setAllowedSensorIds(@NonNull java.util.List<java.lang.Integer>);
}

View File

@@ -367,6 +367,20 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
return this;
}
/**
* @param allow If true, allows authentication when the calling package is not in the
* foreground. This is set to false by default.
* @return This builder
* @hide
*/
@TestApi
@NonNull
@RequiresPermission(anyOf = {TEST_BIOMETRIC, USE_BIOMETRIC_INTERNAL})
public Builder setAllowBackgroundAuthentication(boolean allow) {
mPromptInfo.setAllowBackgroundAuthentication(allow);
return this;
}
/**
* If set check the Device Policy Manager for disabled biometrics.
*
@@ -619,6 +633,15 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
return mPromptInfo.getAllowedSensorIds();
}
/**
* @return The value set by {@link Builder#setAllowBackgroundAuthentication(boolean)}
* @hide
*/
@TestApi
public boolean isAllowBackgroundAuthentication() {
return mPromptInfo.isAllowBackgroundAuthentication();
}
/**
* A wrapper class for the cryptographic operations supported by BiometricPrompt.
*

View File

@@ -48,7 +48,7 @@ interface IBiometricAuthenticator {
// startPreparedClient().
void prepareForAuthentication(boolean requireConfirmation, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie);
int cookie, boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
void startPreparedClient(int cookie);

View File

@@ -44,6 +44,7 @@ public class PromptInfo implements Parcelable {
private boolean mDisallowBiometricsIfPolicyExists;
private boolean mReceiveSystemEvents;
@NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
private boolean mAllowBackgroundAuthentication;
public PromptInfo() {
@@ -64,6 +65,7 @@ public class PromptInfo implements Parcelable {
mDisallowBiometricsIfPolicyExists = in.readBoolean();
mReceiveSystemEvents = in.readBoolean();
mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
mAllowBackgroundAuthentication = in.readBoolean();
}
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -99,11 +101,14 @@ public class PromptInfo implements Parcelable {
dest.writeBoolean(mDisallowBiometricsIfPolicyExists);
dest.writeBoolean(mReceiveSystemEvents);
dest.writeList(mAllowedSensorIds);
dest.writeBoolean(mAllowBackgroundAuthentication);
}
public boolean containsTestConfigurations() {
if (!mAllowedSensorIds.isEmpty()) {
return true;
} else if (mAllowBackgroundAuthentication) {
return true;
}
return false;
}
@@ -183,6 +188,10 @@ public class PromptInfo implements Parcelable {
mAllowedSensorIds = sensorIds;
}
public void setAllowBackgroundAuthentication(boolean allow) {
mAllowBackgroundAuthentication = allow;
}
// Getters
public CharSequence getTitle() {
@@ -248,4 +257,8 @@ public class PromptInfo implements Parcelable {
public List<Integer> getAllowedSensorIds() {
return mAllowedSensorIds;
}
public boolean isAllowBackgroundAuthentication() {
return mAllowBackgroundAuthentication;
}
}

View File

@@ -59,7 +59,7 @@ interface IFaceService {
// startPreparedClient().
void prepareForAuthentication(int sensorId, boolean requireConfirmation, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie);
int cookie, boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
void startPreparedClient(int sensorId, int cookie);

View File

@@ -62,7 +62,8 @@ interface IFingerprintService {
// by BiometricService. To start authentication after the clients are ready, use
// startPreparedClient().
void prepareForAuthentication(int sensorId, IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie);
IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
boolean allowBackgroundAuthentication);
// Starts authentication with the previously prepared client.
void startPreparedClient(int sensorId, int cookie);

View File

@@ -195,7 +195,8 @@ public final class AuthSession implements IBinder.DeathRecipient {
final int cookie = mRandom.nextInt(Integer.MAX_VALUE - 1) + 1;
final boolean requireConfirmation = isConfirmationRequired(sensor);
sensor.goToStateWaitingForCookie(requireConfirmation, mToken, mOperationId,
mUserId, mSensorReceiver, mOpPackageName, cookie);
mUserId, mSensorReceiver, mOpPackageName, cookie,
mPromptInfo.isAllowBackgroundAuthentication());
}
}

View File

@@ -103,11 +103,12 @@ public abstract class BiometricSensor {
void goToStateWaitingForCookie(boolean requireConfirmation, IBinder token, long sessionId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie)
int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
mCookie = cookie;
impl.prepareForAuthentication(requireConfirmation, token,
sessionId, userId, sensorReceiver, opPackageName, mCookie);
sessionId, userId, sensorReceiver, opPackageName, mCookie,
allowBackgroundAuthentication);
mSensorState = STATE_WAITING_FOR_COOKIE;
}

View File

@@ -55,7 +55,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
@Nullable private final TaskStackListener mTaskStackListener;
private final LockoutTracker mLockoutTracker;
private final boolean mIsRestricted;
private final boolean mIsKeyguard;
private final boolean mAllowBackgroundAuthentication;
protected final long mOperationId;
@@ -68,7 +68,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
int targetUserId, long operationId, boolean restricted, @NonNull String owner,
int cookie, boolean requireConfirmation, int sensorId, boolean isStrongBiometric,
int statsModality, int statsClient, @Nullable TaskStackListener taskStackListener,
@NonNull LockoutTracker lockoutTracker, boolean isKeyguard) {
@NonNull LockoutTracker lockoutTracker, boolean allowBackgroundAuthentication) {
super(context, lazyDaemon, token, listener, targetUserId, owner, cookie, sensorId,
statsModality, BiometricsProtoEnums.ACTION_AUTHENTICATE, statsClient);
mIsStrongBiometric = isStrongBiometric;
@@ -79,7 +79,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
mTaskStackListener = taskStackListener;
mLockoutTracker = lockoutTracker;
mIsRestricted = restricted;
mIsKeyguard = isKeyguard;
mAllowBackgroundAuthentication = allowBackgroundAuthentication;
}
public @LockoutTracker.LockoutMode int handleFailedAttempt(int userId) {
@@ -120,7 +120,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
}
public boolean isKeyguard() {
return mIsKeyguard;
return Utils.isKeyguard(getContext(), getOwnerString());
}
@Override
@@ -152,9 +152,15 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
pm.incrementAuthForUser(getTargetUserId(), authenticated);
}
if (mAllowBackgroundAuthentication) {
Slog.w(TAG, "Allowing background authentication,"
+ " this is allowed only for platform or test invocations");
}
// Ensure authentication only succeeds if the client activity is on top.
boolean isBackgroundAuth = false;
if (authenticated && !Utils.isKeyguard(getContext(), getOwnerString())
if (!mAllowBackgroundAuthentication && authenticated
&& !Utils.isKeyguard(getContext(), getOwnerString())
&& !Utils.isSystem(getContext(), getOwnerString())) {
final List<ActivityManager.RunningTaskInfo> tasks =
mActivityTaskManager.getTasks(1);

View File

@@ -61,10 +61,10 @@ public final class FaceAuthenticator extends IBiometricAuthenticator.Stub {
@Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, int cookie)
String opPackageName, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
mFaceService.prepareForAuthentication(mSensorId, requireConfirmation, token, operationId,
userId, sensorReceiver, opPackageName, cookie);
userId, sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication);
}
@Override

View File

@@ -306,7 +306,8 @@ public class FaceService extends SystemService implements BiometricServiceCallba
@Override // Binder call
public void prepareForAuthentication(int sensorId, boolean requireConfirmation,
IBinder token, long operationId, int userId,
IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie) {
IBiometricSensorReceiver sensorReceiver, String opPackageName, int cookie,
boolean allowBackgroundAuthentication) {
Utils.checkPermission(getContext(), USE_BIOMETRIC_INTERNAL);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -318,7 +319,7 @@ public class FaceService extends SystemService implements BiometricServiceCallba
final boolean restricted = true; // BiometricPrompt is always restricted
provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */);
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication);
}
@Override // Binder call

View File

@@ -103,7 +103,8 @@ public interface ServiceProvider {
void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient, boolean isKeyguard);
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication);
void cancelAuthentication(int sensorId, @NonNull IBinder token);

View File

@@ -69,11 +69,11 @@ class FaceAuthenticationClient extends AuthenticationClient<ISession> implements
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
boolean isStrongBiometric, int statsClient, @NonNull UsageStats usageStats,
@NonNull LockoutCache lockoutCache, boolean isKeyguard) {
@NonNull LockoutCache lockoutCache, boolean allowBackgroundAuthentication) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
lockoutCache, isKeyguard);
lockoutCache, allowBackgroundAuthentication);
mUsageStats = usageStats;
mLockoutCache = lockoutCache;
mNotificationManager = context.getSystemService(NotificationManager.class);

View File

@@ -433,7 +433,7 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
final IFace daemon = getHalInstance();
if (daemon == null) {
@@ -454,7 +454,8 @@ public class FaceProvider implements IBinder.DeathRecipient, ServiceProvider {
mContext, mSensors.get(sensorId).getLazySession(), token, callback, userId,
operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
mUsageStats, mSensors.get(sensorId).getLockoutCache(), isKeyguard);
mUsageStats, mSensors.get(sensorId).getLockoutCache(),
allowBackgroundAuthentication);
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when scheduling authenticate", e);

View File

@@ -635,7 +635,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter receiver,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -643,7 +643,7 @@ public class Face10 implements IHwBinder.DeathRecipient, ServiceProvider {
final FaceAuthenticationClient client = new FaceAuthenticationClient(mContext,
mLazyDaemon, token, receiver, userId, operationId, restricted, opPackageName,
cookie, false /* requireConfirmation */, mSensorId, isStrongBiometric,
statsClient, mLockoutTracker, mUsageStats, isKeyguard);
statsClient, mLockoutTracker, mUsageStats, allowBackgroundAuthentication);
mScheduler.scheduleClientMonitor(client);
});
}

View File

@@ -62,11 +62,11 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
@NonNull ClientMonitorCallbackConverter listener, int targetUserId, long operationId,
boolean restricted, String owner, int cookie, boolean requireConfirmation, int sensorId,
boolean isStrongBiometric, int statsClient, @NonNull LockoutTracker lockoutTracker,
@NonNull UsageStats usageStats, boolean isKeyguard) {
@NonNull UsageStats usageStats, boolean allowBackgroundAuthentication) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FACE, statsClient, null /* taskStackListener */,
lockoutTracker, isKeyguard);
lockoutTracker, allowBackgroundAuthentication);
mUsageStats = usageStats;
final Resources resources = getContext().getResources();

View File

@@ -62,10 +62,10 @@ public final class FingerprintAuthenticator extends IBiometricAuthenticator.Stub
@Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long operationId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, int cookie)
String opPackageName, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
mFingerprintService.prepareForAuthentication(mSensorId, token, operationId, userId,
sensorReceiver, opPackageName, cookie);
sensorReceiver, opPackageName, cookie, allowBackgroundAuthentication);
}
@Override

View File

@@ -403,7 +403,7 @@ public class FingerprintService extends SystemService implements BiometricServic
@Override // Binder call
public void prepareForAuthentication(int sensorId, IBinder token, long operationId,
int userId, IBiometricSensorReceiver sensorReceiver, String opPackageName,
int cookie) {
int cookie, boolean allowBackgroundAuthentication) {
Utils.checkPermission(getContext(), MANAGE_BIOMETRIC);
final ServiceProvider provider = getProviderForSensor(sensorId);
@@ -415,7 +415,7 @@ public class FingerprintService extends SystemService implements BiometricServic
final boolean restricted = true; // BiometricPrompt is always restricted
provider.scheduleAuthenticate(sensorId, token, operationId, userId, cookie,
new ClientMonitorCallbackConverter(sensorReceiver), opPackageName, restricted,
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, false /* isKeyguard */);
BiometricsProtoEnums.CLIENT_BIOMETRIC_PROMPT, allowBackgroundAuthentication);
}
@Override // Binder call

View File

@@ -96,7 +96,8 @@ public interface ServiceProvider {
void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId, int userId,
int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient, boolean isKeyguard);
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean allowBackgroundAuthentication);
void startPreparedClient(int sensorId, int cookie);

View File

@@ -59,11 +59,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<ISession> imp
boolean restricted, @NonNull String owner, int cookie, boolean requireConfirmation,
int sensorId, boolean isStrongBiometric, int statsClient,
@Nullable TaskStackListener taskStackListener, @NonNull LockoutCache lockoutCache,
@Nullable IUdfpsOverlayController udfpsOverlayController, boolean isKeyguard) {
@Nullable IUdfpsOverlayController udfpsOverlayController,
boolean allowBackgroundAuthentication) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted, owner,
cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutCache, isKeyguard);
lockoutCache, allowBackgroundAuthentication);
mLockoutCache = lockoutCache;
mUdfpsOverlayController = udfpsOverlayController;
}

View File

@@ -457,7 +457,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter callback,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
final IFingerprint daemon = getHalInstance();
if (daemon == null) {
@@ -479,7 +479,7 @@ public class FingerprintProvider implements IBinder.DeathRecipient, ServiceProvi
operationId, restricted, opPackageName, cookie,
false /* requireConfirmation */, sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mSensors.get(sensorId).getLockoutCache(),
mUdfpsOverlayController, isKeyguard);
mUdfpsOverlayController, allowBackgroundAuthentication);
mSensors.get(sensorId).getScheduler().scheduleClientMonitor(client);
} catch (RemoteException e) {
Slog.e(getTag(), "Remote exception when scheduling authenticate", e);

View File

@@ -609,7 +609,7 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
public void scheduleAuthenticate(int sensorId, @NonNull IBinder token, long operationId,
int userId, int cookie, @NonNull ClientMonitorCallbackConverter listener,
@NonNull String opPackageName, boolean restricted, int statsClient,
boolean isKeyguard) {
boolean allowBackgroundAuthentication) {
mHandler.post(() -> {
scheduleUpdateActiveUserWithoutHandler(userId);
@@ -618,7 +618,8 @@ public class Fingerprint21 implements IHwBinder.DeathRecipient, ServiceProvider
mContext, mLazyDaemon, token, listener, userId, operationId, restricted,
opPackageName, cookie, false /* requireConfirmation */,
mSensorProperties.sensorId, isStrongBiometric, statsClient,
mTaskStackListener, mLockoutTracker, mUdfpsOverlayController, isKeyguard);
mTaskStackListener, mLockoutTracker, mUdfpsOverlayController,
allowBackgroundAuthentication);
mScheduler.scheduleClientMonitor(client);
});
}

View File

@@ -59,11 +59,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
int sensorId, boolean isStrongBiometric, int statsClient,
@NonNull TaskStackListener taskStackListener,
@NonNull LockoutFrameworkImpl lockoutTracker,
@Nullable IUdfpsOverlayController udfpsOverlayController, boolean isKeyguard) {
@Nullable IUdfpsOverlayController udfpsOverlayController,
boolean allowBackgroundAuthentication) {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, isStrongBiometric,
BiometricsProtoEnums.MODALITY_FINGERPRINT, statsClient, taskStackListener,
lockoutTracker, isKeyguard);
lockoutTracker, allowBackgroundAuthentication);
mLockoutFrameworkImpl = lockoutTracker;
mUdfpsOverlayController = udfpsOverlayController;
}

View File

@@ -59,7 +59,8 @@ public final class IrisAuthenticator extends IBiometricAuthenticator.Stub {
@Override
public void prepareForAuthentication(boolean requireConfirmation, IBinder token,
long sessionId, int userId, IBiometricSensorReceiver sensorReceiver,
String opPackageName, int cookie) throws RemoteException {
String opPackageName, int cookie, boolean allowBackgroundAuthentication)
throws RemoteException {
}
@Override

View File

@@ -150,7 +150,8 @@ public class AuthSessionTest {
eq(userId),
eq(mSensorReceiver),
eq(TEST_PACKAGE),
eq(sensor.getCookie()));
eq(sensor.getCookie()),
anyBoolean() /* allowBackgroundAuthentication */);
}
final int cookie1 = session.mPreAuthInfo.eligibleSensors.get(0).getCookie();

View File

@@ -456,7 +456,8 @@ public class BiometricServiceTest {
anyInt() /* userId */,
any(IBiometricSensorReceiver.class),
anyString() /* opPackageName */,
cookieCaptor.capture() /* cookie */);
cookieCaptor.capture() /* cookie */,
anyBoolean() /* allowBackgroundAuthentication */);
// onReadyForAuthentication, mCurrentAuthSession state OK
mBiometricService.mImpl.onReadyForAuthentication(cookieCaptor.getValue());