Merge "Fix fingerprint unlock stuck after renaming" into udc-qpr-dev
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java b/src/com/android/settings/biometrics/fingerprint/FingerprintAuthenticateSidecar.java
index 4264056..f3c8aba 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.AuthenticationResult;
 import android.os.CancellationSignal;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.settings.core.InstrumentedFragment;
 
 /**
@@ -80,7 +81,6 @@
 
                 @Override
                 public void onAuthenticationError(int errMsgId, CharSequence errString) {
-                    mCancellationSignal = null;
                     if (mListener != null) {
                         mListener.onAuthenticationError(errMsgId, errString);
                     } else {
@@ -108,10 +108,12 @@
     }
 
     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 @@
         }
         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 18b05ad..1998d56 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.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.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 @@
     @Mock
     private FragmentTransaction mFragmentTransaction;
 
+    @Captor
+    private ArgumentCaptor<CancellationSignal> mCancellationSignalArgumentCaptor =
+            ArgumentCaptor.forClass(CancellationSignal.class);
+    @Captor
+    private ArgumentCaptor<FingerprintManager.AuthenticationCallback>
+            mAuthenticationCallbackArgumentCaptor = ArgumentCaptor.forClass(
+            FingerprintManager.AuthenticationCallback.class);
+
+    private FingerprintAuthenticateSidecar mFingerprintAuthenticateSidecar;
+
     @Before
     public void setUp() {
         doReturn(true).when(mFingerprintManager).isHardwareDetected();
@@ -146,6 +158,34 @@
                 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 @@
         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();