[go: nahoru, domu]

Merge "Create CaptureCallback interface for CameraExtensionSession and CameraCaptureSession compatibility" into androidx-main
diff --git a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
index f88ee43..80eed7d 100644
--- a/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
+++ b/camera/camera-camera2-pipe-integration/src/androidTest/java/androidx/camera/camera2/pipe/testing/VerifyResultListener.kt
@@ -36,8 +36,10 @@
     private val waitingCount = atomic(capturesCount)
     private val failureException =
         TimeoutException("Test doesn't complete after waiting for $capturesCount frames.")
-    @Volatile private var startReceiving = false
-    @Volatile private var _verifyBlock: (
+    @Volatile
+    private var startReceiving = false
+    @Volatile
+    private var _verifyBlock: (
         captureRequest: RequestMetadata,
         captureResult: FrameInfo
     ) -> Boolean = { _, _ -> false }
@@ -77,6 +79,10 @@
         }
     }
 
+    @Deprecated(
+        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+        level = DeprecationLevel.WARNING
+    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
index 6929c99..dfb4e41 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CameraCallbackMap.kt
@@ -118,6 +118,10 @@
         }
     }
 
+    @Deprecated(
+        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+        level = DeprecationLevel.WARNING
+    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
index 146f2b4..484cd54 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/CapturePipeline.kt
@@ -256,6 +256,12 @@
                             completeSignal.complete(null)
                         }
 
+                        @Deprecated(
+                            message = "Migrating to using RequestFailureWrapper instead of " +
+                                "CaptureFailure",
+                            level = DeprecationLevel.WARNING,
+                            replaceWith = ReplaceWith("onFailed")
+                        )
                         @SuppressLint("ClassVerificationFailure")
                         override fun onFailed(
                             requestMetadata: RequestMetadata,
@@ -283,7 +289,8 @@
             } catch (_: CancellationException) {
                 info {
                     "CapturePipeline#submitRequestInternal:" +
-                    " CameraGraph.Session could not be acquired, requests may need re-submission"
+                        " CameraGraph.Session could not be acquired, requests may need " +
+                        "re-submission"
                 }
 
                 // completing the requests exceptionally so that they are retried with next camera
@@ -324,6 +331,7 @@
                     CaptureResult.CONTROL_AE_STATE
                 ) == CONTROL_AE_STATE_FLASH_REQUIRED
             }
+
             FLASH_MODE_OFF -> false
             else -> throw AssertionError(flashMode)
         }
diff --git a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
index 4f1c06d..6f98ef1 100644
--- a/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
+++ b/camera/camera-camera2-pipe-integration/src/main/java/androidx/camera/camera2/pipe/integration/impl/ComboRequestListener.kt
@@ -84,11 +84,16 @@
         }
     }
 
+    @Deprecated(
+        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+        level = DeprecationLevel.WARNING
+    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
         captureFailure: CaptureFailure
     ) {
+        @Suppress("DEPRECATION")
         listeners.forEach { (listener, executor) ->
             executor.execute { listener.onFailed(requestMetadata, frameNumber, captureFailure) }
         }
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 dadbc05..75a210e 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
@@ -308,7 +308,7 @@
         key: CaptureRequest.Key<*>
     ): Int? = this?.get(key) as? Int
 
