Merge "[DO NOT MERGE] Do not clear calling identify when using BiometricPrompt from FingerprintService." into sc-v2-dev

This commit is contained in:
Joe Bolinger
2022-04-22 19:17:10 +00:00
committed by Android (Google) Code Review
7 changed files with 143 additions and 70 deletions

View File

@@ -420,6 +420,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
return this; return this;
} }
/**
* Set if BiometricPrompt is being used by the legacy fingerprint manager API.
* @param sensorId sensor id
* @return This builder.
* @hide
*/
@NonNull
public Builder setIsForLegacyFingerprintManager(int sensorId) {
mPromptInfo.setIsForLegacyFingerprintManager(sensorId);
return this;
}
/** /**
* Creates a {@link BiometricPrompt}. * Creates a {@link BiometricPrompt}.
* *
@@ -861,28 +873,36 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
@NonNull @CallbackExecutor Executor executor, @NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback, @NonNull AuthenticationCallback callback,
int userId) { int userId) {
authenticateUserForOperation(cancel, executor, callback, userId, 0 /* operationId */); if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal");
}
if (executor == null) {
throw new IllegalArgumentException("Must supply an executor");
}
if (callback == null) {
throw new IllegalArgumentException("Must supply a callback");
}
authenticateInternal(0 /* operationId */, cancel, executor, callback, userId);
} }
/** /**
* Authenticates for the given user and keystore operation. * Authenticates for the given keystore operation.
* *
* @param cancel An object that can be used to cancel authentication * @param cancel An object that can be used to cancel authentication
* @param executor An executor to handle callback events * @param executor An executor to handle callback events
* @param callback An object to receive authentication events * @param callback An object to receive authentication events
* @param userId The user to authenticate
* @param operationId The keystore operation associated with authentication * @param operationId The keystore operation associated with authentication
* *
* @return A requestId that can be used to cancel this operation. * @return A requestId that can be used to cancel this operation.
* *
* @hide * @hide
*/ */
@RequiresPermission(USE_BIOMETRIC_INTERNAL) @RequiresPermission(USE_BIOMETRIC)
public long authenticateUserForOperation( public long authenticateForOperation(
@NonNull CancellationSignal cancel, @NonNull CancellationSignal cancel,
@NonNull @CallbackExecutor Executor executor, @NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback, @NonNull AuthenticationCallback callback,
int userId,
long operationId) { long operationId) {
if (cancel == null) { if (cancel == null) {
throw new IllegalArgumentException("Must supply a cancellation signal"); throw new IllegalArgumentException("Must supply a cancellation signal");
@@ -894,7 +914,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
throw new IllegalArgumentException("Must supply a callback"); throw new IllegalArgumentException("Must supply a callback");
} }
return authenticateInternal(operationId, cancel, executor, callback, userId); return authenticateInternal(operationId, cancel, executor, callback, mContext.getUserId());
} }
/** /**
@@ -1028,7 +1048,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
private void cancelAuthentication(long requestId) { private void cancelAuthentication(long requestId) {
if (mService != null) { if (mService != null) {
try { try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId); mService.cancelAuthentication(mToken, mContext.getPackageName(), requestId);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Unable to cancel authentication", e); Log.e(TAG, "Unable to cancel authentication", e);
} }
@@ -1087,7 +1107,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
} }
final long authId = mService.authenticate(mToken, operationId, userId, final long authId = mService.authenticate(mToken, operationId, userId,
mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo); mBiometricServiceReceiver, mContext.getPackageName(), promptInfo);
cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId)); cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
return authId; return authId;
} catch (RemoteException e) { } catch (RemoteException e) {

View File

@@ -19,7 +19,7 @@ package android.hardware.biometrics;
* ITestSession callback for FingerprintManager and BiometricManager. * ITestSession callback for FingerprintManager and BiometricManager.
* @hide * @hide
*/ */
interface ITestSessionCallback { oneway interface ITestSessionCallback {
void onCleanupStarted(int userId); void onCleanupStarted(int userId);
void onCleanupFinished(int userId); void onCleanupFinished(int userId);
} }

View File

