Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 43 additions & 4 deletions lib/features/call/bloc/call_bloc.dart
Original file line number Diff line number Diff line change
Expand Up @@ -744,8 +744,25 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
Future<void> _onCallPushEventIncoming(_CallPushEventIncoming event, Emitter<CallState> emit) async {
final eventError = event.error;
if (eventError != null) {
_logger.warning('_onCallPushEventIncoming event.error: $eventError');
// TODO: implement correct incoming call hangup (take into account that _signalingClient is disconnected)
// iOS only: CXProvider rejected the incoming call registration before it was
// ever presented to the user (e.g. DND / Focus active, Call Directory blocklist,
// missing VoIP entitlement, or unexpected CXProvider failure).
//
// Consequences:
// - performEndCall will NOT fire (CallKit never registered the call).
// - _signalingModule is very likely disconnected: VoIP push wakes the app
// before the WebSocket is established, so the CXProvider completion fires
// before signaling reconnects.
//
// Recovery path: when signaling reconnects the server replays the incoming
// call event via the handshake. HandshakeProcessor generates a
// HandleIncomingCallAction for the unknown callId, which re-enters
// __onCallSignalingEventIncoming. At that point we have the SIP line and
// can send a DeclineRequest immediately (see that method below).
_logger.warning(
'_onCallPushEventIncoming: OS rejected call registration '
'(callId: ${event.callId}, error: $eventError) — server will be notified on next handshake',
);
return;
}

Expand Down Expand Up @@ -920,8 +937,30 @@ class CallBloc extends Bloc<CallEvent, CallState> with WidgetsBindingObserver im
final callAlreadyTerminated = error == CallkeepIncomingCallError.callIdAlreadyTerminated;

if (error != null && !callAlreadyExists && !callAlreadyAnswered && !callAlreadyTerminated) {
_logger.warning('__onCallSignalingEventIncoming reportNewIncomingCall error: $error');
// TODO: implement correct incoming call hangup (take into account that _signalingClient could be disconnected)
// reportNewIncomingCall rejected the call with an unexpected error:
// - Android: callRejectedBySystem — Telecom already has a call in RINGING state
// (AOSP behaviour, Android 11+), or the 5 s Telecom confirmation timeout elapsed.
// - iOS: unknown / unentitled / internal — rare CXProvider failure on the
// signaling-path reportNewIncomingCall (not the VoIP-push path).
//
// The call was never presented to the user, so performEndCall will NOT fire.
// Notify the server immediately so the remote party is not left ringing.
// _signalingModule.execute returns null when disconnected — the ?. handles that safely.
_logger.warning(
'__onCallSignalingEventIncoming: reportNewIncomingCall error=$error '
'(callId: ${event.callId}, line: ${event.line}) — sending decline',
);
await _signalingModule
.execute(
DeclineRequest(
transaction: WebtritSignalingClient.generateTransactionId(),
line: event.line,
callId: event.callId,
),
)
?.catchError((e, s) {
callErrorReporter.handle(e, s, '__onCallSignalingEventIncoming declineRequest error');
});
return;
}

Expand Down
Loading