diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java index 426405627ab..f3c8aba4095 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java @@ -21,6 +21,7 @@ import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.AuthenticationResult; import android.os.CancellationSignal; +import com.android.internal.annotations.VisibleForTesting; import com.android.settings.core.InstrumentedFragment; /** @@ -80,7 +81,6 @@ public class FingerprintAuthenticateSidecar extends InstrumentedFragment { @Override public void onAuthenticationError(int errMsgId, CharSequence errString) { - mCancellationSignal = null; if (mListener != null) { mListener.onAuthenticationError(errMsgId, errString); } else { @@ -108,10 +108,12 @@ public class FingerprintAuthenticateSidecar extends InstrumentedFragment { } public void stopAuthentication() { - if (mCancellationSignal != null && !mCancellationSignal.isCanceled()) { + if (mCancellationSignal != null) { + // This will automatically check if the cancel has been sent and if so + // it won't send it again. mCancellationSignal.cancel(); + mCancellationSignal = null; } - mCancellationSignal = null; } public void setListener(Listener listener) { @@ -129,4 +131,9 @@ public class FingerprintAuthenticateSidecar extends InstrumentedFragment { } mListener = listener; } + + @VisibleForTesting + boolean isCancelled() { + return mCancellationSignal == null || mCancellationSignal.isCanceled(); + } } \ No newline at end of file diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java index 18b05add7ad..1998d56b1ca 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintSettingsFragmentTest.java @@ -44,6 +44,7 @@ import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; import android.os.Bundle; +import android.os.CancellationSignal; import android.view.LayoutInflater; import android.view.ViewGroup; @@ -68,6 +69,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; @@ -92,6 +94,16 @@ public class FingerprintSettingsFragmentTest { @Mock private FragmentTransaction mFragmentTransaction; + @Captor + private ArgumentCaptor mCancellationSignalArgumentCaptor = + ArgumentCaptor.forClass(CancellationSignal.class); + @Captor + private ArgumentCaptor + mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass( + FingerprintManager.AuthenticationCallback.class); + + private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar; + @Before public void setUp() { doReturn(true).when(mFingerprintManager).isHardwareDetected(); @@ -146,6 +158,34 @@ public class FingerprintSettingsFragmentTest { false)).isTrue(); } + // Test the case when FingerprintAuthenticateSidecar receives an error callback from the + // framework or from another authentication client. The cancellation signal should not be set + // to null because there may exist a running authentication client. + // The signal can only be cancelled from the caller in FingerprintSettings. + @Test + public void testCancellationSignalLifeCycle() { + setUpFragment(false); + + mFingerprintAuthenticateSidecar.setFingerprintManager(mFingerprintManager); + + doNothing().when(mFingerprintManager).authenticate(any(), + mCancellationSignalArgumentCaptor.capture(), + mAuthenticationCallbackArgumentCaptor.capture(), any(), anyInt()); + + mFingerprintAuthenticateSidecar.startAuthentication(1); + + assertThat(mAuthenticationCallbackArgumentCaptor.getValue()).isNotNull(); + assertThat(mCancellationSignalArgumentCaptor.getValue()).isNotNull(); + + // Authentication error callback should not cancel the signal. + mAuthenticationCallbackArgumentCaptor.getValue().onAuthenticationError(0, ""); + assertThat(mFingerprintAuthenticateSidecar.isCancelled()).isFalse(); + + // The signal should be cancelled when caller stops the authentication. + mFingerprintAuthenticateSidecar.stopAuthentication(); + assertThat(mFingerprintAuthenticateSidecar.isCancelled()).isTrue(); + } + private void setUpFragment(boolean showChooseLock) { Intent intent = new Intent(); if (!showChooseLock) { @@ -166,6 +206,10 @@ public class FingerprintSettingsFragmentTest { doReturn(fragmentManager).when(mFragment).getFragmentManager(); doReturn(fragmentManager).when(mActivity).getSupportFragmentManager(); + mFingerprintAuthenticateSidecar = new FingerprintAuthenticateSidecar(); + doReturn(mFingerprintAuthenticateSidecar).when(fragmentManager).findFragmentByTag( + "authenticate_sidecar"); + doNothing().when(mFragment).startActivityForResult(any(Intent.class), anyInt()); setSensor();