-    inner class RequestListener() : Request.Listener {
+    inner class RequestListener : Request.Listener {
         override fun onTotalCaptureResult(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
@@ -324,11 +324,16 @@
             }
         }
 
+        @Deprecated(
+            message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+            level = DeprecationLevel.WARNING
+        )
         override fun onFailed(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
             captureFailure: CaptureFailure,
         ) {
+            @Suppress("DEPRECATION")
             super.onFailed(requestMetadata, frameNumber, captureFailure)
             completeExceptionally(requestMetadata, captureFailure)
         }
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
index ae0292a..a98c7d5 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/CapturePipelineTest.kt
@@ -612,6 +612,7 @@
         fakeCameraGraphSession.requestHandler = { requests ->
             requests.forEach { request ->
                 // Callback capture fail immediately.
+                @Suppress("DEPRECATION")
                 request.listeners.forEach {
                     it.onFailed(
                         requestMetadata = FakeRequestMetadata(),
@@ -687,11 +688,13 @@
 
         // Act.
         capturePipeline.submitStillCaptures(
-            requests = listOf(Request(
-                streams = emptyList(),
-                parameters = mapOf(CONTROL_AE_MODE to CONTROL_AE_MODE_ON_ALWAYS_FLASH),
-                template = RequestTemplate(CameraDevice.TEMPLATE_STILL_CAPTURE)
-            )),
+            requests = listOf(
+                Request(
+                    streams = emptyList(),
+                    parameters = mapOf(CONTROL_AE_MODE to CONTROL_AE_MODE_ON_ALWAYS_FLASH),
+                    template = RequestTemplate(CameraDevice.TEMPLATE_STILL_CAPTURE)
+                )
+            ),
             captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
             flashMode = ImageCapture.FLASH_MODE_ON,
             flashType = ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
@@ -725,11 +728,13 @@
 
         // Act.
         capturePipeline.submitStillCaptures(
-            requests = listOf(Request(
-                streams = emptyList(),
-                parameters = mapOf(CONTROL_AE_MODE to CONTROL_AE_MODE_ON_ALWAYS_FLASH),
-                template = RequestTemplate(CameraDevice.TEMPLATE_STILL_CAPTURE)
-            )),
+            requests = listOf(
+                Request(
+                    streams = emptyList(),
+                    parameters = mapOf(CONTROL_AE_MODE to CONTROL_AE_MODE_ON_ALWAYS_FLASH),
+                    template = RequestTemplate(CameraDevice.TEMPLATE_STILL_CAPTURE)
+                )
+            ),
             captureMode = ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY,
             flashMode = ImageCapture.FLASH_MODE_ON,
             flashType = ImageCapture.FLASH_TYPE_ONE_SHOT_FLASH,
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
index 63e023a..baa17cc 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/impl/StillCaptureRequestTest.kt
@@ -201,6 +201,7 @@
 
         fakeCameraGraphSession.submittedRequests.first().let { request ->
             request.listeners.forEach { listener ->
+                @Suppress("DEPRECATION")
                 listener.onFailed(
                     FakeRequestMetadata(),
                     FrameNumber(0),
diff --git a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
index 806a400..c3d481e 100644
--- a/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
+++ b/camera/camera-camera2-pipe-integration/src/test/java/androidx/camera/camera2/pipe/integration/testing/FakeCameraGraphSession.kt
@@ -51,6 +51,7 @@
         FAILED,
         ABORTED
     }
+
     var startRepeatingSignal = CompletableDeferred(TOTAL_CAPTURE_DONE) // already completed
 
     val submittedRequests = mutableListOf<Request>()
@@ -173,9 +174,11 @@
                 TOTAL_CAPTURE_DONE -> listener.onTotalCaptureResult(
                     FakeRequestMetadata(request = request), FrameNumber(0), FakeFrameInfo()
                 )
-                FAILED -> listener.onFailed(
+
+                FAILED -> @Suppress("DEPRECATION") listener.onFailed(
                     FakeRequestMetadata(request = request), FrameNumber(0), getFakeCaptureFailure()
                 )
+
                 ABORTED -> listener.onRequestSequenceAborted(
                     FakeRequestMetadata(request = request)
                 )
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
index a301e01..bf2264c 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/CameraGraphSimulator.kt
@@ -18,7 +18,6 @@
 
 import android.content.Context
 import android.graphics.SurfaceTexture
-import android.hardware.camera2.CaptureFailure
 import android.hardware.camera2.CaptureResult
 import android.view.Surface
 import androidx.annotation.RequiresApi
@@ -34,6 +33,7 @@
 import androidx.camera.camera2.pipe.GraphState.GraphStateError
 import androidx.camera.camera2.pipe.Metadata
 import androidx.camera.camera2.pipe.Request
+import androidx.camera.camera2.pipe.RequestFailureWrapper
 import androidx.camera.camera2.pipe.StreamId
 import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -283,9 +283,9 @@
             }
         }
 
-        fun simulateFailure(captureFailure: CaptureFailure) {
+        fun simulateFailure(requestFailureWrapper: RequestFailureWrapper) {
             requestSequence.invokeOnRequest(requestMetadata) {
-                it.onFailed(requestMetadata, frameNumber, captureFailure)
+                it.onFailed(requestMetadata, frameNumber, requestFailureWrapper)
             }
         }
 
diff --git a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
index a3ca0db..e097dc3 100644
--- a/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
+++ b/camera/camera-camera2-pipe-testing/src/main/java/androidx/camera/camera2/pipe/testing/FakeRequestListener.kt
@@ -37,7 +37,7 @@
  * to be sent.
  */
 @Suppress("ListenerInterface")
-public class FakeRequestListener(private val replayBuffer: Int = 10) : Request.Listener {
+class FakeRequestListener(private val replayBuffer: Int = 10) : Request.Listener {
 
     private val _onStartedFlow = MutableSharedFlow<OnStarted>(replay = replayBuffer)
     val >
@@ -148,6 +148,10 @@
             "($replayBuffer) may need to be increased."
     }
 
+    @Deprecated(
+        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+        level = DeprecationLevel.WARNING
+    )
     override fun onFailed(
         requestMetadata: RequestMetadata,
         frameNumber: FrameNumber,
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
index 359e98d..1c98dad 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/Requests.kt
@@ -141,6 +141,17 @@
         ) {
         }
 
+        @Deprecated(
+            message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+            level = DeprecationLevel.WARNING
+        )
+        fun onFailed(
+            requestMetadata: RequestMetadata,
+            frameNumber: FrameNumber,
+            captureFailure: CaptureFailure
+        ) {
+        }
+
         /**
          * onFailed occurs when a CaptureRequest failed in some way and the frame will not receive
          * the [onTotalCaptureResult] callback.
@@ -149,13 +160,13 @@
          *
          * @param requestMetadata the data about the camera2 request that was sent to the camera.
          * @param frameNumber the android frame number for this exposure
-         * @param captureFailure the android [CaptureFailure] data
+         * @param requestFailureWrapper the android [RequestFailureWrapper] data wrapper
          * @see android.hardware.camera2.CameraCaptureSession.CaptureCallback.onCaptureFailed
          */
         fun onFailed(
             requestMetadata: RequestMetadata,
             frameNumber: FrameNumber,
-            captureFailure: CaptureFailure
+            requestFailureWrapper: RequestFailureWrapper
         ) {
         }
 
@@ -239,6 +250,23 @@
 }
 
 /**
+ * Interface wrapper for [CaptureFailure].
+ *
+ * This interface should be used instead of [CaptureFailure] because its package-private
+ * constructor prevents directly creating an instance of it.
+ */
+@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
+interface RequestFailureWrapper {
+    val requestMetadata: RequestMetadata
+
+    val frameNumber: FrameNumber
+
+    val reason: Int
+
+    val wasImageCaptured: Boolean
+}
+
+/**
  * A [RequestTemplate] indicates which preset set list of parameters will be applied to a request by
  * default. These values are defined by camera2.
  */
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
new file mode 100644
index 0000000..7b22375
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/AndroidCaptureFailure.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+@file:RequiresApi(21) // TODO(b/200306659): Remove and replace with annotation on package-info.java
+
+package androidx.camera.camera2.pipe.compat
+
+import android.hardware.camera2.CaptureFailure
+import android.os.Build
+import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.FrameNumber
+import androidx.camera.camera2.pipe.RequestFailureWrapper
+import androidx.camera.camera2.pipe.RequestMetadata
+
+/**
+ * This class implements the [RequestFailureWrapper] interface to create a
+ * CaptureFailure object that can be used instead of the package-private [CaptureFailure]
+ */
+@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
+internal class AndroidCaptureFailure(
+    override val requestMetadata: RequestMetadata,
+    override val wasImageCaptured: Boolean,
+    override val frameNumber: FrameNumber,
+    override val reason: Int
+) : RequestFailureWrapper
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureCallback.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureCallback.kt
new file mode 100644
index 0000000..4fbaac1
--- /dev/null
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureCallback.kt
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package androidx.camera.camera2.pipe.compat
+
+import android.hardware.camera2.CameraCaptureSession
+import android.hardware.camera2.CameraExtensionSession
+import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.CaptureResult
+import android.hardware.camera2.TotalCaptureResult
+import androidx.camera.camera2.pipe.FrameNumber
+
+/**
+ * Interface for merging functionality of [CameraCaptureSession.CaptureCallback] and
+ * [CameraExtensionSession.ExtensionCaptureCallback].
+ *
+ * [CameraCaptureSession.CaptureCallback] and [CameraExtensionSession.ExtensionCaptureCallback]
+ * are abstract classes, so a class cannot extend both of them. This interface prevents duplication
+ * of code and developer facing endpoints because it is agnostic of which session type it is
+ * used for.
+ */
+internal interface Camera2CaptureCallback {
+    fun onCaptureStarted(
+        captureRequest: CaptureRequest,
+        captureFrameNumber: Long,
+        captureTimestamp: Long
+    )
+
+    fun onCaptureProgressed(captureRequest: CaptureRequest, partialCaptureResult: CaptureResult)
+
+    fun onCaptureCompleted(
+        captureRequest: CaptureRequest,
+        captureResult: TotalCaptureResult,
+        frameNumber: FrameNumber
+    )
+
+    fun onCaptureFailed(
+        captureRequest: CaptureRequest,
+        frameNumber: FrameNumber
+    )
+
+    fun onCaptureSequenceCompleted(captureSequenceId: Int, captureFrameNumber: Long)
+
+    fun onCaptureSequenceAborted(captureSequenceId: Int)
+}
\ No newline at end of file
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt
index c58f0f6..f0ad932a 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequence.kt
@@ -50,7 +50,8 @@
     override val sequenceListener: CaptureSequence.CaptureSequenceListener,
     private val requestNumberMap: Map<RequestNumber, RequestMetadata>,
     private val surfaceMap: Map<Surface, StreamId>,
-) : CameraCaptureSession.CaptureCallback(), CaptureSequence<CaptureRequest> {
+) : Camera2CaptureCallback, CameraCaptureSession.CaptureCallback(),
+    CaptureSequence<CaptureRequest> {
     private val debugId = captureSequenceDebugIds.incrementAndGet()
     private val hasStarted = CompletableDeferred<Unit>()
 
@@ -85,6 +86,12 @@
         captureRequest: CaptureRequest,
         captureTimestamp: Long,
         captureFrameNumber: Long
+    ) = onCaptureStarted(captureRequest, captureTimestamp, captureFrameNumber)
+
+    override fun onCaptureStarted(
+        captureRequest: CaptureRequest,
+        captureFrameNumber: Long,
+        captureTimestamp: Long
     ) {
         val requestNumber = readRequestNumber(captureRequest)
         val timestamp = CameraTimestamp(captureTimestamp)
@@ -102,6 +109,11 @@
         captureSession: CameraCaptureSession,
         captureRequest: CaptureRequest,
         partialCaptureResult: CaptureResult
+    ) = onCaptureProgressed(captureRequest, partialCaptureResult)
+
+    override fun onCaptureProgressed(
+        captureRequest: CaptureRequest,
+        partialCaptureResult: CaptureResult
     ) {
         val requestNumber = readRequestNumber(captureRequest)
         val frameNumber = FrameNumber(partialCaptureResult.frameNumber)
@@ -118,11 +130,16 @@
         captureSession: CameraCaptureSession,
         captureRequest: CaptureRequest,
         captureResult: TotalCaptureResult
+    ) = onCaptureCompleted(captureRequest, captureResult, FrameNumber(captureResult.frameNumber))
+
+    override fun onCaptureCompleted(
+        captureRequest: CaptureRequest,
+        captureResult: TotalCaptureResult,
+        frameNumber: FrameNumber
     ) {
         sequenceListener.onCaptureSequenceComplete(this)
 
         val requestNumber = readRequestNumber(captureRequest)
-        val frameNumber = FrameNumber(captureResult.frameNumber)
 
         // Load the request and throw if we are not able to find an associated request. Under
         // normal circumstances this should never happen.
@@ -137,21 +154,42 @@
         invokeOnRequest(request) { it.onComplete(request, frameNumber, frameInfo) }
     }
 
+    @Deprecated(
+        message = "Migrating to using RequestFailureWrapper instead of CaptureFailure",
+        level = DeprecationLevel.WARNING,
+        replaceWith = ReplaceWith("onFailed")
+    )
     override fun onCaptureFailed(
         captureSession: CameraCaptureSession,
         captureRequest: CaptureRequest,
         captureFailure: CaptureFailure
+    ) = onCaptureFailed(
+        captureRequest,
+        FrameNumber(captureFailure.frameNumber)
+    )
+
+    override fun onCaptureFailed(
+        captureRequest: CaptureRequest,
+        frameNumber: FrameNumber
     ) {
         sequenceListener.onCaptureSequenceComplete(this)
 
         val requestNumber = readRequestNumber(captureRequest)
-        val frameNumber = FrameNumber(captureFailure.frameNumber)
 
         // Load the request and throw if we are not able to find an associated request. Under
         // normal circumstances this should never happen.
         val request = readRequestMetadata(requestNumber)
 
-        invokeOnRequest(request) { it.onFailed(request, frameNumber, captureFailure) }
+        val androidCaptureFailure = AndroidCaptureFailure(
+            request,
+            false,
+            frameNumber,
+            CaptureFailure.REASON_ERROR
+        )
+
+        invokeOnRequest(request) {
+            it.onFailed(request, frameNumber, androidCaptureFailure)
+        }
     }
 
     override fun onCaptureBufferLost(
@@ -178,6 +216,11 @@
         captureSession: CameraCaptureSession,
         captureSequenceId: Int,
         captureFrameNumber: Long
+    ) = onCaptureSequenceCompleted(captureSequenceId, captureFrameNumber)
+
+    override fun onCaptureSequenceCompleted(
+        captureSequenceId: Int,
+        captureFrameNumber: Long
     ) {
         sequenceListener.onCaptureSequenceComplete(this)
 
@@ -195,7 +238,9 @@
     override fun onCaptureSequenceAborted(
         captureSession: CameraCaptureSession,
         captureSequenceId: Int
-    ) {
+    ) = onCaptureSequenceAborted(captureSequenceId)
+
+    override fun onCaptureSequenceAborted(captureSequenceId: Int) {
         sequenceListener.onCaptureSequenceComplete(this)
 
         check(sequenceNumber == captureSequenceId) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
index 9a9bd8ac..212b808 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/Camera2CaptureSequenceProcessor.kt
@@ -277,23 +277,15 @@
                 if (shouldWaitForRepeatingRequest) {
                     lastSingleRepeatingRequestSequence = captureSequence
                 }
-                session.setRepeatingRequest(
-                    captureSequence.captureRequestList[0], captureCallback, threads.camera2Handler
-                )
+                session.setRepeatingRequest(captureSequence.captureRequestList[0], captureCallback)
             } else {
-                session.capture(
-                    captureSequence.captureRequestList[0], captureSequence, threads.camera2Handler
-                )
+                session.capture(captureSequence.captureRequestList[0], captureSequence)
             }
         } else {
             if (captureSequence.repeating) {
-                session.setRepeatingBurst(
-                    captureSequence.captureRequestList, captureSequence, threads.camera2Handler
-                )
+                session.setRepeatingBurst(captureSequence.captureRequestList, captureSequence)
             } else {
-                session.captureBurst(
-                    captureSequence.captureRequestList, captureSequence, threads.camera2Handler
-                )
+                session.captureBurst(captureSequence.captureRequestList, captureSequence)
             }
         }
     }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
index 8981549..6a4b1eb 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CameraDeviceWrapper.kt
@@ -26,7 +26,6 @@
 import android.hardware.camera2.params.InputConfiguration
 import android.hardware.camera2.params.OutputConfiguration
 import android.os.Build
-import android.os.Handler
 import android.view.Surface
 import androidx.annotation.GuardedBy
 import androidx.annotation.RequiresApi
@@ -37,6 +36,7 @@
 import androidx.camera.camera2.pipe.core.Debug
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.core.SystemTimeSource
+import androidx.camera.camera2.pipe.core.Threads
 import androidx.camera.camera2.pipe.core.Timestamps
 import androidx.camera.camera2.pipe.core.Timestamps.formatMs
 import androidx.camera.camera2.pipe.internal.CameraErrorListener
@@ -64,8 +64,7 @@
     /** @see CameraDevice.createCaptureSession */
     fun createCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean
 
     /** @see CameraDevice.createReprocessableCaptureSession */
@@ -73,24 +72,21 @@
     fun createReprocessableCaptureSession(
         input: InputConfiguration,
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean
 
     /** @see CameraDevice.createConstrainedHighSpeedCaptureSession */
     @RequiresApi(Build.VERSION_CODES.M)
     fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean
 
     /** @see CameraDevice.createCaptureSessionByOutputConfigurations */
     @RequiresApi(Build.VERSION_CODES.N)
     fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean
 
     /** @see CameraDevice.createReprocessableCaptureSessionByConfigurations */
@@ -98,8 +94,7 @@
     fun createReprocessableCaptureSessionByConfigurations(
         inputConfig: InputConfigData,
         outputs: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean
 
     /** @see CameraDevice.createCaptureSession */
@@ -133,13 +128,13 @@
     private val cameraErrorListener: CameraErrorListener,
     private val interopSessionStateCallback: StateCallback? = null,
     private val interopExtensionSessionStateCallback: CameraExtensionSession.StateCallback? = null,
+    private val threads: Threads
 ) : CameraDeviceWrapper {
     private val _lastStateCallback = atomic<OnSessionFinalized?>(null)
 
     override fun createCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
@@ -154,9 +149,10 @@
                 stateCallback,
                 previousStateCallback,
                 cameraErrorListener,
-                interopSessionStateCallback
+                interopSessionStateCallback,
+                threads.camera2Handler
             ),
-            handler
+            threads.camera2Handler
         )
     } != null
 
@@ -183,7 +179,8 @@
                         stateCallback,
                         previousStateCallback,
                         cameraErrorListener,
-                        interopExtensionSessionStateCallback
+                        interopExtensionSessionStateCallback,
+                        config.executor
                     ),
                 )
             Api31Compat.createExtensionCaptureSession(cameraDevice, sessionConfig)
@@ -193,8 +190,7 @@
     override fun createReprocessableCaptureSession(
         input: InputConfiguration,
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
@@ -210,17 +206,17 @@
                 stateCallback,
                 previousStateCallback,
                 cameraErrorListener,
-                interopSessionStateCallback
+                interopSessionStateCallback,
+                threads.camera2Handler
             ),
-            handler
+            threads.camera2Handler
         )
     } != null
 
     @RequiresApi(23)
     override fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
@@ -235,17 +231,17 @@
                 stateCallback,
                 previousStateCallback,
                 cameraErrorListener,
-                interopSessionStateCallback
+                interopSessionStateCallback,
+                threads.camera2Handler
             ),
-            handler
+            threads.camera2Handler
         )
     } != null
 
     @RequiresApi(24)
     override fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
