diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java index bc1c5f4baceb7..e52fa13ea6ef8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java @@ -247,13 +247,13 @@ public class AuthContainerView extends LinearLayout break; case AuthBiometricView.Callback.ACTION_BUTTON_TRY_AGAIN: mFailedModalities.clear(); - mConfig.mCallback.onTryAgainPressed(); + mConfig.mCallback.onTryAgainPressed(getRequestId()); break; case AuthBiometricView.Callback.ACTION_ERROR: animateAway(AuthDialogCallback.DISMISSED_ERROR); break; case AuthBiometricView.Callback.ACTION_USE_DEVICE_CREDENTIAL: - mConfig.mCallback.onDeviceCredentialPressed(); + mConfig.mCallback.onDeviceCredentialPressed(getRequestId()); mHandler.postDelayed(() -> { addCredentialView(false /* animatePanel */, true /* animateContents */); }, mConfig.mSkipAnimation ? 0 : AuthDialog.ANIMATE_CREDENTIAL_START_DELAY_MS); @@ -371,7 +371,7 @@ public class AuthContainerView extends LinearLayout void sendEarlyUserCanceled() { mConfig.mCallback.onSystemEvent( - BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL); + BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL, getRequestId()); } @Override @@ -762,7 +762,8 @@ public class AuthContainerView extends LinearLayout private void sendPendingCallbackIfNotNull() { Log.d(TAG, "pendingCallback: " + mPendingCallbackReason); if (mPendingCallbackReason != null) { - mConfig.mCallback.onDismissed(mPendingCallbackReason, mCredentialAttestation); + mConfig.mCallback.onDismissed(mPendingCallbackReason, + mCredentialAttestation, getRequestId()); mPendingCallbackReason = null; } } @@ -792,7 +793,7 @@ public class AuthContainerView extends LinearLayout } mContainerState = STATE_SHOWING; if (mBiometricView != null) { - mConfig.mCallback.onDialogAnimatedIn(); + mConfig.mCallback.onDialogAnimatedIn(getRequestId()); mBiometricView.onDialogAnimatedIn(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java index a097c5e761493..47ff59cfc2815 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java @@ -340,11 +340,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onTryAgainPressed() { + public void onTryAgainPressed(long requestId) { if (mReceiver == null) { Log.e(TAG, "onTryAgainPressed: Receiver is null"); return; } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onTryAgainPressed"); + return; + } + try { mReceiver.onTryAgainPressed(); } catch (RemoteException e) { @@ -353,11 +359,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onDeviceCredentialPressed() { + public void onDeviceCredentialPressed(long requestId) { if (mReceiver == null) { Log.e(TAG, "onDeviceCredentialPressed: Receiver is null"); return; } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onDeviceCredentialPressed"); + return; + } + try { mReceiver.onDeviceCredentialPressed(); } catch (RemoteException e) { @@ -366,11 +378,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onSystemEvent(int event) { + public void onSystemEvent(int event, long requestId) { if (mReceiver == null) { Log.e(TAG, "onSystemEvent(" + event + "): Receiver is null"); return; } + + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onSystemEvent"); + return; + } + try { mReceiver.onSystemEvent(event); } catch (RemoteException e) { @@ -379,12 +397,17 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onDialogAnimatedIn() { + public void onDialogAnimatedIn(long requestId) { if (mReceiver == null) { Log.e(TAG, "onDialogAnimatedIn: Receiver is null"); return; } + if (requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onDialogAnimatedIn"); + return; + } + try { mReceiver.onDialogAnimatedIn(); } catch (RemoteException e) { @@ -393,7 +416,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba } @Override - public void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation) { + public void onDismissed(@DismissedReason int reason, + @Nullable byte[] credentialAttestation, long requestId) { + + if (mCurrentDialog != null && requestId != mCurrentDialog.getRequestId()) { + Log.w(TAG, "requestId doesn't match, skip onDismissed"); + return; + } + switch (reason) { case AuthDialogCallback.DISMISSED_USER_CANCELED: sendResultAndCleanUp(BiometricPrompt.DISMISSED_REASON_USER_CANCEL, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java index a7d2901b21c3f..bbe461aaf6d9c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthDialogCallback.java @@ -47,27 +47,28 @@ public interface AuthDialogCallback { * @param reason * @param credentialAttestation the HAT received from LockSettingsService upon verification */ - void onDismissed(@DismissedReason int reason, @Nullable byte[] credentialAttestation); + void onDismissed(@DismissedReason int reason, + @Nullable byte[] credentialAttestation, long requestId); /** * Invoked when the "try again" button is clicked */ - void onTryAgainPressed(); + void onTryAgainPressed(long requestId); /** * Invoked when the "use password" button is clicked */ - void onDeviceCredentialPressed(); + void onDeviceCredentialPressed(long requestId); /** * See {@link android.hardware.biometrics.BiometricPrompt.Builder * #setReceiveSystemEvents(boolean)} * @param event */ - void onSystemEvent(int event); + void onSystemEvent(int event, long requestId); /** * Notifies when the dialog has finished animating. */ - void onDialogAnimatedIn(); + void onDialogAnimatedIn(long requestId); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt index bc5a4d3d274ec..911fdb19eb666 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt @@ -48,11 +48,12 @@ import org.junit.Test import org.junit.runner.RunWith import org.mockito.Mock import org.mockito.Mockito.anyInt +import org.mockito.Mockito.anyLong import org.mockito.Mockito.eq import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.junit.MockitoJUnit import org.mockito.Mockito.`when` as whenever +import org.mockito.junit.MockitoJUnit @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @@ -87,7 +88,7 @@ class AuthContainerViewTest : SysuiTestCase() { @Test fun testNotifiesAnimatedIn() { initializeFingerprintContainer() - verify(callback).onDialogAnimatedIn() + verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L) } @Test @@ -96,13 +97,13 @@ class AuthContainerViewTest : SysuiTestCase() { container.dismissFromSystemServer() waitForIdleSync() - verify(callback, never()).onDialogAnimatedIn() + verify(callback, never()).onDialogAnimatedIn(anyLong()) container.addToView() waitForIdleSync() // attaching the view resets the state and allows this to happen again - verify(callback).onDialogAnimatedIn() + verify(callback).onDialogAnimatedIn(authContainer?.requestId ?: 0L) } @Test @@ -110,14 +111,17 @@ class AuthContainerViewTest : SysuiTestCase() { val container = initializeFingerprintContainer() waitForIdleSync() - verify(callback).onDialogAnimatedIn() + val requestID = authContainer?.requestId ?: 0L + + verify(callback).onDialogAnimatedIn(requestID) container.onWindowFocusChanged(false) waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_USER_CANCELED), - eq(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq(null), /* credentialAttestation */ + eq(requestID) ) assertThat(container.parent).isNull() } @@ -131,8 +135,9 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED), - eq(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED), + eq(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(container.parent).isNull() } @@ -146,11 +151,13 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onSystemEvent( - eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL) + eq(BiometricConstants.BIOMETRIC_SYSTEM_EVENT_EARLY_USER_CANCEL), + eq(authContainer?.requestId ?: 0L) ) verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_USER_CANCELED), - eq(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_USER_CANCELED), + eq(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(container.parent).isNull() } @@ -164,8 +171,9 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE), - eq(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE), + eq(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(container.parent).isNull() } @@ -180,7 +188,7 @@ class AuthContainerViewTest : SysuiTestCase() { ) waitForIdleSync() - verify(callback).onTryAgainPressed() + verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L) } @Test @@ -192,8 +200,9 @@ class AuthContainerViewTest : SysuiTestCase() { waitForIdleSync() verify(callback).onDismissed( - eq(AuthDialogCallback.DISMISSED_ERROR), - eq(null) /* credentialAttestation */ + eq(AuthDialogCallback.DISMISSED_ERROR), + eq(null), /* credentialAttestation */ + eq(authContainer?.requestId ?: 0L) ) assertThat(authContainer!!.parent).isNull() } @@ -209,7 +218,7 @@ class AuthContainerViewTest : SysuiTestCase() { ) waitForIdleSync() - verify(callback).onDeviceCredentialPressed() + verify(callback).onDeviceCredentialPressed(authContainer?.requestId ?: 0L) assertThat(container.hasCredentialView()).isTrue() } @@ -312,12 +321,12 @@ class AuthContainerViewTest : SysuiTestCase() { container.onAuthenticationFailed(BiometricAuthenticator.TYPE_FACE, "failed") waitForIdleSync() - verify(callback, never()).onTryAgainPressed() + verify(callback, never()).onTryAgainPressed(anyLong()) container.onPointerDown() waitForIdleSync() - verify(callback).onTryAgainPressed() + verify(callback).onTryAgainPressed(authContainer?.requestId ?: 0L) } private fun initializeFingerprintContainer( diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java index d948a99f8ad89..d158892e4ec54 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java @@ -291,7 +291,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonUserCanceled_whenDismissedByUserCancel() throws Exception { showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_USER_CANCEL), eq(null) /* credentialAttestation */); @@ -301,7 +302,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonNegative_whenDismissedByButtonNegative() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_NEGATIVE), eq(null) /* credentialAttestation */); @@ -311,7 +313,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonConfirmed_whenDismissedByButtonPositive() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BUTTON_POSITIVE, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRMED), eq(null) /* credentialAttestation */); @@ -321,7 +324,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonConfirmNotRequired_whenDismissedByAuthenticated() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_BIOMETRIC_CONFIRM_NOT_REQUIRED), eq(null) /* credentialAttestation */); @@ -331,7 +335,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonError_whenDismissedByError() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_ERROR, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_ERROR), eq(null) /* credentialAttestation */); @@ -341,7 +346,8 @@ public class AuthControllerTest extends SysuiTestCase { public void testSendsReasonServerRequested_whenDismissedByServer() throws Exception { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_BY_SYSTEM_SERVER, - null /* credentialAttestation */); + null, /* credentialAttestation */ + mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_SERVER_REQUESTED), eq(null) /* credentialAttestation */); @@ -355,7 +361,7 @@ public class AuthControllerTest extends SysuiTestCase { final byte[] credentialAttestation = generateRandomHAT(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, - credentialAttestation); + credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), AdditionalMatchers.aryEq(credentialAttestation)); @@ -531,7 +537,7 @@ public class AuthControllerTest extends SysuiTestCase { final byte[] credentialAttestation = generateRandomHAT(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED, - credentialAttestation); + credentialAttestation, mAuthController.mCurrentDialog.getRequestId()); verify(mReceiver).onDialogDismissed( eq(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED), AdditionalMatchers.aryEq(credentialAttestation)); @@ -640,17 +646,19 @@ public class AuthControllerTest extends SysuiTestCase { @Test public void testDoesNotCrash_whenTryAgainPressedAfterDismissal() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + final long requestID = mAuthController.mCurrentDialog.getRequestId(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); - mAuthController.onTryAgainPressed(); + null, /* credentialAttestation */requestID); + mAuthController.onTryAgainPressed(requestID); } @Test public void testDoesNotCrash_whenDeviceCredentialPressedAfterDismissal() { showDialog(new int[] {1} /* sensorIds */, false /* credentialAllowed */); + final long requestID = mAuthController.mCurrentDialog.getRequestId(); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); - mAuthController.onDeviceCredentialPressed(); + null /* credentialAttestation */, requestID); + mAuthController.onDeviceCredentialPressed(requestID); } @Test @@ -708,7 +716,8 @@ public class AuthControllerTest extends SysuiTestCase { // WHEN dialog is shown and then dismissed showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */); mAuthController.onDismissed(AuthDialogCallback.DISMISSED_USER_CANCELED, - null /* credentialAttestation */); + null /* credentialAttestation */, + mAuthController.mCurrentDialog.getRequestId()); // THEN callback should be received verify(callback).onBiometricPromptDismissed();