Merge "Ensure authentication lifecycle ends for callers" into sc-dev am: a0df5a941b
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15434472 Change-Id: I3ac9db0763ec34ab2d9100cdc565d636861a7f75
This commit is contained in:
@@ -180,7 +180,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
||||
+ ", isBP: " + isBiometricPrompt()
|
||||
+ ", listener: " + listener
|
||||
+ ", requireConfirmation: " + mRequireConfirmation
|
||||
+ ", user: " + getTargetUserId());
|
||||
+ ", user: " + getTargetUserId()
|
||||
+ ", clientMonitor: " + toString());
|
||||
|
||||
final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
|
||||
if (isCryptoOperation()) {
|
||||
@@ -304,6 +305,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
||||
public void handleLifecycleAfterAuth() {
|
||||
AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAuthenticationCanceled() {
|
||||
sendCancelOnly(listener);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Allow system-defined limit of number of attempts before giving up
|
||||
@@ -338,10 +344,30 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
||||
public void handleLifecycleAfterAuth() {
|
||||
AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendAuthenticationCanceled() {
|
||||
sendCancelOnly(listener);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void sendCancelOnly(@Nullable ClientMonitorCallbackConverter listener) {
|
||||
if (listener == null) {
|
||||
Slog.e(TAG, "Unable to sendAuthenticationCanceled, listener null");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
listener.onError(getSensorId(),
|
||||
getCookie(),
|
||||
BiometricConstants.BIOMETRIC_ERROR_CANCELED,
|
||||
0 /* vendorCode */);
|
||||
} catch (RemoteException e) {
|
||||
Slog.e(TAG, "Remote exception", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAcquired(int acquiredInfo, int vendorCode) {
|
||||
super.onAcquired(acquiredInfo, vendorCode);
|
||||
|
||||
@@ -73,6 +73,11 @@ public class CoexCoordinator {
|
||||
* from scheduler if auth was successful).
|
||||
*/
|
||||
void handleLifecycleAfterAuth();
|
||||
|
||||
/**
|
||||
* Requests the owner to notify the caller that authentication was canceled.
|
||||
*/
|
||||
void sendAuthenticationCanceled();
|
||||
}
|
||||
|
||||
private static CoexCoordinator sInstance;
|
||||
@@ -356,7 +361,11 @@ public class CoexCoordinator {
|
||||
private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) {
|
||||
for (SuccessfulAuth auth : mSuccessfulAuths) {
|
||||
if (currentTimeMillis - auth.mAuthTimestamp >= SUCCESSFUL_AUTH_VALID_DURATION_MS) {
|
||||
Slog.d(TAG, "Removing stale auth: " + auth);
|
||||
// TODO(b/193089985): This removes the auth but does not notify the client with
|
||||
// an appropriate lifecycle event (such as ERROR_CANCELED), and violates the
|
||||
// API contract. However, this might be OK for now since the validity duration
|
||||
// is way longer than the time it takes to auth with fingerprint.
|
||||
Slog.e(TAG, "Removing stale auth: " + auth);
|
||||
mSuccessfulAuths.remove(auth);
|
||||
} else if (auth.mSensorType == SENSOR_TYPE_FACE) {
|
||||
mSuccessfulAuths.remove(auth);
|
||||
@@ -367,9 +376,13 @@ public class CoexCoordinator {
|
||||
}
|
||||
|
||||
private void removeAndFinishAllFaceFromQueue() {
|
||||
// Note that these auth are all successful, but have never notified the client (e.g.
|
||||
// keyguard). To comply with the authentication lifecycle, we must notify the client that
|
||||
// auth is "done". The safest thing to do is to send ERROR_CANCELED.
|
||||
for (SuccessfulAuth auth : mSuccessfulAuths) {
|
||||
if (auth.mSensorType == SENSOR_TYPE_FACE) {
|
||||
Slog.d(TAG, "Removing from queue and finishing: " + auth);
|
||||
Slog.d(TAG, "Removing from queue, canceling, and finishing: " + auth);
|
||||
auth.mCallback.sendAuthenticationCanceled();
|
||||
auth.mCallback.handleLifecycleAfterAuth();
|
||||
mSuccessfulAuths.remove(auth);
|
||||
}
|
||||
|
||||
@@ -255,13 +255,16 @@ public class CoexCoordinatorTest {
|
||||
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
|
||||
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
|
||||
|
||||
// For easier reading
|
||||
final CoexCoordinator.Callback faceCallback = mCallback;
|
||||
|
||||
mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
|
||||
mCallback);
|
||||
verify(mCallback, never()).sendHapticFeedback();
|
||||
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
|
||||
faceCallback);
|
||||
verify(faceCallback, never()).sendHapticFeedback();
|
||||
verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
|
||||
// CoexCoordinator requests the system to hold onto this AuthenticationClient until
|
||||
// UDFPS result is known
|
||||
verify(mCallback, never()).handleLifecycleAfterAuth();
|
||||
verify(faceCallback, never()).handleLifecycleAfterAuth();
|
||||
|
||||
// Reset the mock
|
||||
CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
|
||||
@@ -274,6 +277,8 @@ public class CoexCoordinatorTest {
|
||||
verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
|
||||
verify(udfpsCallback).handleLifecycleAfterAuth();
|
||||
|
||||
verify(faceCallback).sendAuthenticationCanceled();
|
||||
|
||||
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
||||
} else {
|
||||
mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient,
|
||||
@@ -281,16 +286,16 @@ public class CoexCoordinatorTest {
|
||||
if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
|
||||
verify(udfpsCallback, never()).sendHapticFeedback();
|
||||
|
||||
verify(mCallback).sendHapticFeedback();
|
||||
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
|
||||
verify(mCallback).handleLifecycleAfterAuth();
|
||||
verify(faceCallback).sendHapticFeedback();
|
||||
verify(faceCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
|
||||
verify(faceCallback).handleLifecycleAfterAuth();
|
||||
|
||||
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
||||
} else {
|
||||
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
||||
|
||||
verify(mCallback, never()).sendHapticFeedback();
|
||||
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
|
||||
verify(faceCallback, never()).sendHapticFeedback();
|
||||
verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
|
||||
|
||||
verify(udfpsCallback).sendHapticFeedback();
|
||||
verify(udfpsCallback)
|
||||
|
||||
Reference in New Issue
Block a user