Merge "Fix corner case when auth is canceled but credential was already confirmed"
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index e0ca1ac..875619a 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -338,7 +338,13 @@
 
     @Override
     public void hideAuthenticationDialog() {
-        if (DEBUG) Log.d(TAG, "hideAuthenticationDialog");
+        if (DEBUG) Log.d(TAG, "hideAuthenticationDialog: " + mCurrentDialog);
+
+        if (mCurrentDialog == null) {
+            // Could be possible if the caller canceled authentication after credential success
+            // but before the client was notified.
+            return;
+        }
 
         mCurrentDialog.dismissFromSystemServer();
 
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 c0e92e0..65399bf 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -20,7 +20,6 @@
 
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertNull;
-import static junit.framework.TestCase.assertNotNull;
 
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyInt;
@@ -292,6 +291,24 @@
     // Corner case tests
 
     @Test
+    public void testCancelAuthentication_whenCredentialConfirmed_doesntCrash() throws Exception {
+        // It's possible that before the client is notified that credential is confirmed, the client
+        // requests to cancel authentication.
+        //
+        // Test that the following sequence of events does not crash SystemUI:
+        // 1) Credential is confirmed
+        // 2) Client cancels authentication
+
+        showDialog(Authenticators.DEVICE_CREDENTIAL, BiometricPrompt.TYPE_NONE);
+        verify(mDialog1).show(any(), any());
+
+        mAuthController.onDismissed(AuthDialogCallback.DISMISSED_CREDENTIAL_AUTHENTICATED);
+        verify(mReceiver).onDialogDismissed(BiometricPrompt.DISMISSED_REASON_CREDENTIAL_CONFIRMED);
+
+        mAuthController.hideAuthenticationDialog();
+    }
+
+    @Test
     public void testShowNewDialog_beforeOldDialogDismissed_SkipsAnimations() {
         showDialog(Authenticators.BIOMETRIC_WEAK, BiometricPrompt.TYPE_FACE);
         verify(mDialog1).show(any(), any());