@@ -260,9 +256,10 @@
                 stateCallback,
                 previousStateCallback,
                 cameraErrorListener,
-                interopSessionStateCallback
+                interopSessionStateCallback,
+                threads.camera2Handler
             ),
-            handler
+            threads.camera2Handler
         )
     } != null
 
@@ -270,8 +267,7 @@
     override fun createReprocessableCaptureSessionByConfigurations(
         inputConfig: InputConfigData,
         outputs: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean = catchAndReportCameraExceptions(cameraId, cameraErrorListener) {
         val previousStateCallback = _lastStateCallback.value
         check(_lastStateCallback.compareAndSet(previousStateCallback, stateCallback))
@@ -289,9 +285,10 @@
                 stateCallback,
                 previousStateCallback,
                 cameraErrorListener,
-                interopSessionStateCallback
+                interopSessionStateCallback,
+                threads.camera2Handler
             ),
-            handler
+            threads.camera2Handler
         )
     } != null
 
@@ -312,7 +309,8 @@
                         stateCallback,
                         previousStateCallback,
                         cameraErrorListener,
-                        interopSessionStateCallback
+                        interopSessionStateCallback,
+                        threads.camera2Handler
                     )
                 )
 
@@ -387,15 +385,14 @@
 
     override fun createCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ) = synchronized(lock) {
         if (disconnected) {
             Log.warn { "createCaptureSession failed: Virtual device disconnected" }
             stateCallback.onSessionFinalized()
             false
         } else {
-            androidCameraDevice.createCaptureSession(outputs, stateCallback, handler)
+            androidCameraDevice.createCaptureSession(outputs, stateCallback)
         }
     }
 
