Merge "[CameraPipe] Fix updates happening in out of order" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
index 5621082..b5eb887 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/FocusMeteringControl.kt
@@ -30,6 +30,7 @@
 import androidx.camera.camera2.pipe.Lock3ABehavior
 import androidx.camera.camera2.pipe.Result3A
 import androidx.camera.camera2.pipe.core.Log.debug
+import androidx.camera.camera2.pipe.core.Log.warn
 import androidx.camera.camera2.pipe.integration.adapter.asListenableFuture
 import androidx.camera.camera2.pipe.integration.adapter.propagateTo
 import androidx.camera.camera2.pipe.integration.compat.ZoomCompat
@@ -291,6 +292,9 @@
     ) {
         invokeOnCompletion { throwable ->
             if (throwable != null) {
+                warn(throwable) {
+                    "propagateToFocusMeteringResultDeferred: completed exceptionally!"
+                }
                 resultDeferred.completeExceptionally(throwable)
             } else {
                 val result3A = getCompleted()
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
index 0a5a70a..e5552c8 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/UseCaseCameraState.kt
@@ -32,7 +32,6 @@
 import androidx.camera.camera2.pipe.RequestMetadata
 import androidx.camera.camera2.pipe.RequestTemplate
 import androidx.camera.camera2.pipe.StreamId
-import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.core.Log.debug
 import androidx.camera.camera2.pipe.integration.config.UseCaseCameraScope
 import androidx.camera.camera2.pipe.integration.config.UseCaseGraphConfig
@@ -189,7 +188,7 @@
     }
 
     fun capture(requests: List<Request>) {
-        threads.scope.launch(start = CoroutineStart.UNDISPATCHED) {
+        threads.sequentialScope.launch(start = CoroutineStart.UNDISPATCHED) {
             cameraGraph.acquireSession().use { it.submit(requests) }
         }
     }
@@ -259,13 +258,13 @@
         // synchronously with the latest values. The startRepeating/stopRepeating call happens
         // outside of the synchronized block to avoid holding a lock while updating the camera
         // state.
-        threads.scope.launch(start = CoroutineStart.UNDISPATCHED) {
+        threads.sequentialScope.launch(start = CoroutineStart.UNDISPATCHED) {
             val result: CompletableDeferred<Unit>?
             val request: Request?
             try {
                     cameraGraph.acquireSession()
                 } catch (e: CancellationException) {
-                    Log.debug(e) { "Cannot acquire session at ${this@UseCaseCameraState}" }
+                    debug(e) { "Cannot acquire session at ${this@UseCaseCameraState}" }
                     null
                 }
                 .let { session ->
@@ -305,8 +304,9 @@
                                     )
                                 }
                             }
-                            Log.debug { "Update RepeatingRequest: $request" }
+                            debug { "Update RepeatingRequest: $request" }
                             it.startRepeating(request)
+                            // TODO: Invoke update3A only if required e.g. a 3A value has changed
                             it.update3A(request.parameters)
                         }
                     }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
index 210ae47..aeeb4db 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ZoomControl.kt
@@ -25,6 +25,7 @@
 import androidx.camera.camera2.pipe.integration.internal.ZoomMath.getZoomRatioFromLinearZoom
 import androidx.camera.core.CameraControl
 import androidx.camera.core.ZoomState
+import androidx.camera.core.impl.utils.Threads
 import androidx.camera.core.impl.utils.futures.Futures
 import androidx.lifecycle.LiveData
 import androidx.lifecycle.MutableLiveData
@@ -35,10 +36,8 @@
 import javax.inject.Inject
 import kotlinx.coroutines.CompletableDeferred
 import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.Job
 import kotlinx.coroutines.launch
-import kotlinx.coroutines.withContext
 
 const val DEFAULT_ZOOM_RATIO = 1.0f
 
@@ -92,11 +91,12 @@
         applyZoomState(defaultZoomState)
     }
 
-    private suspend fun setZoomState(value: ZoomState) {
-        // TODO: camera-camera2 updates livedata with setValue if calling thread is main thread,
-        //  and updates with postValue otherwise. Need to consider if always using setValue
-        //  via main thread is alright in camera-pipe.
-        withContext(Dispatchers.Main) { _zoomState.value = value }
+    private fun setZoomState(value: ZoomState) {
+        if (Threads.isMainThread()) {
+            _zoomState.value = value
+        } else {
+            _zoomState.postValue(value)
+        }
     }
 
     fun setLinearZoom(linearZoom: Float): ListenableFuture<Void> {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt
index a124a2a..90ebc54 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/graph/Controller3A.kt
@@ -215,6 +215,7 @@
 
         val result = listener.result
         synchronized(this) {
+            debug { "Controller3A#update3A: cancelling previous request $lastUpdate3AResult" }
             lastUpdate3AResult?.cancel("A newer call for 3A state update initiated.")
             lastUpdate3AResult = result
         }