Do not clear calling identify when using BiometricPrompt from FingerprintService.

Bug: 214261879
Test: atest AuthControllerTest FaceAuthenticationClientTest FingerprintAuthenticationClientTest
Test: Manually verify with test apps in bug
Change-Id: I8ae9f2b8a970bf7e5d32121dc358f7d0f0d060b8
This commit is contained in:
Joe Bolinger
2022-03-18 23:56:35 +00:00
parent ee9ba8ff1b
commit 9b3c3a949b
9 changed files with 222 additions and 74 deletions

View File

@@ -421,6 +421,18 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
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}.
*
@@ -883,28 +895,36 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
@NonNull @CallbackExecutor Executor executor,
@NonNull AuthenticationCallback callback,
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 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
*
* @return A requestId that can be used to cancel this operation.
*
* @hide
*/
@RequiresPermission(USE_BIOMETRIC_INTERNAL)
public long authenticateUserForOperation(
@RequiresPermission(USE_BIOMETRIC)
public long authenticateForOperation(
@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");
@@ -916,7 +936,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
throw new IllegalArgumentException("Must supply a callback");
}
return authenticateInternal(operationId, cancel, executor, callback, userId);
return authenticateInternal(operationId, cancel, executor, callback, mContext.getUserId());
}
/**
@@ -1050,7 +1070,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
private void cancelAuthentication(long requestId) {
if (mService != null) {
try {
mService.cancelAuthentication(mToken, mContext.getOpPackageName(), requestId);
mService.cancelAuthentication(mToken, mContext.getPackageName(), requestId);
} catch (RemoteException e) {
Log.e(TAG, "Unable to cancel authentication", e);
}
@@ -1109,7 +1129,7 @@ public class BiometricPrompt implements BiometricAuthenticator, BiometricConstan
}
final long authId = mService.authenticate(mToken, operationId, userId,
mBiometricServiceReceiver, mContext.getOpPackageName(), promptInfo);
mBiometricServiceReceiver, mContext.getPackageName(), promptInfo);
cancel.setOnCancelListener(new OnAuthenticationCancelListener(authId));
return authId;
} catch (RemoteException e) {

View File

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

View File

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

View File

@@ -144,7 +144,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
public void onTaskStackChanged() {
mHandler.post(AuthController.this::handleTaskStackChanged);
mHandler.post(AuthController.this::cancelIfOwnerIsNotInForeground);
}
};
@@ -195,7 +195,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
}
};
private void handleTaskStackChanged() {
private void cancelIfOwnerIsNotInForeground() {
mExecution.assertIsMainThread();
if (mCurrentDialog != null) {
try {
@@ -207,7 +207,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
final String topPackage = runningTasks.get(0).topActivity.getPackageName();
if (!topPackage.contentEquals(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 = null;
mOrientationListener.disable();
@@ -879,6 +879,10 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
mCurrentDialog = newDialog;
mCurrentDialog.show(mWindowManager, savedState);
mOrientationListener.enable();
if (!promptInfo.isAllowBackgroundAuthentication()) {
mHandler.post(this::cancelIfOwnerIsNotInForeground);
}
}
private void onDialogDismissed(@DismissedReason int reason) {

View File

@@ -593,16 +593,26 @@ public class AuthControllerTest extends SysuiTestCase {
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
public void testClientNotified_whenTaskStackChangesDuringAuthentication() throws Exception {
showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */);
List<ActivityManager.RunningTaskInfo> tasks = new ArrayList<>();
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);
switchTask("other_package");
mAuthController.mTaskStackListener.onTaskStackChanged();
mTestableLooper.processAllMessages();
@@ -720,6 +730,16 @@ public class AuthControllerTest extends SysuiTestCase {
BIOMETRIC_MULTI_SENSOR_FINGERPRINT_AND_FACE);
}
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() {
PromptInfo promptInfo = new PromptInfo();

View File

@@ -105,7 +105,7 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
mIsStrongBiometric = isStrongBiometric;
mOperationId = operationId;
mRequireConfirmation = requireConfirmation;
mActivityTaskManager = ActivityTaskManager.getInstance();
mActivityTaskManager = getActivityTaskManager();
mBiometricManager = context.getSystemService(BiometricManager.class);
mTaskStackListener = taskStackListener;
mLockoutTracker = lockoutTracker;
@@ -133,6 +133,10 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
return mStartTimeMs;
}
protected ActivityTaskManager getActivityTaskManager() {
return ActivityTaskManager.getInstance();
}
@Override
public void binderDied() {
final boolean clearListener = !isBiometricPrompt();
@@ -310,45 +314,50 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
sendCancelOnly(listener);
}
});
} else {
// Allow system-defined limit of number of attempts before giving up
final @LockoutTracker.LockoutMode int lockoutMode =
handleFailedAttempt(getTargetUserId());
if (lockoutMode != LockoutTracker.LOCKOUT_NONE) {
markAlreadyDone();
} else { // not authenticated
if (isBackgroundAuth) {
Slog.e(TAG, "cancelling due to background auth");
cancel();
} else {
// 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

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

View File

@@ -18,6 +18,7 @@ package com.android.server.biometrics.sensors.face.aidl;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
@@ -26,8 +27,13 @@ import static org.mockito.Mockito.same;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.face.ISession;
import android.hardware.face.Face;
import android.os.IBinder;
import android.os.RemoteException;
import android.platform.test.annotations.Presubmit;
@@ -53,6 +59,9 @@ import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
@Presubmit
@SmallTest
public class FaceAuthenticationClientTest {
@@ -82,6 +91,10 @@ public class FaceAuthenticationClientTest {
private ClientMonitorCallback mCallback;
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
private ICancellationSignal mCancellationSignal;
@Captor
private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@@ -116,6 +129,25 @@ public class FaceAuthenticationClientTest {
verify(mHal, never()).authenticate(anyLong());
}
@Test
public void cancelsAuthWhenNotInForeground() throws Exception {
final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
topTask.topActivity = new ComponentName("other", "thing");
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask));
when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
final FaceAuthenticationClient client = createClient();
client.start(mCallback);
client.onAuthenticated(new Face("friendly", 1 /* faceId */, 2 /* deviceId */),
true /* authenticated */, new ArrayList<>());
verify(mCancellationSignal).cancel();
}
private FaceAuthenticationClient createClient() throws RemoteException {
return createClient(2 /* version */);
}
private FaceAuthenticationClient createClient(int version) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
@@ -126,6 +158,11 @@ public class FaceAuthenticationClientTest {
false /* requireConfirmation */, 9 /* sensorId */,
mBiometricLogger, mBiometricContext, true /* isStrongBiometric */,
mUsageStats, mLockoutCache, false /* allowBackgroundAuthentication */,
false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */);
false /* isKeyguardBypassEnabled */, null /* sensorPrivacyManager */) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;
}
};
}
}

