Merge "Ensure authentication lifecycle ends for callers" into sc-dev
This commit is contained in:
@@ -180,7 +180,8 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
|||||||
+ ", isBP: " + isBiometricPrompt()
|
+ ", isBP: " + isBiometricPrompt()
|
||||||
+ ", listener: " + listener
|
+ ", listener: " + listener
|
||||||
+ ", requireConfirmation: " + mRequireConfirmation
|
+ ", requireConfirmation: " + mRequireConfirmation
|
||||||
+ ", user: " + getTargetUserId());
|
+ ", user: " + getTargetUserId()
|
||||||
|
+ ", clientMonitor: " + toString());
|
||||||
|
|
||||||
final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
|
final PerformanceTracker pm = PerformanceTracker.getInstanceForSensorId(getSensorId());
|
||||||
if (isCryptoOperation()) {
|
if (isCryptoOperation()) {
|
||||||
@@ -304,6 +305,11 @@ public abstract class AuthenticationClient<T> extends AcquisitionClient<T>
|
|||||||
public void handleLifecycleAfterAuth() {
|
public void handleLifecycleAfterAuth() {
|
||||||
AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
|
AuthenticationClient.this.handleLifecycleAfterAuth(true /* authenticated */);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void sendAuthenticationCanceled() {
|
||||||
|
sendCancelOnly(listener);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// Allow system-defined limit of number of attempts before giving up
|
// 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() {
|
public void handleLifecycleAfterAuth() {
|
||||||
AuthenticationClient.this.handleLifecycleAfterAuth(false /* authenticated */);
|
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
|
@Override
|
||||||
public void onAcquired(int acquiredInfo, int vendorCode) {
|
public void onAcquired(int acquiredInfo, int vendorCode) {
|
||||||
super.onAcquired(acquiredInfo, vendorCode);
|
super.onAcquired(acquiredInfo, vendorCode);
|
||||||
|
|||||||
@@ -73,6 +73,11 @@ public class CoexCoordinator {
|
|||||||
* from scheduler if auth was successful).
|
* from scheduler if auth was successful).
|
||||||
*/
|
*/
|
||||||
void handleLifecycleAfterAuth();
|
void handleLifecycleAfterAuth();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Requests the owner to notify the caller that authentication was canceled.
|
||||||
|
*/
|
||||||
|
void sendAuthenticationCanceled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static CoexCoordinator sInstance;
|
private static CoexCoordinator sInstance;
|
||||||
@@ -356,7 +361,11 @@ public class CoexCoordinator {
|
|||||||
private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) {
|
private SuccessfulAuth popSuccessfulFaceAuthIfExists(long currentTimeMillis) {
|
||||||
for (SuccessfulAuth auth : mSuccessfulAuths) {
|
for (SuccessfulAuth auth : mSuccessfulAuths) {
|
||||||
if (currentTimeMillis - auth.mAuthTimestamp >= SUCCESSFUL_AUTH_VALID_DURATION_MS) {
|
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);
|
mSuccessfulAuths.remove(auth);
|
||||||
} else if (auth.mSensorType == SENSOR_TYPE_FACE) {
|
} else if (auth.mSensorType == SENSOR_TYPE_FACE) {
|
||||||
mSuccessfulAuths.remove(auth);
|
mSuccessfulAuths.remove(auth);
|
||||||
@@ -367,9 +376,13 @@ public class CoexCoordinator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void removeAndFinishAllFaceFromQueue() {
|
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) {
|
for (SuccessfulAuth auth : mSuccessfulAuths) {
|
||||||
if (auth.mSensorType == SENSOR_TYPE_FACE) {
|
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();
|
auth.mCallback.handleLifecycleAfterAuth();
|
||||||
mSuccessfulAuths.remove(auth);
|
mSuccessfulAuths.remove(auth);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -255,13 +255,16 @@ public class CoexCoordinatorTest {
|
|||||||
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
|
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_FACE, faceClient);
|
||||||
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
|
mCoexCoordinator.addAuthenticationClient(SENSOR_TYPE_UDFPS, udfpsClient);
|
||||||
|
|
||||||
|
// For easier reading
|
||||||
|
final CoexCoordinator.Callback faceCallback = mCallback;
|
||||||
|
|
||||||
mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
|
mCoexCoordinator.onAuthenticationSucceeded(0 /* currentTimeMillis */, faceClient,
|
||||||
mCallback);
|
faceCallback);
|
||||||
verify(mCallback, never()).sendHapticFeedback();
|
verify(faceCallback, never()).sendHapticFeedback();
|
||||||
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
|
verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
|
||||||
// CoexCoordinator requests the system to hold onto this AuthenticationClient until
|
// CoexCoordinator requests the system to hold onto this AuthenticationClient until
|
||||||
// UDFPS result is known
|
// UDFPS result is known
|
||||||
verify(mCallback, never()).handleLifecycleAfterAuth();
|
verify(faceCallback, never()).handleLifecycleAfterAuth();
|
||||||
|
|
||||||
// Reset the mock
|
// Reset the mock
|
||||||
CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
|
CoexCoordinator.Callback udfpsCallback = mock(CoexCoordinator.Callback.class);
|
||||||
@@ -274,6 +277,8 @@ public class CoexCoordinatorTest {
|
|||||||
verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
|
verify(udfpsCallback).sendAuthenticationResult(true /* addAuthTokenIfStrong */);
|
||||||
verify(udfpsCallback).handleLifecycleAfterAuth();
|
verify(udfpsCallback).handleLifecycleAfterAuth();
|
||||||
|
|
||||||
|
verify(faceCallback).sendAuthenticationCanceled();
|
||||||
|
|
||||||
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
||||||
} else {
|
} else {
|
||||||
mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient,
|
mCoexCoordinator.onAuthenticationRejected(udfpsRejectedAfterMs, udfpsClient,
|
||||||
@@ -281,16 +286,16 @@ public class CoexCoordinatorTest {
|
|||||||
if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
|
if (udfpsRejectedAfterMs <= CoexCoordinator.SUCCESSFUL_AUTH_VALID_DURATION_MS) {
|
||||||
verify(udfpsCallback, never()).sendHapticFeedback();
|
verify(udfpsCallback, never()).sendHapticFeedback();
|
||||||
|
|
||||||
verify(mCallback).sendHapticFeedback();
|
verify(faceCallback).sendHapticFeedback();
|
||||||
verify(mCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
|
verify(faceCallback).sendAuthenticationResult(eq(true) /* addAuthTokenIfStrong */);
|
||||||
verify(mCallback).handleLifecycleAfterAuth();
|
verify(faceCallback).handleLifecycleAfterAuth();
|
||||||
|
|
||||||
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
||||||
} else {
|
} else {
|
||||||
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
assertTrue(mCoexCoordinator.mSuccessfulAuths.isEmpty());
|
||||||
|
|
||||||
verify(mCallback, never()).sendHapticFeedback();
|
verify(faceCallback, never()).sendHapticFeedback();
|
||||||
verify(mCallback, never()).sendAuthenticationResult(anyBoolean());
|
verify(faceCallback, never()).sendAuthenticationResult(anyBoolean());
|
||||||
|
|
||||||
verify(udfpsCallback).sendHapticFeedback();
|
verify(udfpsCallback).sendHapticFeedback();
|
||||||
verify(udfpsCallback)
|
verify(udfpsCallback)
|
||||||
|
|||||||
Reference in New Issue
Block a user