@@ -46,6 +46,7 @@ public class PromptInfo implements Parcelable {
@NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>(); @NonNull private List<Integer> mAllowedSensorIds = new ArrayList<>();
private boolean mAllowBackgroundAuthentication; private boolean mAllowBackgroundAuthentication;
private boolean mIgnoreEnrollmentState; private boolean mIgnoreEnrollmentState;
private boolean mIsForLegacyFingerprintManager = false;
public PromptInfo() { public PromptInfo() {
@@ -68,6 +69,7 @@ public class PromptInfo implements Parcelable {
mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader()); mAllowedSensorIds = in.readArrayList(Integer.class.getClassLoader());
mAllowBackgroundAuthentication = in.readBoolean(); mAllowBackgroundAuthentication = in.readBoolean();
mIgnoreEnrollmentState = in.readBoolean(); mIgnoreEnrollmentState = in.readBoolean();
mIsForLegacyFingerprintManager = in.readBoolean();
} }
public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() { public static final Creator<PromptInfo> CREATOR = new Creator<PromptInfo>() {
@@ -105,10 +107,15 @@ public class PromptInfo implements Parcelable {
dest.writeList(mAllowedSensorIds); dest.writeList(mAllowedSensorIds);
dest.writeBoolean(mAllowBackgroundAuthentication); dest.writeBoolean(mAllowBackgroundAuthentication);
dest.writeBoolean(mIgnoreEnrollmentState); dest.writeBoolean(mIgnoreEnrollmentState);
dest.writeBoolean(mIsForLegacyFingerprintManager);
} }
public boolean containsTestConfigurations() { public boolean containsTestConfigurations() {
if (!mAllowedSensorIds.isEmpty()) { if (mIsForLegacyFingerprintManager
&& mAllowedSensorIds.size() == 1
&& !mAllowBackgroundAuthentication) {
return false;
} else if (!mAllowedSensorIds.isEmpty()) {
return true; return true;
} else if (mAllowBackgroundAuthentication) { } else if (mAllowBackgroundAuthentication) {
return true; return true;
@@ -188,7 +195,8 @@ public class PromptInfo implements Parcelable {
} }
public void setAllowedSensorIds(@NonNull List<Integer> sensorIds) { public void setAllowedSensorIds(@NonNull List<Integer> sensorIds) {
mAllowedSensorIds = sensorIds; mAllowedSensorIds.clear();
mAllowedSensorIds.addAll(sensorIds);
} }
public void setAllowBackgroundAuthentication(boolean allow) { public void setAllowBackgroundAuthentication(boolean allow) {
@@ -199,6 +207,12 @@ public class PromptInfo implements Parcelable {
mIgnoreEnrollmentState = ignoreEnrollmentState; mIgnoreEnrollmentState = ignoreEnrollmentState;
} }
public void setIsForLegacyFingerprintManager(int sensorId) {
mIsForLegacyFingerprintManager = true;
mAllowedSensorIds.clear();
mAllowedSensorIds.add(sensorId);
}
// Getters // Getters
public CharSequence getTitle() { public CharSequence getTitle() {
@@ -272,4 +286,8 @@ public class PromptInfo implements Parcelable {
public boolean isIgnoreEnrollmentState() { public boolean isIgnoreEnrollmentState() {
return mIgnoreEnrollmentState; return mIgnoreEnrollmentState;
} }
public boolean isForLegacyFingerprintManager() {
return mIsForLegacyFingerprintManager;
}
} }

View File

@@ -134,7 +134,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
private class BiometricTaskStackListener extends TaskStackListener { private class BiometricTaskStackListener extends TaskStackListener {
@Override @Override
public void onTaskStackChanged() { public void onTaskStackChanged() {
mHandler.post(AuthController.this::handleTaskStackChanged); mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground);
} }
} }
@@ -181,7 +181,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
} }
}; };
private void handleTaskStackChanged() { private void cancelIfOwnerIsNotInForeground() {
mExecution.assertIsMainThread(); mExecution.assertIsMainThread();
if (mCurrentDialog != null) { if (mCurrentDialog != null) {
try { try {
@@ -193,7 +193,7 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
final String topPackage = runningTasks.get(0).topActivity.getPackageName(); final String topPackage = runningTasks.get(0).topActivity.getPackageName();
if (!topPackage.contentEquals(clientPackage) if (!topPackage.contentEquals(clientPackage)
&& !Utils.isSystem(mContext, clientPackage)) { && !Utils.isSystem(mContext, clientPackage)) {
Log.w(TAG, "Evicting client due to: " + topPackage); Log.e(TAG, "Evicting client due to: " + topPackage);
mCurrentDialog.dismissWithoutCallback(true /* animate */); mCurrentDialog.dismissWithoutCallback(true /* animate */);
mCurrentDialog = null; mCurrentDialog = null;
mOrientationListener.disable(); mOrientationListener.disable();
@@ -814,6 +814,10 @@ public class AuthController extends SystemUI implements CommandQueue.Callbacks,
mCurrentDialog = newDialog; mCurrentDialog = newDialog;
mCurrentDialog.show(mWindowManager, savedState); mCurrentDialog.show(mWindowManager, savedState);
mOrientationListener.enable(); mOrientationListener.enable();
if (!promptInfo.isAllowBackgroundAuthentication()) {
mHandler.post(this::cancelIfOwnerIsNotInForeground);
}
} }
private void onDialogDismissed(@DismissedReason int reason) { private void onDialogDismissed(@DismissedReason int reason) {

View File

@@ -554,16 +554,26 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController.mLastBiometricPromptInfo.getAuthenticators()); mAuthController.mLastBiometricPromptInfo.getAuthenticators());
} }
@Test
public void testClientNotified_whenTaskStackChangesDuringShow() throws Exception {
switchTask("other_package");
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
mTestableLooper.processAllMessages();
assertNull(mAuthController.mCurrentDialog);
assertNull(mAuthController.mReceiver);
verify(mDialog1).dismissWithoutCallback(true /* animate */);
verify(mReceiver).onDialogDismissed(
eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL),
eq(null) /* credentialAttestation */);
}
@Test @Test
public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception { public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>(); switchTask("other_package");
ActivityManager.RunningTaskInfo taskInfo = mock(ActivityManager.RunningTaskInfo.class);
taskInfo.topActivity = mock(ComponentName.class);
when(taskInfo.topActivity.getPackageName()).thenReturn("other_package");
tasks.add(taskInfo);
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
mAuthController.mTaskStackListener.onTaskStackChanged(); mAuthController.mTaskStackListener.onTaskStackChanged();
mTestableLooper.processAllMessages(); mTestableLooper.processAllMessages();
@@ -640,6 +650,16 @@ public class AuthControllerTest extends SysuiTestCase {
BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT); BIOMETRIC_MULTI_SENSOR_FACE_THEN_FINGERPRINT);
} }
private void switchTask(String packageName) {
final List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
final ActivityManager.RunningTaskInfo taskInfo =
mock(ActivityManager.RunningTaskInfo.class);
taskInfo.topActivity = mock(ComponentName.class);
when(taskInfo.topActivity.getPackageName()).thenReturn(packageName);
tasks.add(taskInfo);
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(tasks);
}
private PromptInfo createTestPromptInfo() { private PromptInfo createTestPromptInfo() {
PromptInfo promptInfo = new PromptInfo(); PromptInfo promptInfo = new PromptInfo();

View File

@@ -118,7 +118,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
mIsStrongBiometric = isStrongBiometric; mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId; mOperationId = operationId;
mRequireConfirmation = requireConfirmation; mRequireConfirmation = requireConfirmation;
mActivityTaskManager = ActivityTaskManager.getInstance(); mActivityTaskManager = getActivityTaskManager();
mBiometricManager = context.getSystemService(BiometricManager.class); mBiometricManager = context.getSystemService(BiometricManager.class);
mTaskStackListener = taskStackListener; mTaskStackListener = taskStackListener;
mLockoutTracker = lockoutTracker; mLockoutTracker = lockoutTracker;
@@ -146,6 +146,10 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
return mStartTimeMs; return mStartTimeMs;
} }
protected ActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getInstance();
}
@Override @Override
public void binderDied() { public void binderDied() {
final boolean clearListener = !isBiometricPrompt(); final boolean clearListener = !isBiometricPrompt();
@@ -322,45 +326,50 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
sendCancelOnly(listener); sendCancelOnly(listener);
} }
}); });
} else { } else { // not authenticated
// Allow system-defined limit of number of attempts before giving up if (isBackgroundAuth) {
final @LockoutTracker.LockoutMode int lockoutMode = Slog.e(TAG, "cancelling due to background auth");
handleFailedAttempt(getTargetUserId()); cancel();
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) { } else {
markAlreadyDone(); // Allow system-defined limit of number of attempts before giving up
final @LockoutTracker.LockoutMode int lockoutMode =
handleFailedAttempt(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
markAlreadyDone();
}
final CoexCoordinator coordinator = CoexCoordinator.getInstance();
coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode,
new CoexCoordinator.Callback() {
@Override
public void sendAuthenticationResult(boolean addAuthTokenIfStrong) {
if (listener != null) {
try {
listener.onAuthenticationFailed(getSensorId());
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify listener", e);
}
}
}
@Override
public void sendHapticFeedback() {
if (listener != null && mShouldVibrate) {
vibrateError();
}
}
@Override
public void handleLifecycleAfterAuth() {
AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
}
@Override
public void sendAuthenticationCanceled() {
sendCancelOnly(listener);
}
});
} }
final CoexCoordinator coordinator = CoexCoordinator.getInstance();
coordinator.onAuthenticationRejected(SystemClock.uptimeMillis(), this, lockoutMode,
new CoexCoordinator.Callback() {
@Override
public void sendAuthenticationResult(boolean addAuthTokenIfStrong) {
if (listener != null) {
try {
listener.onAuthenticationFailed(getSensorId());
} catch (RemoteException e) {
Slog.e(TAG, "Unable to notify listener", e);
}
}
}
@Override
public void sendHapticFeedback() {
if (listener != null && mShouldVibrate) {
vibrateError();
}
}
@Override
public void handleLifecycleAfterAuth() {
AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
}
@Override
public void sendAuthenticationCanceled() {
sendCancelOnly(listener);
}
});
} }
} }