View File

@@ -31,7 +31,11 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.content.ComponentName;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.common.ICancellationSignal;
import android.hardware.biometrics.common.OperationContext;
import android.hardware.biometrics.fingerprint.ISession;
import android.hardware.biometrics.fingerprint.PointerContext;
@@ -66,6 +70,7 @@ import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
@Presubmit
@@ -111,6 +116,10 @@ public class FingerprintAuthenticationClientTest {
@Mock
private Sensor.HalSessionCallback mHalSessionCallback;
@Mock
private ActivityTaskManager mActivityTaskManager;
@Mock
private ICancellationSignal mCancellationSignal;
@Mock
private Probe mLuxProbe;
@Captor
private ArgumentCaptor<OperationContext> mOperationContextCaptor;
@@ -288,11 +297,36 @@ public class FingerprintAuthenticationClientTest {
verify(mSideFpsController).hide(anyInt());
}
@Test
public void cancelsAuthWhenNotInForeground() throws Exception {
final ActivityManager.RunningTaskInfo topTask = new ActivityManager.RunningTaskInfo();
topTask.topActivity = new ComponentName("other", "thing");
when(mActivityTaskManager.getTasks(anyInt())).thenReturn(List.of(topTask));
when(mHal.authenticateWithContext(anyLong(), any())).thenReturn(mCancellationSignal);
final FingerprintAuthenticationClient client = createClientWithoutBackgroundAuth();
client.start(mCallback);
client.onAuthenticated(new Fingerprint("friendly", 1 /* fingerId */, 2 /* deviceId */),
true /* authenticated */, new ArrayList<>());
verify(mCancellationSignal).cancel();
}
private FingerprintAuthenticationClient createClient() throws RemoteException {
return createClient(100);
return createClient(100 /* version */, true /* allowBackgroundAuthentication */);
}
private FingerprintAuthenticationClient createClientWithoutBackgroundAuth()
throws RemoteException {
return createClient(100 /* version */, false /* allowBackgroundAuthentication */);
}
private FingerprintAuthenticationClient createClient(int version) throws RemoteException {
return createClient(version, true /* allowBackgroundAuthentication */);
}
private FingerprintAuthenticationClient createClient(int version,
boolean allowBackgroundAuthentication) throws RemoteException {
when(mHal.getInterfaceVersion()).thenReturn(version);
final AidlSession aidl = new AidlSession(version, mHal, USER_ID, mHalSessionCallback);
@@ -302,7 +336,11 @@ public class FingerprintAuthenticationClientTest {
9 /* sensorId */, mBiometricLogger, mBiometricContext,
true /* isStrongBiometric */,
null /* taskStackListener */, mLockoutCache,
mUdfpsOverlayController, mSideFpsController,
true /* allowBackgroundAuthentication */, mSensorProps);
mUdfpsOverlayController, mSideFpsController, allowBackgroundAuthentication, mSensorProps) {
@Override
protected ActivityTaskManager getActivityTaskManager() {
return mActivityTaskManager;
}
};
}
}