Reconnection and Network Resilience

Network switches and short connectivity drops can interrupt call media. The SDK handles reconnection automatically in most cases — use media lifecycle events and reconnect APIs to keep users informed and recover where possible.

How Automatic Reconnection Works

When the SDK detects a media interruption (ICE connection failure, network switch), it automatically attempts to restore the connection by performing an ICE restart on the peer connection.

Timing and Retry Behavior

Parameter Value Description
Max automatic retries 5 The SDK retries up to 5 times before giving up
Retry interval 3 seconds Fixed delay between each reconnection attempt (no exponential backoff)
Media timeout 10 seconds If no media is established within 10s of a connection attempt, the SDK treats it as failed
Total automatic recovery window ~15 seconds 5 retries x 3s interval = up to 15s of automatic recovery before failure

What Happens at Each Stage

  1. Reconnecting — The SDK detects a media interruption and fires onCallMediaReconnecting. It performs up to 5 ICE restart attempts, spaced 3 seconds apart. No developer action is needed during this phase.

  2. Reconnected — If any retry succeeds, the SDK fires onCallMediaReconnection. The audio stream is restored and the call continues normally.

  3. Failed — If all 5 automatic retries are exhausted, or if the server returns an error during reconnection, the SDK fires onCallMediaDisconnect. At this point:

    • Audio has stopped
    • The call session may still be active on the server
    • The developer can attempt manual recovery with reconnectCall(callId) if network connectivity has returned
    • If no recovery occurs, the server will eventually terminate the call and onCallHangup fires with mediaTimeout reason

Developer action summary

  • During reconnecting: No action required. Show a "reconnecting" UI indicator.
  • On reconnection success: Remove the indicator, return to normal call UI.
  • On disconnect (failure): Optionally attempt reconnectCall(callId) if you detect network is back. Otherwise, inform the user the call was lost.

Session-Level Reconnection

Separately from media reconnection, the SDK also automatically reconnects the WebSocket session when connectivity drops:

Parameter Value
Max reconnection attempts 5
Reconnection delay 5 seconds (with 0.75 randomization factor)

Session reconnection fires onReconnecting and onReconnection events (distinct from the call media events above).

onCallMediaDisconnect

Fires when the media connection is terminally lost — after all 5 automatic reconnection attempts have been exhausted, or after an unrecoverable server error. At this point the audio stream has stopped, but the call session may still be active on the server. You can attempt manual recovery with reconnectCall(callId).

client.on(
  'callMediaDisconnect',
  (callId: string, reason: CallDisconnectReason) => {
    if (reason === 'networkChange') {
      console.log(`Call ${callId} was disconnected due to a network change`);
    } else {
      exhaustiveCheck(reason);
    }
  }
);
 client.setOnCallMediaDisconnectListener { legId, reason ->
     when (reason) {
         CallDisconnectReason.networkChange -> println("Received call media disconnect $reason for Call $legId")
     }

 }
 client.setOnCallMediaDisconnectListener((legId, reason) -> {
     if (reason == CallDisconnectReason.networkChange) {
         System.out.println("Received call media disconnect " + reason + " for Call " + legId);
     }
     return null;
 });
 func voiceClient(_ client: VGVoiceClient, didReceiveMediaDisconnectForCall callId: VGCallId, reason: VGCallDisconnectReason) {
     print("leg \(callId) has received media disconnect for \(reason)")
 }
 - (void) voiceClient:(VGVoiceClient *)client didReceiveMediaDisconnectForCall:(VGCallId)callId reason:(VGCallDisconnectReason)reason {
     NSLog(@"Leg %@ has received media disconnect for reason %ld", callId, reason);
 }

onCallMediaReconnecting

Fires when the SDK detects a media interruption (such as a network switch between Wi-Fi and cellular) and is automatically attempting to restore the connection. No user action is required — show a "reconnecting" indicator in your UI.

client.on('callMediaReconnecting', (callId) => {
  console.log(`Call media is reconnecting for call ${callId}`);
});
 client.setOnCallMediaReconnectingListener { legId ->
     println("Received call media reconnecting for Call $legId")
 }
 client.setOnCallMediaReconnectingListener((legId) -> {
     System.out.println("Received call media reconnecting for Call " + legId);
     return null;
 });
 func voiceClient(_ client: VGVoiceClient, didReceiveMediaReconnectingForCall callId: VGCallId) {
     print("leg \(callId) is Reconnecting")
 }
 - (void) voiceClient:(VGVoiceClient *)client didReceiveMediaReconnectingForCall:(VGCallId)callId {
     NSLog(@"Leg %@ is reconnecting", callId);
 }

onCallMediaReconnection

Fires when the SDK successfully restores the media connection after an interruption. Remove the reconnecting indicator and return to normal call UI.

client.on('callMediaReconnection', (callId) => {
  console.log(`Call media is reconnected for call ${callId}`);
});
 client.setOnCallMediaReconnectionListener { legId ->
     println("Received call media reconnection for Call $legId")
 }
 client.setOnCallMediaReconnectionListener((legId) -> {
     System.out.println("Received call media reconnection for Call " + legId);
     return null;
 });
 func voiceClient(_ client: VGVoiceClient, didReceiveMediaReconnectionForCall callId: VGCallId) {
     print("leg \(callId) has received media reconnection")
 }
 - (void) voiceClient:(VGVoiceClient *)client didReceiveMediaReconnectionForCall:(VGCallId)callId {
     NSLog(@"Leg %@ has received media reconnection", callId);
 }

Manual Reconnect

Use reconnectCall(callId) when your app decides to trigger recovery explicitly — for example, after receiving onCallMediaDisconnect and confirming that network connectivity has returned.

Manual reconnection also follows the same retry logic (up to 5 attempts, 3s apart) once triggered.

const callId = 'callId';
await client.reconnectCall(callId);
 // Callback
 client.reconnectCall("callId") {
     it?.let { err ->
         // Handle Error in reconnecting call
         println("Error in reconnecting call: $err")
     }
 }
 // Coroutine
 try {
     client.reconnectCall("callId")
 }
 catch (e:Error) {
     // Handle Error in reconnecting call
 }

 client.reconnectCall("callId", it -> {
     if (it != null) {
         // Handle Error in reconnecting call
         System.out.println("Error in reconnecting call: " + it);
     }
     return null;
 });
 // Callback
 client.reconnectCall("someCallId") { error in
     guard error != nil else {
         print("Failed to reconnect \(error?.localizedDescription ?? "")")
         return
     }
 }
 
 /// Async Await
 Task.detached {
     do {
         try await client.reconnectCall("someCallId")
     }
     catch {
         print("error")
     }
 }
 [client reconnect:@"someCallId" callback:^(NSError * _Nullable error) {
         if(error){
             NSLog(@"Failed to reconnect: %@", error);
         }
 }];

Android Requirement

Automatic network-change recovery on Android requires:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>

Failure Behavior and UX

  • If all automatic retries fail and no manual reconnection succeeds, onCallHangup eventually fires with mediaTimeout reason.
  • Show a reconnecting indicator when onCallMediaReconnecting fires.
  • Return to normal call UI only after onCallMediaReconnection fires.
  • On onCallMediaDisconnect, inform the user and optionally offer a "Retry" button that calls reconnectCall(callId).