View File

@@ -331,12 +331,12 @@ public class FingerprintService extends SystemService {
provider.second.getSensorProperties(sensorId); provider.second.getSensorProperties(sensorId);
if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName) if (!isKeyguard && !Utils.isSettings(getContext(), opPackageName)
&& sensorProps != null && sensorProps.isAnyUdfpsType()) { && sensorProps != null && sensorProps.isAnyUdfpsType()) {
identity = Binder.clearCallingIdentity();
try { try {
return authenticateWithPrompt(operationId, sensorProps, userId, receiver, return authenticateWithPrompt(operationId, sensorProps, userId, receiver,
ignoreEnrollmentState); opPackageName, ignoreEnrollmentState);
} finally { } catch (PackageManager.NameNotFoundException e) {
Binder.restoreCallingIdentity(identity); Slog.e(TAG, "Invalid package", e);
return -1;
} }
} }
return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId, return provider.second.scheduleAuthenticate(provider.first, token, operationId, userId,
@@ -349,12 +349,15 @@ public class FingerprintService extends SystemService {
@NonNull final FingerprintSensorPropertiesInternal props, @NonNull final FingerprintSensorPropertiesInternal props,
final int userId, final int userId,
final IFingerprintServiceReceiver receiver, final IFingerprintServiceReceiver receiver,
boolean ignoreEnrollmentState) { final String opPackageName,
boolean ignoreEnrollmentState) throws PackageManager.NameNotFoundException {
final Context context = getUiContext(); final Context context = getUiContext();
final Context promptContext = context.createPackageContextAsUser(
opPackageName, 0 /* flags */, UserHandle.getUserHandleForUid(userId));
final Executor executor = context.getMainExecutor(); final Executor executor = context.getMainExecutor();
final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(context) final BiometricPrompt biometricPrompt = new BiometricPrompt.Builder(promptContext)
.setTitle(context.getString(R.string.biometric_dialog_default_title)) .setTitle(context.getString(R.string.biometric_dialog_default_title))
.setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle)) .setSubtitle(context.getString(R.string.fingerprint_dialog_default_subtitle))
.setNegativeButton( .setNegativeButton(
@@ -368,8 +371,7 @@ public class FingerprintService extends SystemService {
Slog.e(TAG, "Remote exception in negative button onClick()", e); Slog.e(TAG, "Remote exception in negative button onClick()", e);
} }
}) })
.setAllowedSensorIds(new ArrayList<>( .setIsForLegacyFingerprintManager(props.sensorId)
Collections.singletonList(props.sensorId)))
.setIgnoreEnrollmentState(ignoreEnrollmentState) .setIgnoreEnrollmentState(ignoreEnrollmentState)
.build(); .build();
@@ -423,8 +425,8 @@ public class FingerprintService extends SystemService {
} }
}; };
return biometricPrompt.authenticateUserForOperation( return biometricPrompt.authenticateForOperation(
new CancellationSignal(), executor, promptCallback, userId, operationId); new CancellationSignal(), executor, promptCallback, operationId);
} }
@Override @Override