Skip to content

Commit 5bfb85b

Browse files
committed
fix: wrap full renegotiation sequence in try/catch, update error context, add unawaited
1 parent 39d792d commit 5bfb85b

File tree

3 files changed

+46
-22
lines changed

3 files changed

+46
-22
lines changed

lib/features/call/bloc/call_bloc.dart

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2784,7 +2784,8 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
27842784
onIceCandidate: (candidate) => add(_PeerConnectionEvent.iceCandidateIdentified(callId, candidate)),
27852785
onAddStream: (stream) => add(_PeerConnectionEvent.streamAdded(callId, stream)),
27862786
onRemoveStream: (stream) => add(_PeerConnectionEvent.streamRemoved(callId, stream)),
2787-
onRenegotiationNeeded: (pc) => _renegotiationHandler.handle(callId, lineId, pc, _sendRenegotiationUpdate),
2787+
onRenegotiationNeeded: (pc) =>
2788+
unawaited(_renegotiationHandler.handle(callId, lineId, pc, _sendRenegotiationUpdate)),
27882789
),
27892790
);
27902791
}

lib/features/call/utils/renegotiation_handler.dart

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,26 +23,26 @@ class RenegotiationHandler {
2323
final stateBeforeOffer = peerConnection.signalingState;
2424
_logger.fine(() => 'onRenegotiationNeeded signalingState: $stateBeforeOffer');
2525
if (stateBeforeOffer == RTCSignalingState.RTCSignalingStateStable) {
26-
final localDescription = await peerConnection.createOffer({});
27-
sdpMunger?.apply(localDescription);
28-
29-
final stateAfterOffer = peerConnection.signalingState;
30-
if (stateAfterOffer != RTCSignalingState.RTCSignalingStateStable) {
31-
_logger.fine(
32-
() =>
33-
'onRenegotiationNeeded: state changed to $stateAfterOffer after createOffer, skipping setLocalDescription',
34-
);
35-
return;
36-
}
37-
38-
// According to RFC 8829 5.6 (https://datatracker.ietf.org/doc/html/rfc8829#section-5.6),
39-
// localDescription should be set before sending the offer to transition into have-local-offer state.
40-
await peerConnection.setLocalDescription(localDescription);
41-
4226
try {
27+
final localDescription = await peerConnection.createOffer({});
28+
sdpMunger?.apply(localDescription);
29+
30+
final stateAfterOffer = peerConnection.signalingState;
31+
if (stateAfterOffer != RTCSignalingState.RTCSignalingStateStable) {
32+
_logger.fine(
33+
() =>
34+
'onRenegotiationNeeded: state changed to $stateAfterOffer after createOffer, skipping setLocalDescription',
35+
);
36+
return;
37+
}
38+
39+
// According to RFC 8829 5.6 (https://datatracker.ietf.org/doc/html/rfc8829#section-5.6),
40+
// localDescription should be set before sending the offer to transition into have-local-offer state.
41+
await peerConnection.setLocalDescription(localDescription);
42+
4343
await execute(callId, lineId, localDescription);
4444
} catch (e, s) {
45-
callErrorReporter.handle(e, s, '_createPeerConnection:onRenegotiationNeeded error');
45+
callErrorReporter.handle(e, s, 'RenegotiationHandler.handle error (callId=$callId, lineId=$lineId)');
4646
}
4747
}
4848
}

test/features/call/utils/renegotiation_handler_test.dart

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,14 @@ void main() {
151151
});
152152

153153
group('RenegotiationHandler — execute error handling', () {
154+
setUp(() {
155+
when(() => mockErrorReporter.handle(any(), any(), any())).thenReturn(null);
156+
});
157+
154158
test('reports error via callErrorReporter when execute throws', () async {
155159
when(() => mockPC.signalingState).thenReturn(RTCSignalingState.RTCSignalingStateStable);
156160
when(() => mockPC.createOffer(any())).thenAnswer((_) async => kOffer);
157161
when(() => mockPC.setLocalDescription(any())).thenAnswer((_) async {});
158-
when(() => mockErrorReporter.handle(any(), any(), any())).thenReturn(null);
159-
160162
handler = RenegotiationHandler(callErrorReporter: mockErrorReporter);
161163
final exception = Exception('signaling error');
162164

@@ -165,12 +167,33 @@ void main() {
165167
verify(() => mockErrorReporter.handle(exception, any(), any())).called(1);
166168
});
167169

170+
test('reports error via callErrorReporter when createOffer throws', () async {
171+
when(() => mockPC.signalingState).thenReturn(RTCSignalingState.RTCSignalingStateStable);
172+
final exception = Exception('createOffer error');
173+
when(() => mockPC.createOffer(any())).thenThrow(exception);
174+
handler = RenegotiationHandler(callErrorReporter: mockErrorReporter);
175+
176+
await handler.handle(kCallId, kLineId, mockPC, (_, _, _) async {});
177+
178+
verify(() => mockErrorReporter.handle(exception, any(), any())).called(1);
179+
});
180+
181+
test('reports error via callErrorReporter when setLocalDescription throws', () async {
182+
when(() => mockPC.signalingState).thenReturn(RTCSignalingState.RTCSignalingStateStable);
183+
when(() => mockPC.createOffer(any())).thenAnswer((_) async => kOffer);
184+
final exception = Exception('setLocalDescription error');
185+
when(() => mockPC.setLocalDescription(any())).thenThrow(exception);
186+
handler = RenegotiationHandler(callErrorReporter: mockErrorReporter);
187+
188+
await handler.handle(kCallId, kLineId, mockPC, (_, _, _) async {});
189+
190+
verify(() => mockErrorReporter.handle(exception, any(), any())).called(1);
191+
});
192+
168193
test('does not rethrow when execute throws', () async {
169194
when(() => mockPC.signalingState).thenReturn(RTCSignalingState.RTCSignalingStateStable);
170195
when(() => mockPC.createOffer(any())).thenAnswer((_) async => kOffer);
171196
when(() => mockPC.setLocalDescription(any())).thenAnswer((_) async {});
172-
when(() => mockErrorReporter.handle(any(), any(), any())).thenReturn(null);
173-
174197
handler = RenegotiationHandler(callErrorReporter: mockErrorReporter);
175198

176199
await expectLater(

0 commit comments

Comments
 (0)