@@ -403,8 +400,7 @@
     override fun createReprocessableCaptureSession(
         input: InputConfiguration,
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ) = synchronized(lock) {
         if (disconnected) {
             Log.warn { "createReprocessableCaptureSession failed: Virtual device disconnected" }
@@ -414,8 +410,7 @@
             androidCameraDevice.createReprocessableCaptureSession(
                 input,
                 outputs,
-                stateCallback,
-                handler
+                stateCallback
             )
         }
     }
@@ -423,8 +418,7 @@
     @RequiresApi(23)
     override fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ) = synchronized(lock) {
         if (disconnected) {
             Log.warn {
@@ -435,8 +429,7 @@
         } else {
             androidCameraDevice.createConstrainedHighSpeedCaptureSession(
                 outputs,
-                stateCallback,
-                handler
+                stateCallback
             )
         }
     }
@@ -444,8 +437,7 @@
     @RequiresApi(24)
     override fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ) = synchronized(lock) {
         if (disconnected) {
             Log.warn {
@@ -456,8 +448,7 @@
         } else {
             androidCameraDevice.createCaptureSessionByOutputConfigurations(
                 outputConfigurations,
-                stateCallback,
-                handler
+                stateCallback
             )
         }
     }
@@ -466,8 +457,7 @@
     override fun createReprocessableCaptureSessionByConfigurations(
         inputConfig: InputConfigData,
         outputs: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ) = synchronized(lock) {
         if (disconnected) {
             Log.warn {
@@ -480,8 +470,7 @@
             androidCameraDevice.createReprocessableCaptureSessionByConfigurations(
                 inputConfig,
                 outputs,
-                stateCallback,
-                handler
+                stateCallback
             )
         }
     }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
index c13f8c6..694c257 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactory.kt
@@ -106,7 +106,7 @@
         captureSessionState: CaptureSessionState
     ): Map<StreamId, OutputConfigurationWrapper> {
         if (!cameraDevice.createCaptureSession(
-                surfaces.map { it.value }, captureSessionState, threads.camera2Handler
+                surfaces.map { it.value }, captureSessionState
             )
         ) {
             Log.warn {
@@ -137,8 +137,7 @@
                         outputConfig.format.value
                     ),
                     surfaces.map { it.value },
-                    captureSessionState,
-                    threads.camera2Handler
+                    captureSessionState
                 )
             ) {
                 Log.warn {
@@ -149,7 +148,7 @@
             }
         } else {
             if (!cameraDevice.createCaptureSession(
-                    surfaces.map { it.value }, captureSessionState, threads.camera2Handler
+                    surfaces.map { it.value }, captureSessionState
                 )
             ) {
                 Log.warn {
@@ -171,7 +170,7 @@
         captureSessionState: CaptureSessionState
     ): Map<StreamId, OutputConfigurationWrapper> {
         if (!cameraDevice.createConstrainedHighSpeedCaptureSession(
-                surfaces.map { it.value }, captureSessionState, threads.camera2Handler
+                surfaces.map { it.value }, captureSessionState
             )
         ) {
             Log.warn {
@@ -213,7 +212,7 @@
 
         val result = if (graphConfig.input == null) {
             cameraDevice.createCaptureSessionByOutputConfigurations(
-                outputs.all, captureSessionState, threads.camera2Handler
+                outputs.all, captureSessionState
             )
         } else {
             val outputConfig = graphConfig.input.stream.outputs.single()
@@ -224,8 +223,7 @@
                     outputConfig.format.value
                 ),
                 outputs.all,
-                captureSessionState,
-                threads.camera2Handler
+                captureSessionState
             )
         }
         if (!result) {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
index 47b388f..93fb66d 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/CaptureSessionWrapper.kt
@@ -66,59 +66,47 @@
     /**
      * @param request The settings for this exposure
      * @param listener The callback object to notify once this request has been processed.
-     * @param handler The handler on which the listener should be invoked, or null to use the
-     *   current thread's looper.
      * @return An unique capture sequence id.
      * @see [CameraCaptureSession.capture].
      */
     fun capture(
         request: CaptureRequest,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int?
 
     /**
      * @param requests A list of CaptureRequest(s) for this sequence of exposures
      * @param listener A callback object to notify each time one of the requests in the burst has
      *   been processed.
-     * @param handler The handler on which the listener should be invoked, or null to use the
-     *   current thread's looper.
      * @return An unique capture sequence id.
      * @see [CameraCaptureSession.captureBurst].
      */
     fun captureBurst(
         requests: List<CaptureRequest>,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int?
 
     /**
      * @param requests A list of settings to cycle through indefinitely.
      * @param listener A callback object to notify each time one of the requests in the repeating
      *   bursts has finished processing.
-     * @param handler The handler on which the listener should be invoked, or null to use the
-     *   current thread's looper.
      * @return An unique capture sequence ID.
      * @see [CameraCaptureSession.setRepeatingBurst]
      */
     fun setRepeatingBurst(
         requests: List<CaptureRequest>,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int?
 
     /**
      * @param request The request to repeat indefinitely.
      * @param listener The callback object to notify every time the request finishes processing.
-     * @param handler The handler on which the listener should be invoked, or null to use the
-     *   current thread's looper.
      * @return An unique capture sequence ID.
      * @see [CameraCaptureSession.setRepeatingRequest].
      */
     fun setRepeatingRequest(
         request: CaptureRequest,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int?
 
     /** @see [CameraCaptureSession.stopRepeating]. */
@@ -166,7 +154,8 @@
     private val stateCallback: CameraCaptureSessionWrapper.StateCallback,
     lastStateCallback: OnSessionFinalized?,
     private val cameraErrorListener: CameraErrorListener,
-    private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null
+    private val interopSessionStateCallback: CameraCaptureSession.StateCallback? = null,
+    private val callbackHandler: Handler
 ) : CameraCaptureSession.StateCallback() {
     private val _lastStateCallback = atomic(lastStateCallback)
     private val captureSession = atomic<CameraCaptureSessionWrapper?>(null)
@@ -237,9 +226,11 @@
         return if (Build.VERSION.SDK_INT >= 23 &&
             session is CameraConstrainedHighSpeedCaptureSession
         ) {
-            AndroidCameraConstrainedHighSpeedCaptureSession(device, session, cameraErrorListener)
+            AndroidCameraConstrainedHighSpeedCaptureSession(
+                device, session, cameraErrorListener, callbackHandler
+            )
         } else {
-            AndroidCameraCaptureSession(device, session, cameraErrorListener)
+            AndroidCameraCaptureSession(device, session, cameraErrorListener, callbackHandler)
         }
     }
 
@@ -272,6 +263,7 @@
     override val device: CameraDeviceWrapper,
     private val cameraCaptureSession: CameraCaptureSession,
     private val cameraErrorListener: CameraErrorListener,
+    private val callbackHandler: Handler
 ) : CameraCaptureSessionWrapper {
     override fun abortCaptures(): Boolean =
         catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
@@ -280,38 +272,34 @@
 
     override fun capture(
         request: CaptureRequest,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
         cameraCaptureSession.capture(
             request,
             listener,
-            handler
+            callbackHandler
         )
     }
 
     override fun captureBurst(
         requests: List<CaptureRequest>,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
-        cameraCaptureSession.captureBurst(requests, listener, handler)
+        cameraCaptureSession.captureBurst(requests, listener, callbackHandler)
     }
 
     override fun setRepeatingBurst(
         requests: List<CaptureRequest>,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
-        cameraCaptureSession.setRepeatingBurst(requests, listener, handler)
+        cameraCaptureSession.setRepeatingBurst(requests, listener, callbackHandler)
     }
 
     override fun setRepeatingRequest(
         request: CaptureRequest,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
-        cameraCaptureSession.setRepeatingRequest(request, listener, handler)
+        cameraCaptureSession.setRepeatingRequest(request, listener, callbackHandler)
     }
 
     override fun stopRepeating(): Boolean =
@@ -377,7 +365,8 @@
     device: CameraDeviceWrapper,
     private val session: CameraConstrainedHighSpeedCaptureSession,
     private val cameraErrorListener: CameraErrorListener,
-) : AndroidCameraCaptureSession(device, session, cameraErrorListener),
+    private val callbackHandler: Handler
+) : AndroidCameraCaptureSession(device, session, cameraErrorListener, callbackHandler),
     CameraConstrainedHighSpeedCaptureSessionWrapper {
     @Throws(ObjectUnavailableException::class)
     override fun createHighSpeedRequestList(request: CaptureRequest): List<CaptureRequest> {
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
index e75ce9a..d3b8f17 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/ExtensionSessionWrapper.kt
@@ -18,13 +18,21 @@
 
 package androidx.camera.camera2.pipe.compat
 
+import android.hardware.camera2.CameraCaptureSession
 import android.hardware.camera2.CameraExtensionSession
 import android.hardware.camera2.CaptureRequest
+import android.hardware.camera2.TotalCaptureResult
+import android.view.Surface
 import androidx.annotation.RequiresApi
+import androidx.camera.camera2.pipe.FrameNumber
 import androidx.camera.camera2.pipe.UnsafeWrapper
+import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.internal.CameraErrorListener
+import java.util.LinkedList
+import java.util.Queue
 import java.util.concurrent.Executor
 import kotlin.reflect.KClass
+import kotlinx.atomicfu.AtomicLong
 import kotlinx.atomicfu.atomic
 
 /**
@@ -33,44 +41,17 @@
  * This interface has been modified to correct nullness, adjust exceptions, and to return or produce
  * wrapper interfaces instead of the native Camera2 types.
  */
-internal interface CameraExtensionSessionWrapper : UnsafeWrapper, AutoCloseable {
+internal interface CameraExtensionSessionWrapper : CameraCaptureSessionWrapper, UnsafeWrapper,
+    AutoCloseable {
 
     /**
      * @return The [CameraDeviceWrapper] that created this CameraExtensionSession
      * @see [CameraExtensionSession.getDevice]
      */
-    val device: CameraDeviceWrapper
-
-    /**
-     * @param request The settings for this exposure
-     * @param listener The callback object to notify once this request has been processed.
-     * @param executor The executor on which the listener should be invoked, or null to use the
-     *   current thread's looper.
-     * @return An unique capture sequence id.
-     * @see [CameraExtensionSession.capture].
-     */
-    fun capture(
-        request: CaptureRequest,
-        executor: Executor,
-        listener: CameraExtensionSession.ExtensionCaptureCallback
-    ): Int?
-
-    /**
-     * @param request The request to repeat indefinitely.
-     * @param executor The executor on which the listener should be invoked, or null to use the
-     *   current thread's looper.
-     * @param listener The callback object to notify every time the request finishes processing.
-     * @return An unique capture sequence ID.
-     * @see [CameraExtensionSession.setRepeatingRequest].
-     */
-    fun setRepeatingRequest(
-        request: CaptureRequest,
-        executor: Executor,
-        listener: CameraExtensionSession.ExtensionCaptureCallback
-    ): Int?
+    override val device: CameraDeviceWrapper
 
     /** @see [CameraExtensionSession.stopRepeating]. */
-    fun stopRepeating(): Boolean
+    override fun stopRepeating(): Boolean
 
     /** @see CameraExtensionSession.StateCallback */
     interface StateCallback : OnSessionFinalized {
@@ -91,7 +72,8 @@
     private val stateCallback: CameraExtensionSessionWrapper.StateCallback,
     lastStateCallback: OnSessionFinalized?,
     private val cameraErrorListener: CameraErrorListener,
-    private val interopSessionStateCallback: CameraExtensionSession.StateCallback? = null
+    private val interopSessionStateCallback: CameraExtensionSession.StateCallback? = null,
+    private val callbackExecutor: Executor
 ) : CameraExtensionSession.StateCallback() {
     private val _lastStateCallback = atomic(lastStateCallback)
     private val extensionSession = atomic<CameraExtensionSessionWrapper?>(null)
@@ -139,7 +121,7 @@
         session: CameraExtensionSession,
         cameraErrorListener: CameraErrorListener,
     ): CameraExtensionSessionWrapper {
-        return AndroidCameraExtensionSession(device, session, cameraErrorListener)
+        return AndroidCameraExtensionSession(device, session, cameraErrorListener, callbackExecutor)
     }
 
     private fun finalizeSession() {
@@ -159,25 +141,40 @@
     override val device: CameraDeviceWrapper,
     private val cameraExtensionSession: CameraExtensionSession,
     private val cameraErrorListener: CameraErrorListener,
+    private val callbackExecutor: Executor
 ) : CameraExtensionSessionWrapper {
+
+    private val frameNumbers: AtomicLong = atomic(0L)
+    private val frameNumbersMap: MutableMap<CameraExtensionSession, Long> = HashMap()
+
     override fun capture(
         request: CaptureRequest,
-        executor: Executor,
-        listener: CameraExtensionSession.ExtensionCaptureCallback
+        listener: CameraCaptureSession.CaptureCallback
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
+        val frameQueue = LinkedList<Long>()
         cameraExtensionSession.capture(
             request,
-            executor,
-            listener,
+            callbackExecutor,
+            Camera2CaptureSessionCallbackToExtensionCaptureCallback(
+                listener as Camera2CaptureCallback,
+                frameQueue
+            )
         )
     }
 
     override fun setRepeatingRequest(
         request: CaptureRequest,
-        executor: Executor,
-        listener: CameraExtensionSession.ExtensionCaptureCallback,
+        listener: CameraCaptureSession.CaptureCallback,
     ): Int? = catchAndReportCameraExceptions(device.cameraId, cameraErrorListener) {
-        cameraExtensionSession.setRepeatingRequest(request, executor, listener)
+        val frameQueue = LinkedList<Long>()
+        cameraExtensionSession.setRepeatingRequest(
+            request,
+            callbackExecutor,
+            Camera2CaptureSessionCallbackToExtensionCaptureCallback(
+                listener as Camera2CaptureCallback,
+                frameQueue
+            )
+        )
     }
 
     override fun stopRepeating(): Boolean =
@@ -185,6 +182,40 @@
             cameraExtensionSession.stopRepeating()
         } != null
 
+    override val isReprocessable: Boolean
+        get() = false
+
+    override val inputSurface: Surface?
+        get() = null
+
+    override fun abortCaptures(): Boolean = false
+
+    override fun captureBurst(
+        requests: List<CaptureRequest>,
+        listener: CameraCaptureSession.CaptureCallback
+    ): Int? {
+        requests.forEach { captureRequest -> capture(captureRequest, listener) }
+        return null
+    }
+
+    override fun setRepeatingBurst(
+        requests: List<CaptureRequest>,
+        listener: CameraCaptureSession.CaptureCallback
+    ): Int? {
+        check(requests.size == 1) {
+            "CameraExtensionSession does not support setRepeatingBurst for more than one" +
+                "CaptureRequest"
+        }
+        return setRepeatingRequest(requests.single(), listener)
+    }
+
+    override fun finalizeOutputConfigurations(
+        outputConfigs: List<OutputConfigurationWrapper>
+    ): Boolean {
+        Log.warn { "CameraExtensionSession does not support finalizeOutputConfigurations()" }
+        return false
+    }
+
     @Suppress("UNCHECKED_CAST")
     override fun <T : Any> unwrapAs(type: KClass<T>): T? =
         when (type) {
@@ -195,4 +226,57 @@
     override fun close() {
         return cameraExtensionSession.close()
     }
+
+    inner class Camera2CaptureSessionCallbackToExtensionCaptureCallback(
+        private val captureCallback: Camera2CaptureCallback,
+        private val frameQueue: Queue<Long>
+    ) : CameraExtensionSession.ExtensionCaptureCallback() {
+
+        override fun onCaptureStarted(
+            session: CameraExtensionSession,
+            request: CaptureRequest,
+            timestamp: Long
+        ) {
+            val frameNumber = frameNumbers.incrementAndGet()
+            frameNumbersMap.getOrDefault(session, frameNumber)
+            frameQueue.add(frameNumber)
+            captureCallback.onCaptureStarted(
+                request,
+                frameNumber,
+                timestamp
+            )
+        }
+
+        override fun onCaptureProcessStarted(
+            session: CameraExtensionSession,
+            request: CaptureRequest
+        ) {
+        }
+
+        override fun onCaptureFailed(session: CameraExtensionSession, request: CaptureRequest) {
+            val frameNumber = frameQueue.remove()
+            captureCallback.onCaptureFailed(
+                request,
+                FrameNumber(frameNumber)
+            )
+        }
+
+        override fun onCaptureSequenceCompleted(session: CameraExtensionSession, sequenceId: Int) {
+            val frameNumber = frameNumbersMap[session]
+            captureCallback.onCaptureSequenceCompleted(sequenceId, frameNumber!!)
+        }
+
+        override fun onCaptureSequenceAborted(session: CameraExtensionSession, sequenceId: Int) {
+            captureCallback.onCaptureSequenceAborted(sequenceId)
+        }
+
+        override fun onCaptureResultAvailable(
+            session: CameraExtensionSession,
+            request: CaptureRequest,
+            result: TotalCaptureResult
+        ) {
+            val frameNumber = frameQueue.remove()
+            captureCallback.onCaptureCompleted(request, result, FrameNumber(frameNumber))
+        }
+    }
 }
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
index a5d46ea..c12e597 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpener.kt
@@ -166,7 +166,8 @@
     private val cameraErrorListener: CameraErrorListener,
     private val camera2DeviceCloser: Camera2DeviceCloser,
     private val timeSource: TimeSource,
-    private val cameraInteropConfig: CameraPipe.CameraInteropConfig?
+    private val cameraInteropConfig: CameraPipe.CameraInteropConfig?,
+    private val threads: Threads
 ) {
     internal suspend fun tryOpenCamera(
         cameraId: CameraId,
@@ -183,6 +184,7 @@
                 timeSource,
                 cameraErrorListener,
                 camera2DeviceCloser,
+                threads,
                 cameraInteropConfig?.cameraDeviceStateCallback,
                 cameraInteropConfig?.cameraSessionStateCallback
             )
diff --git a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
index 05cc5eb..456e87f 100644
--- a/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
+++ b/camera/camera-camera2-pipe/src/main/java/androidx/camera/camera2/pipe/compat/VirtualCamera.kt
@@ -31,6 +31,7 @@
 import androidx.camera.camera2.pipe.core.DurationNs
 import androidx.camera.camera2.pipe.core.Log
 import androidx.camera.camera2.pipe.core.SystemTimeSource
+import androidx.camera.camera2.pipe.core.Threads
 import androidx.camera.camera2.pipe.core.TimeSource
 import androidx.camera.camera2.pipe.core.TimestampNs
 import androidx.camera.camera2.pipe.core.Timestamps
@@ -248,6 +249,7 @@
     private val timeSource: TimeSource,
     private val cameraErrorListener: CameraErrorListener,
     private val camera2DeviceCloser: Camera2DeviceCloser,
+    private val threads: Threads,
     private val interopDeviceStateCallback: CameraDevice.StateCallback? = null,
     private val interopSessionStateCallback: StateCallback? = null,
     private val interopExtensionSessionStateCallback: CameraExtensionSession.StateCallback? = null
@@ -347,7 +349,8 @@
                     cameraId,
                     cameraErrorListener,
                     interopSessionStateCallback,
-                    interopExtensionSessionStateCallback
+                    interopExtensionSessionStateCallback,
+                    threads
                 )
             )
 
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt
index 6b0573a..9ed1ce8 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/AndroidCaptureSessionStateCallbackTest.kt
@@ -18,6 +18,7 @@
 
 import android.hardware.camera2.CameraCaptureSession
 import android.os.Build
+import android.os.Handler
 import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
 import org.junit.Test
@@ -37,12 +38,14 @@
     private val previousStateCallback: CameraCaptureSessionWrapper.StateCallback = mock()
     private val captureSession: CameraCaptureSession = mock()
     private val cameraErrorListener: CameraErrorListener = mock()
+    private val callbackHandler: Handler = mock()
     private val androidStateCallback =
         AndroidCaptureSessionStateCallback(
             device = camera,
             stateCallback = stateCallback,
             lastStateCallback = previousStateCallback,
             cameraErrorListener = cameraErrorListener,
+            callbackHandler = callbackHandler
         )
 
     @Test
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
index 7cafd66..dcf8a14 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/CaptureSessionFactoryTest.kt
@@ -47,6 +47,7 @@
 import androidx.camera.camera2.pipe.testing.FakeCaptureSequence
 import androidx.camera.camera2.pipe.testing.FakeCaptureSequenceProcessor
 import androidx.camera.camera2.pipe.testing.FakeGraphProcessor
+import androidx.camera.camera2.pipe.testing.FakeThreads
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
 import androidx.camera.camera2.pipe.testing.RobolectricCameras
 import androidx.test.core.app.ApplicationProvider
@@ -116,7 +117,8 @@
                     testCamera.metadata,
                     testCamera.cameraDevice,
                     testCamera.cameraId,
-                    cameraErrorListener
+                    cameraErrorListener,
+                    threads = FakeThreads.fromTestScope(this)
                 ),
                 mapOf(stream1.id to surface),
                 captureSessionState =
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
index c6f20ef..7c5d185 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/RetryingCameraStateOpenerTest.kt
@@ -35,12 +35,14 @@
 import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.FakeCamera2DeviceCloser
 import androidx.camera.camera2.pipe.testing.FakeCameraMetadata
+import androidx.camera.camera2.pipe.testing.FakeThreads
 import androidx.camera.camera2.pipe.testing.FakeTimeSource
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
 import com.google.common.truth.Truth.assertThat
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import kotlinx.coroutines.async
 import kotlinx.coroutines.delay
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceTimeBy
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
@@ -101,6 +103,7 @@
             cameraDeviceCloser,
             fakeTimeSource,
             cameraInteropConfig = null,
+            FakeThreads.fromTestScope(TestScope())
         )
 
     private val cameraAvailabilityMonitor =
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt
index a77eec4..eda65e8 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/compat/VirtualCameraTest.kt
@@ -31,6 +31,7 @@
 import androidx.camera.camera2.pipe.graph.GraphListener
 import androidx.camera.camera2.pipe.internal.CameraErrorListener
 import androidx.camera.camera2.pipe.testing.FakeCamera2DeviceCloser
+import androidx.camera.camera2.pipe.testing.FakeThreads
 import androidx.camera.camera2.pipe.testing.RobolectricCameraPipeTestRunner
 import androidx.camera.camera2.pipe.testing.RobolectricCameras
 import com.google.common.truth.Truth.assertThat
@@ -41,6 +42,7 @@
 import kotlinx.coroutines.flow.first
 import kotlinx.coroutines.flow.flowOf
 import kotlinx.coroutines.launch
+import kotlinx.coroutines.test.TestScope
 import kotlinx.coroutines.test.advanceUntilIdle
 import kotlinx.coroutines.test.runTest
 import org.junit.After
@@ -105,6 +107,7 @@
                         testCamera.cameraDevice,
                         testCamera.cameraId,
                         cameraErrorListener,
+                        threads = FakeThreads.fromTestScope(this)
                     )
                 )
             )
@@ -137,6 +140,7 @@
             testCamera.cameraDevice,
             testCamera.cameraId,
             cameraErrorListener,
+            threads = FakeThreads.fromTestScope(this)
         )
         val cameraStateClosing = CameraStateClosing()
         val cameraStateClosed =
@@ -190,6 +194,7 @@
                         testCamera.cameraDevice,
                         testCamera.cameraId,
                         cameraErrorListener,
+                        threads = FakeThreads.fromTestScope(this)
                     )
                 )
             )
