Merge "Fix BiometricPrompt cannot detect when press the notification in LS" into tm-qpr-dev am: 02a8a30217

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19229655

Change-Id: I96890dbe6c892caa23ef900753fc1b27587cc94c
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Vincent Wang
2022-07-15 03:55:54 +00:00
committed by Automerger Merge Worker
5 changed files with 98 additions and 48 deletions

View File

@@ -249,13 +249,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);
@@ -373,7 +373,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
@@ -774,7 +774,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;
}
}
@@ -804,7 +805,7 @@ public class AuthContainerView extends LinearLayout
}
mContainerState = STATE_SHOWING;
if (mBiometricView != null) {
mConfig.mCallback.onDialogAnimatedIn();
mConfig.mCallback.onDialogAnimatedIn(getRequestId());
mBiometricView.onDialogAnimatedIn();
}
}

View File

@@ -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,

View File

@@ -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);
}

View File

@@ -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<ByteArray?>(null) /* credentialAttestation */
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq<ByteArray?>(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<ByteArray?>(null) /* credentialAttestation */
eq(AuthDialogCallback.DISMISSED_BIOMETRIC_AUTHENTICATED),
eq<ByteArray?>(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<ByteArray?>(null) /* credentialAttestation */
eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
eq<ByteArray?>(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<ByteArray?>(null) /* credentialAttestation */
eq(AuthDialogCallback.DISMISSED_BUTTON_NEGATIVE),
eq<ByteArray?>(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<ByteArray?>(null) /* credentialAttestation */
eq(AuthDialogCallback.DISMISSED_ERROR),
eq<ByteArray?>(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()
}
@@ -322,12 +331,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(

View File

@@ -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();