Merge "Cancel graph state collection after UseCaseCamera is closed" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
index 9ab3c3c..3c3ebe9 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCamera.kt
@@ -23,6 +23,7 @@
 import android.hardware.camera2.params.MeteringRectangle
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraGraph
+import androidx.camera.camera2.pipe.GraphState.GraphStateStopped
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.core.Log.debug
 import androidx.camera.camera2.pipe.integration.adapter.SessionConfigAdapter
@@ -35,8 +36,10 @@
 import dagger.Module
 import javax.inject.Inject
 import kotlinx.atomicfu.atomic
+import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.Job
+import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
 
 internal val useCaseCameraIds = atomic(0)
@@ -85,7 +88,7 @@
     override val requestControl: UseCaseCameraRequestControl,
 ) : UseCaseCamera {
     private val debugId = useCaseCameraIds.incrementAndGet()
-    private val graphStateJob: Job
+    private val closed = atomic(false)
 
     override var runningUseCases = setOf<UseCase>()
         set(value) {
@@ -109,21 +112,27 @@
         useCaseGraphConfig.apply {
             cameraStateAdapter.onGraphUpdated(graph)
         }
-        graphStateJob = threads.scope.launch {
+        threads.scope.launch {
             useCaseGraphConfig.apply {
                 graph.graphState.collect {
                     cameraStateAdapter.onGraphStateUpdated(graph, it)
+                    if (closed.value && it is GraphStateStopped) {
+                        cancel()
+                    }
                 }
             }
         }
     }
 
     override fun close(): Job {
-        graphStateJob.cancel()
-        return threads.scope.launch {
-            debug { "Closing $this" }
-            useCaseGraphConfig.graph.close()
-            useCaseSurfaceManager.stopAsync().await()
+        return if (closed.compareAndSet(expect = false, update = true)) {
+            threads.scope.launch {
+                debug { "Closing $this" }
+                useCaseGraphConfig.graph.close()
+                useCaseSurfaceManager.stopAsync().await()
+            }
+        } else {
+            CompletableDeferred(Unit)
         }
     }