[camera] Fix crash from changing permissions (#2447)

Our listeners are called for every single permissions change, not just
the first one. This resulted in multiple replies being sent to Dart and
fatal crashes. Change to instead manually keep track of responses and
only respond to the first call.
diff --git a/packages/camera/CHANGELOG.md b/packages/camera/CHANGELOG.md
index 474dec2..79eefa6 100644
--- a/packages/camera/CHANGELOG.md
+++ b/packages/camera/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.5.7+3
+
+* Fix an Android crash when permissions are requested multiple times.
+
 ## 0.5.7+2
 
 * Remove the deprecated `author:` field from pubspec.yaml
diff --git a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java
index 3c86ce0..c1c3fa8 100644
--- a/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java
+++ b/packages/camera/android/src/main/java/io/flutter/plugins/camera/CameraPermissions.java
@@ -4,6 +4,7 @@
 import android.Manifest.permission;
 import android.app.Activity;
 import android.content.pm.PackageManager;
+import androidx.annotation.VisibleForTesting;
 import androidx.core.app.ActivityCompat;
 import androidx.core.content.ContextCompat;
 import io.flutter.plugin.common.PluginRegistry;
@@ -59,29 +60,38 @@
         == PackageManager.PERMISSION_GRANTED;
   }
 
-  private static class CameraRequestPermissionsListener
+  @VisibleForTesting
+  static final class CameraRequestPermissionsListener
       implements PluginRegistry.RequestPermissionsResultListener {
 
+    // There's no way to unregister permission listeners in the v1 embedding, so we'll be called
+    // duplicate times in cases where the user denies and then grants a permission. Keep track of if
+    // we've responded before and bail out of handling the callback manually if this is a repeat
+    // call.
+    boolean alreadyCalled = false;
+
     final ResultCallback callback;
 
-    private CameraRequestPermissionsListener(ResultCallback callback) {
+    @VisibleForTesting
+    CameraRequestPermissionsListener(ResultCallback callback) {
       this.callback = callback;
     }
 
     @Override
     public boolean onRequestPermissionsResult(int id, String[] permissions, int[] grantResults) {
-      if (id == CAMERA_REQUEST_ID) {
-        if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
-          callback.onResult("cameraPermission", "MediaRecorderCamera permission not granted");
-        } else if (grantResults.length > 1
-            && grantResults[1] != PackageManager.PERMISSION_GRANTED) {
-          callback.onResult("cameraPermission", "MediaRecorderAudio permission not granted");
-        } else {
-          callback.onResult(null, null);
-        }
-        return true;
+      if (alreadyCalled || id != CAMERA_REQUEST_ID) {
+        return false;
       }
-      return false;
+
+      alreadyCalled = true;
+      if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
+        callback.onResult("cameraPermission", "MediaRecorderCamera permission not granted");
+      } else if (grantResults.length > 1 && grantResults[1] != PackageManager.PERMISSION_GRANTED) {
+        callback.onResult("cameraPermission", "MediaRecorderAudio permission not granted");
+      } else {
+        callback.onResult(null, null);
+      }
+      return true;
     }
   }
 }
diff --git a/packages/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java b/packages/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java
new file mode 100644
index 0000000..b622c31
--- /dev/null
+++ b/packages/camera/android/src/test/java/io/flutter/plugins/camera/CameraPermissionsTest.java
@@ -0,0 +1,23 @@
+package io.flutter.plugins.camera;
+
+import static junit.framework.TestCase.assertEquals;
+
+import android.content.pm.PackageManager;
+import io.flutter.plugins.camera.CameraPermissions.CameraRequestPermissionsListener;
+import org.junit.Test;
+
+public class CameraPermissionsTest {
+  @Test
+  public void listener_respondsOnce() {
+    final int[] calledCounter = {0};
+    CameraRequestPermissionsListener permissionsListener =
+        new CameraRequestPermissionsListener((String code, String desc) -> calledCounter[0]++);
+
+    permissionsListener.onRequestPermissionsResult(
+        9796, null, new int[] {PackageManager.PERMISSION_DENIED});
+    permissionsListener.onRequestPermissionsResult(
+        9796, null, new int[] {PackageManager.PERMISSION_GRANTED});
+
+    assertEquals(1, calledCounter[0]);
+  }
+}
diff --git a/packages/camera/pubspec.yaml b/packages/camera/pubspec.yaml
index c45ea86..2e8d1cb 100644
--- a/packages/camera/pubspec.yaml
+++ b/packages/camera/pubspec.yaml
@@ -2,7 +2,7 @@
 description: A Flutter plugin for getting information about and controlling the
   camera on Android and iOS. Supports previewing the camera feed, capturing images, capturing video,
   and streaming image buffers to dart.
-version: 0.5.7+2
+version: 0.5.7+3
 
 homepage: https://github.com/flutter/plugins/tree/master/packages/camera