@@ -227,6 +232,7 @@
                         testCamera.cameraDevice,
                         testCamera.cameraId,
                         cameraErrorListener,
+                        threads = FakeThreads.fromTestScope(this)
                     )
                 )
             )
@@ -251,7 +257,7 @@
         val surfaceTexture = SurfaceTexture(0).also { it.setDefaultBufferSize(640, 480) }
         val surface = Surface(surfaceTexture)
         val callback: CameraCaptureSessionWrapper.StateCallback = mock()
-        val result = virtualAndroidCameraState.createCaptureSession(listOf(surface), callback, null)
+        val result = virtualAndroidCameraState.createCaptureSession(listOf(surface), callback)
         assertThat(result).isFalse()
         verify(callback, times(1)).onSessionFinalized()
         surface.release()
@@ -261,6 +267,7 @@
 
 @RunWith(RobolectricCameraPipeTestRunner::class)
 @Config(minSdk = Build.VERSION_CODES.LOLLIPOP)
+@OptIn(ExperimentalCoroutinesApi::class)
 internal class AndroidCameraDeviceTest {
     private val mainLooper = shadowOf(getMainLooper())
     private val cameraId = RobolectricCameras.create()
@@ -299,6 +306,7 @@
                 timeSource,
                 cameraErrorListener,
                 cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         assertThat(listener.state.value).isInstanceOf(CameraStateUnopened.javaClass)
@@ -346,6 +354,7 @@
                 timeSource,
                 cameraErrorListener,
                 cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         listener.onDisconnected(testCamera.cameraDevice)
@@ -369,6 +378,7 @@
                 timeSource,
                 cameraErrorListener,
                 cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         listener.close()
@@ -389,6 +399,7 @@
                 timeSource,
                 cameraErrorListener,
                 cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         listener.closeWith(IllegalArgumentException("Test Exception"))
@@ -409,6 +420,7 @@
                 timeSource,
                 cameraErrorListener,
                 cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         listener.onError(testCamera.cameraDevice, CameraDevice.StateCallback.ERROR_CAMERA_SERVICE)
@@ -431,6 +443,7 @@
                 timeSource,
                 cameraErrorListener,
                 cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         listener.onOpened(testCamera.cameraDevice)
@@ -453,7 +466,8 @@
                 attemptTimestampNanos = now,
                 timeSource,
                 cameraErrorListener,
-                cameraDeviceCloser
+                cameraDeviceCloser,
+                FakeThreads.fromTestScope(TestScope())
             )
 
         listener.onError(testCamera.cameraDevice, CameraDevice.StateCallback.ERROR_CAMERA_SERVICE)
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
index f46885f..0fdd447 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCameraDeviceWrapper.kt
@@ -21,7 +21,6 @@
 import android.hardware.camera2.TotalCaptureResult
 import android.hardware.camera2.params.InputConfiguration
 import android.os.Build
