diff --git a/android/src/main/java/com/oney/WebRTCModule/EncryptionManagerBridge.java b/android/src/main/java/com/oney/WebRTCModule/EncryptionManagerBridge.java index 30e692cd7..55c496b90 100644 --- a/android/src/main/java/com/oney/WebRTCModule/EncryptionManagerBridge.java +++ b/android/src/main/java/com/oney/WebRTCModule/EncryptionManagerBridge.java @@ -292,34 +292,46 @@ private class EventObserver implements EncryptionManager.Observer { @Override public void onE2eeEvent(EncryptionManager.E2eeEvent event) { WritableMap params = Arguments.createMap(); - params.putString("managerId", handle); - params.putString("type", event.type.name); - params.putString("userId", event.userId); - - if (event.trackType != null) { - params.putInt("trackType", event.trackType.getValue()); - } - if (event.keyIndex != null) { - params.putInt("keyIndex", event.keyIndex); - } - if (event.version != null) { - params.putInt("version", event.version); - } - if (event.reason != null) { - params.putString("reason", event.reason); - } - if (event.keyState != null) { - params.putMap("keyState", keyStateToMap(event.keyState)); - } - if (event.encode != null) { - params.putArray("encode", trackPerfToArray(event.encode)); - } - if (event.decode != null) { - params.putArray("decode", trackPerfToArray(event.decode)); + try { + params.putString("managerId", handle); + params.putString("type", event.type.name); + params.putString("userId", event.userId); + + if (event.trackType != null) { + params.putInt("trackType", event.trackType.getValue()); + } + if (event.keyIndex != null) { + params.putInt("keyIndex", event.keyIndex); + } + if (event.version != null) { + params.putInt("version", event.version); + } + if (event.reason != null) { + params.putString("reason", event.reason); + } + if (event.keyState != null) { + params.putMap("keyState", keyStateToMap(event.keyState)); + } + if (event.encode != null) { + params.putArray("encode", trackPerfToArray(event.encode)); + } + if (event.decode != null) { + params.putArray("decode", trackPerfToArray(event.decode)); + } + } catch (RuntimeException e) { + // A broken event is dropped; keys and transforms are not affected. + Log.w(TAG, "onE2eeEvent(): dropping event", e); + return; } // This callback runs on the crypto worker; events must be emitted off it. - ThreadUtils.runOnExecutor(() -> webRTCModule.sendEvent("encryptionManagerEvent", params)); + ThreadUtils.runOnExecutor(() -> { + try { + webRTCModule.sendEvent("encryptionManagerEvent", params); + } catch (RuntimeException e) { + Log.w(TAG, "onE2eeEvent(): dropping event", e); + } + }); } } diff --git a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java index a0b5baf10..5bf9de99e 100644 --- a/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java +++ b/android/src/main/java/com/oney/WebRTCModule/WebRTCModule.java @@ -267,8 +267,9 @@ public void invalidate() { try { ThreadUtils .submitToExecutor(() -> { - // 0. Dispose E2EE managers: their frame transforms are held by the senders and - // receivers of the PeerConnections disposed next. + // 0. Dispose E2EE managers. JS is gone after a reload, so nothing else can + // release them. The order relative to the PeerConnections does not matter + // for safety; on a normal leave the PeerConnections go first. encryptionManagerBridge.disposeAll(); // 1. Dispose PeerConnections (dispose() calls close() internally) @@ -725,17 +726,13 @@ public MediaStreamTrack getTrackById(String trackId) { if (local != null) { return local; } + // Use the track wrapper the observer keeps. Do not use pc.getReceivers() here: + // it disposes the wrappers from its previous call, which detaches their sinks. for (int i = 0, size = mPeerConnectionObservers.size(); i < size; i++) { PeerConnectionObserver pco = mPeerConnectionObservers.valueAt(i); - PeerConnection pc = pco.getPeerConnection(); - if (pc == null) { - continue; - } - for (RtpReceiver receiver : pc.getReceivers()) { - MediaStreamTrack track = receiver.track(); - if (track != null && trackId.equals(track.id())) { - return track; - } + MediaStreamTrack track = pco.remoteTracks.get(trackId); + if (track != null) { + return track; } } return null; diff --git a/ios/RCTWebRTC/WebRTCModule.m b/ios/RCTWebRTC/WebRTCModule.m index f8902de2e..73149496c 100644 --- a/ios/RCTWebRTC/WebRTCModule.m +++ b/ios/RCTWebRTC/WebRTCModule.m @@ -37,8 +37,8 @@ + (BOOL)requiresMainQueueSetup { } - (void)dealloc { - // E2EE managers first: their frame transforms are held by the senders and receivers of the peer - // connections closed below, and nothing in JS survives to dispose them. + // Dispose E2EE managers. JS is gone, so nothing else can release them. The order + // relative to the peer connections does not matter for safety. for (NSString *handle in _encryptionManagers) { RTC_OBJC_TYPE(RTCEncryptionManager) *manager = _encryptionManagers[handle]; manager.delegate = nil; diff --git a/src/RTCEncryptionManager.ts b/src/RTCEncryptionManager.ts index 45f74d17a..541593cdc 100644 --- a/src/RTCEncryptionManager.ts +++ b/src/RTCEncryptionManager.ts @@ -104,6 +104,9 @@ export default class RTCEncryptionManager { * `codec` is an exact lowercase pin (`opus`/`vp8`/`vp9`/`h264`); anything else fails closed. * Omitting it reads the codec from the frame. Omitting `trackType` defaults to audio vs video * from the sender, so screen-share types must be passed explicitly. + * + * Call this before the sender has a track or is negotiated to send. Frames sent + * before the transform is attached go out unencrypted. */ encrypt(sender: RTCRtpSender, codec?: string, trackType?: RTCEncryptionTrackType): void { this._invoke('encryptionManagerEncrypt', {