diff --git a/tools/chatgui-android/video-tests/src/main/kotlin/android/hardware/camera2/HostCamera2.kt b/tools/chatgui-android/video-tests/src/main/kotlin/android/hardware/camera2/HostCamera2.kt index 4a7755cd..a74dd65e 100644 --- a/tools/chatgui-android/video-tests/src/main/kotlin/android/hardware/camera2/HostCamera2.kt +++ b/tools/chatgui-android/video-tests/src/main/kotlin/android/hardware/camera2/HostCamera2.kt @@ -6,6 +6,7 @@ import android.util.Size import android.util.Range import android.view.Surface import java.util.concurrent.CopyOnWriteArrayList +import java.util.concurrent.ConcurrentLinkedQueue class CameraAccessException(val reason: Int) : Exception("camera access failure reason=$reason") { companion object { const val CAMERA_IN_USE = 4; const val CAMERA_DISABLED = 1; const val CAMERA_DISCONNECTED = 2 } @@ -51,6 +52,7 @@ class CameraManager { var failures = emptySet() var nextDeviceFailures = emptySet() var nextSessionFailures = emptySet() + val sessionFailures = ConcurrentLinkedQueue>() var autoOpened = true var autoClosed = true var autoConfigured = true @@ -86,7 +88,7 @@ class CameraDevice(val id: String, private val owner: CameraManager, private val fun createCaptureRequest(type: Int): CaptureRequest.Builder { check("request" !in failures); return CaptureRequest.Builder() } fun createCaptureSession(targets: List, callback: CameraCaptureSession.StateCallback, delivery: Handler) { check("session" !in failures) { "session creation failed" } - val session = CameraCaptureSession(this, callback, delivery).also { it.failures = owner.nextSessionFailures } + val session = CameraCaptureSession(this, callback, delivery).also { it.failures = owner.sessionFailures.poll() ?: owner.nextSessionFailures } owner.sessions.add(session) if (owner.autoConfigured) delivery.post { if ("configure" in session.failures || "session after" in failures) session.failConfiguration() diff --git a/tools/chatgui-android/video-tests/src/test/kotlin/com/utun/chat/data/CallVideoEngineLifecycleTest.kt b/tools/chatgui-android/video-tests/src/test/kotlin/com/utun/chat/data/CallVideoEngineLifecycleTest.kt index 80c4c18e..901c39b2 100644 --- a/tools/chatgui-android/video-tests/src/test/kotlin/com/utun/chat/data/CallVideoEngineLifecycleTest.kt +++ b/tools/chatgui-android/video-tests/src/test/kotlin/com/utun/chat/data/CallVideoEngineLifecycleTest.kt @@ -168,17 +168,17 @@ class CallVideoEngineLifecycleTest { val engine = engine() val target = engine.preview.attach(android.graphics.SurfaceTexture(1)) try { - manager.nextSessionFailures = setOf("configure") + manager.sessionFailures.add(setOf("configure")) engine.select(engine.state.value.settings.copy(enabled = true)) - await("preview configuration failure reported") { engine.preview.state.value.failed } - manager.nextSessionFailures = emptySet() - await("capture failure stops") { engine.stop() } + await("capture fallback without preview") { manager.sessions.size == 2 && manager.sessions.last().repeating } + assertTrue(engine.state.value.settings.enabled) + await("fallback cleanup completes") { engine.stop() } assertTrue(manager.sessions.all { it.closed }) } finally { engine.preview.detach(target); target.release() } } @Test fun synchronousSessionFailureKeepsPreviewOutputsUntilTheDeviceAcknowledgesClosure() { - manager.nextDeviceFailures = setOf("session after"); manager.autoClosed = false + manager.nextDeviceFailures = setOf("session"); manager.autoClosed = false val engine = engine(); val target = engine.preview.attach(android.graphics.SurfaceTexture(2)) engine.select(engine.state.value.settings.copy(enabled = true)) await("failed session camera close requested") { manager.opened.singleOrNull()?.device?.closed == true }