From 2b3138b7d5e29ef5acfcb2f501a61a28a249469a Mon Sep 17 00:00:00 2001 From: Adrian Niculescu <15037449+adrian-niculescu@users.noreply.github.com> Date: Tue, 25 Nov 2025 00:56:38 +0200 Subject: [PATCH] Fixed data race in CameraEventsDispatchHandler --- .changeset/fix-camera-events-dispatch-race.md | 5 ++++ .../video/CameraEventsDispatchHandler.kt | 25 +++++++------------ 2 files changed, 14 insertions(+), 16 deletions(-) create mode 100644 .changeset/fix-camera-events-dispatch-race.md diff --git a/.changeset/fix-camera-events-dispatch-race.md b/.changeset/fix-camera-events-dispatch-race.md new file mode 100644 index 000000000..0b9fd9d0b --- /dev/null +++ b/.changeset/fix-camera-events-dispatch-race.md @@ -0,0 +1,5 @@ +--- +"client-sdk-android": patch +--- + +Fixed data race in `CameraEventsDispatchHandler` by using `CopyOnWriteArraySet` for thread-safe iteration. \ No newline at end of file diff --git a/livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraEventsDispatchHandler.kt b/livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraEventsDispatchHandler.kt index b87287d7a..d2f57f834 100644 --- a/livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraEventsDispatchHandler.kt +++ b/livekit-android-sdk/src/main/java/io/livekit/android/room/track/video/CameraEventsDispatchHandler.kt @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 LiveKit, Inc. + * Copyright 2023-2025 LiveKit, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,61 +17,54 @@ package io.livekit.android.room.track.video import livekit.org.webrtc.CameraVideoCapturer.CameraEventsHandler +import java.util.concurrent.CopyOnWriteArraySet /** * Dispatches CameraEventsHandler callbacks to registered handlers. */ class CameraEventsDispatchHandler : CameraEventsHandler { - private val handlers = mutableSetOf() + private val handlers = CopyOnWriteArraySet() - @Synchronized fun registerHandler(handler: CameraEventsHandler) { handlers.add(handler) } - @Synchronized fun unregisterHandler(handler: CameraEventsHandler) { handlers.remove(handler) } override fun onCameraError(errorDescription: String) { - val handlersCopy = handlers.toMutableSet() - for (handler in handlersCopy) { + for (handler in handlers) { handler.onCameraError(errorDescription) } } override fun onCameraDisconnected() { - val handlersCopy = handlers.toMutableSet() - for (handler in handlersCopy) { + for (handler in handlers) { handler.onCameraDisconnected() } } override fun onCameraFreezed(errorDescription: String) { - val handlersCopy = handlers.toMutableSet() - for (handler in handlersCopy) { + for (handler in handlers) { handler.onCameraFreezed(errorDescription) } } override fun onCameraOpening(cameraName: String) { - val handlersCopy = handlers.toMutableSet() - for (handler in handlersCopy) { + for (handler in handlers) { handler.onCameraOpening(cameraName) } } override fun onFirstFrameAvailable() { - val handlersCopy = handlers.toMutableSet() - for (handler in handlersCopy) { + for (handler in handlers) { handler.onFirstFrameAvailable() } } override fun onCameraClosed() { - val handlersCopy = handlers.toMutableSet() - for (handler in handlersCopy) { + for (handler in handlers) { handler.onCameraClosed() } }