-import android.os.Handler
 import android.view.Surface
 import androidx.annotation.RequiresApi
 import androidx.camera.camera2.pipe.CameraId
@@ -63,8 +62,7 @@
 
     override fun createCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean {
         createFakeCaptureSession(stateCallback)
         return true
@@ -78,8 +76,7 @@
     override fun createReprocessableCaptureSession(
         input: InputConfiguration,
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean {
         createFakeCaptureSession(stateCallback)
         return true
@@ -87,8 +84,7 @@
 
     override fun createConstrainedHighSpeedCaptureSession(
         outputs: List<Surface>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean {
         createFakeCaptureSession(stateCallback)
         return true
@@ -96,8 +92,7 @@
 
     override fun createCaptureSessionByOutputConfigurations(
         outputConfigurations: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean {
         createFakeCaptureSession(stateCallback)
         return true
@@ -106,8 +101,7 @@
     override fun createReprocessableCaptureSessionByConfigurations(
         inputConfig: InputConfigData,
         outputs: List<OutputConfigurationWrapper>,
-        stateCallback: CameraCaptureSessionWrapper.StateCallback,
-        handler: Handler?
+        stateCallback: CameraCaptureSessionWrapper.StateCallback
     ): Boolean {
         createFakeCaptureSession(stateCallback)
         return true
diff --git a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt
index bc4d75d..1b63efa 100644
--- a/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt
+++ b/camera/camera-camera2-pipe/src/test/java/androidx/camera/camera2/pipe/testing/FakeCaptureSessionWrapper.kt
@@ -18,7 +18,6 @@
 
 import android.hardware.camera2.CameraCaptureSession
 import android.hardware.camera2.CaptureRequest
-import android.os.Handler
 import android.view.Surface
 import androidx.camera.camera2.pipe.compat.CameraCaptureSessionWrapper
 import androidx.camera.camera2.pipe.compat.CameraDeviceWrapper
@@ -50,8 +49,7 @@
 
     override fun capture(
         request: CaptureRequest,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int {
         lastCapture = listOf(request)
         lastCaptureCallback = listener
@@ -62,8 +60,7 @@
 
     override fun captureBurst(
         requests: List<CaptureRequest>,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int {
         lastCapture = requests.toList()
         lastCaptureCallback = listener
@@ -74,8 +71,7 @@
 
     override fun setRepeatingBurst(
         requests: List<CaptureRequest>,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int {
         lastRepeating = requests.toList()
         lastRepeatingCallback = listener
@@ -86,8 +82,7 @@
 
     override fun setRepeatingRequest(
         request: CaptureRequest,
-        listener: CameraCaptureSession.CaptureCallback,
-        handler: Handler?
+        listener: CameraCaptureSession.CaptureCallback
     ): Int {
         lastRepeating = listOf(request)
         lastRepeatingCallback = listener