From d004e93c010139219c1409f333dd2b27aea4e302 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 14 Apr 2026 16:26:38 +0200 Subject: [PATCH 1/8] chore: cleanup for android on bridge reload --- .../oney/WebRTCModule/GetUserMediaImpl.java | 13 ++++++ .../com/oney/WebRTCModule/WebRTCModule.java | 40 +++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java index c17254a8b..55fc3ae6d 100644 --- a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java +++ b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java @@ -285,6 +285,17 @@ void mediaStreamTrackSetEnabled(String trackId, final boolean enabled) { } } + void disposeAllTracks() { + for (Map.Entry entry : tracks.entrySet()) { + try { + entry.getValue().dispose(); + } catch (Exception e) { + Log.w(TAG, "disposeAllTracks: error disposing " + entry.getKey(), e); + } + } + tracks.clear(); + } + void disposeTrack(String id) { TrackPrivate track = tracks.remove(id); if (track != null) { @@ -344,6 +355,8 @@ public void run() { } private void createScreenStream() { + // Guards against onServiceConnected firing after invalidate() has disposed and nulled mFactory. + if (webRTCModule.mFactory == null) return; VideoTrack track = createScreenTrack(); if (track == null) { diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java index 089f6150a..2787bb468 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java @@ -124,6 +124,46 @@ public WebRTCModule(ReactApplicationContext reactContext) { getUserMediaImpl = new GetUserMediaImpl(this, reactContext); } + @Override + public void invalidate() { + Log.d(TAG, "invalidate()"); + + try { + ThreadUtils.submitToExecutor(() -> { + // 1. Dispose PeerConnections (dispose() calls close() internally) + for (int i = 0; i < mPeerConnectionObservers.size(); i++) { + try { + mPeerConnectionObservers.valueAt(i).dispose(); + } catch (Exception e) { + Log.w(TAG, "invalidate(): error disposing PC " + mPeerConnectionObservers.keyAt(i), e); + } + } + mPeerConnectionObservers.clear(); + + // 2. Stop capturers + dispose tracks (prevents use-after-free on factory threads) + getUserMediaImpl.disposeAllTracks(); + + // 3. Dispose local streams + for (MediaStream stream : localStreams.values()) { + stream.dispose(); + } + localStreams.clear(); + + // 4. Dispose factory (frees C++ factory + 3 threads) + if (mFactory != null) { + mFactory.dispose(); + mFactory = null; + } + + return null; + }).get(); + } catch (InterruptedException | ExecutionException e) { + Log.e(TAG, "invalidate() error", e); + } + + super.invalidate(); + } + private JavaAudioDeviceModule createAudioDeviceModule(ReactApplicationContext reactContext) { speechActivityDetector = new SpeechActivityDetector(new SpeechActivityDetector.Listener() { @Override From 11501b03bdcb89166d35e656185ec07c2c3c62ef Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Tue, 14 Apr 2026 16:50:49 +0200 Subject: [PATCH 2/8] cancel timer also --- .../main/java/com/oney/WebRTCModule/GetUserMediaImpl.java | 8 +++++++- .../com/oney/WebRTCModule/PeerConnectionObserver.java | 2 ++ .../java/com/oney/WebRTCModule/VideoTrackAdapter.java | 4 ++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java index 55fc3ae6d..f224165c1 100644 --- a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java +++ b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java @@ -356,7 +356,13 @@ public void run() { private void createScreenStream() { // Guards against onServiceConnected firing after invalidate() has disposed and nulled mFactory. - if (webRTCModule.mFactory == null) return; + if (webRTCModule.mFactory == null) { + if (displayMediaPromise != null) { + displayMediaPromise.reject("ERR_MODULE_DISPOSED", "WebRTCModule disposed during getDisplayMedia"); + displayMediaPromise = null; + } + return; + } VideoTrack track = createScreenTrack(); if (track == null) { diff --git a/android/src/main/java/com/oney/WebRTCModule/PeerConnectionObserver.java b/android/src/main/java/com/oney/WebRTCModule/PeerConnectionObserver.java index 32b028f82..6ba19a041 100644 --- a/android/src/main/java/com/oney/WebRTCModule/PeerConnectionObserver.java +++ b/android/src/main/java/com/oney/WebRTCModule/PeerConnectionObserver.java @@ -99,6 +99,8 @@ void dispose() { // by the PeerConnection instance (RtpReceivers, RtpSenders, etc.) peerConnection.dispose(); + videoTrackAdapters.dispose(); + remoteStreamIds.clear(); remoteStreams.clear(); remoteTracks.clear(); diff --git a/android/src/main/java/com/oney/WebRTCModule/VideoTrackAdapter.java b/android/src/main/java/com/oney/WebRTCModule/VideoTrackAdapter.java index 486bfe6b7..946dfee55 100644 --- a/android/src/main/java/com/oney/WebRTCModule/VideoTrackAdapter.java +++ b/android/src/main/java/com/oney/WebRTCModule/VideoTrackAdapter.java @@ -89,6 +89,10 @@ public void removeDimensionDetector(VideoTrack videoTrack) { Log.d(TAG, "Deleted dimension detector for " + trackId); } + void dispose() { + timer.cancel(); + } + /** * Implements 'mute'/'unmute' events for remote video tracks through * the {@link VideoSink} interface. From 6c593e05d0faeadf09e810bd82ef74fe0f0bb711 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 15 Apr 2026 10:15:44 +0200 Subject: [PATCH 3/8] coderabbit review fix --- .../src/main/java/com/oney/WebRTCModule/WebRTCModule.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java index 2787bb468..d416b4ac4 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java @@ -144,8 +144,12 @@ public void invalidate() { getUserMediaImpl.disposeAllTracks(); // 3. Dispose local streams - for (MediaStream stream : localStreams.values()) { - stream.dispose(); + for (Map.Entry entry : localStreams.entrySet()) { + try { + entry.getValue().dispose(); + } catch (Exception e) { + Log.w(TAG, "invalidate(): error disposing stream " + entry.getKey(), e); + } } localStreams.clear(); From aeb2bf304172161b2dc22391bf3bcdaa4b2811fc Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 15 Apr 2026 10:38:12 +0200 Subject: [PATCH 4/8] 137.1.4-alpha.6 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8f3a70152..be544cac8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.5", + "version": "137.1.4-alpha.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.5", + "version": "137.1.4-alpha.6", "license": "MIT", "dependencies": { "base64-js": "1.5.1", diff --git a/package.json b/package.json index ba8cc44c3..2c81622ca 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.5", + "version": "137.1.4-alpha.6", "repository": { "type": "git", "url": "git+https://github.com/GetStream/react-native-webrtc.git" From c95abaa24d72b724305582e9fa9cee9560c8c120 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 15 Apr 2026 11:18:23 +0200 Subject: [PATCH 5/8] dispose stream then track --- .../java/com/oney/WebRTCModule/WebRTCModule.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java index d416b4ac4..d2cf9ea40 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java @@ -140,19 +140,22 @@ public void invalidate() { } mPeerConnectionObservers.clear(); - // 2. Stop capturers + dispose tracks (prevents use-after-free on factory threads) - getUserMediaImpl.disposeAllTracks(); - - // 3. Dispose local streams + // 2. Detach tracks, then dispose streams. Tracks themselves get disposed in step 3. for (Map.Entry entry : localStreams.entrySet()) { try { - entry.getValue().dispose(); + MediaStream stream = entry.getValue(); + for (AudioTrack t : new ArrayList<>(stream.audioTracks)) stream.removeTrack(t); + for (VideoTrack t : new ArrayList<>(stream.videoTracks)) stream.removeTrack(t); + stream.dispose(); } catch (Exception e) { Log.w(TAG, "invalidate(): error disposing stream " + entry.getKey(), e); } } localStreams.clear(); + // 3. Stop capturers + dispose tracks (prevents use-after-free on factory threads) + getUserMediaImpl.disposeAllTracks(); + // 4. Dispose factory (frees C++ factory + 3 threads) if (mFactory != null) { mFactory.dispose(); From df36d6c3c921a17415a33ef82c7af21ca1faa0f5 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 15 Apr 2026 11:18:57 +0200 Subject: [PATCH 6/8] 137.1.4-alpha.7 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index be544cac8..f1b0063ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.6", + "version": "137.1.4-alpha.7", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.6", + "version": "137.1.4-alpha.7", "license": "MIT", "dependencies": { "base64-js": "1.5.1", diff --git a/package.json b/package.json index 2c81622ca..9aa49d8fd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.6", + "version": "137.1.4-alpha.7", "repository": { "type": "git", "url": "git+https://github.com/GetStream/react-native-webrtc.git" From 4bf4f73f7b562571d7b59a8fa4058e38ee9d5fe5 Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 15 Apr 2026 11:53:15 +0200 Subject: [PATCH 7/8] cleanup adapter for cloned track --- .../main/java/com/oney/WebRTCModule/GetUserMediaImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java index f224165c1..731ee8e5c 100644 --- a/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java +++ b/android/src/main/java/com/oney/WebRTCModule/GetUserMediaImpl.java @@ -633,9 +633,10 @@ public void dispose() { } } - // Clean up VideoTrackAdapter for video tracks - if (!isClone && videoTrackAdapter != null && track instanceof VideoTrack) { + // Clean up VideoTrackAdapter for video tracks (each TrackPrivate, incl. clones, has its own) + if (videoTrackAdapter != null && track instanceof VideoTrack) { videoTrackAdapter.removeDimensionDetector((VideoTrack) track); + videoTrackAdapter.dispose(); } /* From 9594dd3b73846999f144ed8d28b4bf5569388b9d Mon Sep 17 00:00:00 2001 From: Santhosh Vaiyapuri Date: Wed, 15 Apr 2026 11:53:33 +0200 Subject: [PATCH 8/8] 137.1.4-alpha.8 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index f1b0063ba..8ef8e5ddd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.7", + "version": "137.1.4-alpha.8", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.7", + "version": "137.1.4-alpha.8", "license": "MIT", "dependencies": { "base64-js": "1.5.1", diff --git a/package.json b/package.json index 9aa49d8fd..84608304c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@stream-io/react-native-webrtc", - "version": "137.1.4-alpha.7", + "version": "137.1.4-alpha.8", "repository": { "type": "git", "url": "git+https://github.com/GetStream/react-native-webrtc.git"