Commit 13f0d85f authored by Simon Morlat's avatar Simon Morlat

implement automatic repairing of SIP dialogs broken by a network disconnection.

parent 1127f97f
......@@ -3651,16 +3651,23 @@ static void report_bandwidth(LinphoneCall *call, MediaStream *as, MediaStream *v
}
static void linphone_core_disconnected(LinphoneCore *lc, LinphoneCall *call){
char temp[256]={0};
static void linphone_call_lost(LinphoneCall *call, LinphoneReason reason){
LinphoneCore *lc = call->core;
char *temp = NULL;
char *from=NULL;
from = linphone_call_get_remote_address_as_string(call);
snprintf(temp,sizeof(temp)-1,"Remote end %s seems to have disconnected, the call is going to be closed.",from ? from : "");
switch(reason){
case LinphoneReasonIOError:
temp = ms_strdup_printf("Call with %s disconnected because of network, it is going to be closed.", from ? from : "?");
break;
default:
temp = ms_strdup_printf("Media connectivity with %s is lost, call is going to be closed.", from ? from : "?");
break;
}
if (from) ms_free(from);
ms_message("On call [%p]: %s",call,temp);
linphone_core_notify_display_warning(lc,temp);
ms_message("LinphoneCall [%p]: %s",call, temp);
linphone_core_notify_display_warning(lc, temp);
linphone_core_terminate_call(lc,call);
linphone_core_play_named_tone(lc,LinphoneToneCallLost);
}
......@@ -3928,7 +3935,7 @@ void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapse
&& call->audiostream->ms.state==MSStreamStarted && disconnect_timeout>0 )
disconnected=!audio_stream_alive(call->audiostream,disconnect_timeout);
if (disconnected)
linphone_core_disconnected(call->core,call);
linphone_call_lost(call, LinphoneReasonUnknown);
}
void linphone_call_log_completed(LinphoneCall *call){
......@@ -4234,3 +4241,52 @@ RtpTransport* linphone_call_get_meta_rtcp_transport(LinphoneCall *call, int stre
rtp_session_get_transports(call->sessions[stream_index].rtp_session, &meta_rtp, &meta_rtcp);
return meta_rtcp;
}
void linphone_call_set_broken(LinphoneCall *call){
switch(call->state){
/*for all the early states, we prefer to drop the call*/
case LinphoneCallOutgoingInit:
case LinphoneCallOutgoingRinging:
case LinphoneCallOutgoingEarlyMedia:
case LinphoneCallIncomingReceived:
case LinphoneCallIncomingEarlyMedia:
linphone_call_lost(call, LinphoneReasonIOError);
break;
case LinphoneCallStreamsRunning:
case LinphoneCallPaused:
case LinphoneCallPausedByRemote:
call->broken = TRUE;
break;
default:
ms_error("linphone_call_set_broken() unimplemented case.");
break;
}
}
void linphone_call_repair_if_broken(LinphoneCall *call){
LinphoneCallParams *params;
if (!call->broken) return;
/*First, make sure that the proxy from which we received this call, or to which we routed this call is registered*/
if (!call->dest_proxy || linphone_proxy_config_get_state(call->dest_proxy) != LinphoneRegistrationOk) return;
switch (call->state){
case LinphoneCallStreamsRunning:
case LinphoneCallPaused:
case LinphoneCallPausedByRemote:
ms_message("LinphoneCall[%p] is going to be updated (reINVITE) in order to recover from lost connectivity", call);
if (call->ice_session){
ice_session_restart(call->ice_session);
ice_session_set_role(call->ice_session, IR_Controlling);
}
params = linphone_core_create_call_params(call->core, call);
linphone_core_update_call(call->core, call, params);
linphone_call_params_unref(params);
break;
default:
ms_error("linphone_call_resume_if_broken(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state));
break;
}
}
......@@ -3281,6 +3281,7 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho
}
if (params!=NULL){
call->broken = FALSE;
linphone_call_set_state(call,nextstate,"Updating call");
#if defined(VIDEO_ENABLED) && defined(BUILD_UPNP)
has_video = call->params->has_video;
......@@ -6200,6 +6201,8 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu
if (!lc->network_reachable){
linphone_core_invalidate_friend_subscriptions(lc);
sal_reset_transports(lc->sal);
/*mark all calls as broken, so that they can be either dropped immediately or restaured when network will be back*/
ms_list_for_each(lc->calls, (MSIterateFunc) linphone_call_set_broken);
}else{
linphone_core_resolve_stun_server(lc);
}
......
......@@ -1833,4 +1833,3 @@ void linphone_task_list_run(LinphoneTaskList *t){
void linphone_task_list_free(LinphoneTaskList *t){
t->hooks = ms_list_free_with_data(t->hooks, (void (*)(void*))ms_free);
}
......@@ -311,6 +311,7 @@ struct _LinphoneCall{
bool_t record_active;
bool_t paused_by_app;
bool_t broken; /*set to TRUE when the call is in broken state due to network disconnection or transport */
};
BELLE_SIP_DECLARE_VPTR(LinphoneCall);
......@@ -935,6 +936,8 @@ LinphoneEcCalibratorStatus ec_calibrator_get_status(EcCalibrator *ecc);
void ec_calibrator_destroy(EcCalibrator *ecc);
void linphone_call_background_tasks(LinphoneCall *call, bool_t one_second_elapsed);
void linphone_call_set_broken(LinphoneCall *call);
void linphone_call_repair_if_broken(LinphoneCall *call);
void linphone_core_preempt_sound_resources(LinphoneCore *lc);
int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call);
......
......@@ -1512,8 +1512,13 @@ void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrat
if (update_friends){
linphone_core_update_friends_subscriptions(lc,cfg,TRUE);
}
if (lc)
if (lc){
linphone_core_notify_registration_state_changed(lc,cfg,state,message);
if (lc->calls && lp_config_get_int(lc->config, "sip", "repair_broken_calls", 1)){
/*if we are registered and there were broken calls due to a past network disconnection, attempt to repair them*/
ms_list_for_each(lc->calls, (MSIterateFunc) linphone_call_repair_if_broken);
}
}
} else {
/*state already reported*/
}
......
......@@ -4901,6 +4901,55 @@ static void call_record_with_custom_rtp_modifier(void) {
custom_rtp_modifier(FALSE, TRUE);
}
static void _call_with_network_switch(bool_t use_ice){
LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc");
bool_t call_ok;
if (use_ice){
linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce);
linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce);
}
BC_ASSERT_TRUE((call_ok=call(pauline,marie)));
if (!call_ok) goto end;
wait_for_until(marie->lc, pauline->lc, NULL, 0, 2000);
if (use_ice) BC_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection));
/*marie looses the network and reconnects*/
linphone_core_set_network_reachable(marie->lc, FALSE);
wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000);
/*marie will reconnect and register*/
linphone_core_set_network_reachable(marie->lc, TRUE);
BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneRegistrationOk, 2));
/*pauline shall receive a reINVITE to update the session*/
BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallUpdatedByRemote, 1));
BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2));
BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2));
liblinphone_tester_check_rtcp(pauline, marie);
if (use_ice) BC_ASSERT_TRUE(check_ice(pauline,marie,LinphoneIceStateHostConnection));
/*pauline shall be able to end the call without problem now*/
end_call(pauline, marie);
end:
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
}
static void call_with_network_switch(void){
_call_with_network_switch(FALSE);
}
#if 0
static void call_with_network_switch_and_ice(void){
_call_with_network_switch(TRUE);
}
#endif
test_t call_tests[] = {
{ "Early declined call", early_declined_call },
{ "Call declined", call_declined },
......@@ -5033,7 +5082,12 @@ test_t call_tests[] = {
{ "Call with complex late offering", call_with_complex_late_offering },
{ "Call with custom RTP Modifier", call_with_custom_rtp_modifier },
{ "Call paused resumed with custom RTP Modifier", call_paused_resumed_with_custom_rtp_modifier },
{ "Call record with custom RTP Modifier", call_record_with_custom_rtp_modifier }
{ "Call record with custom RTP Modifier", call_record_with_custom_rtp_modifier },
{ "Call with network switch", call_with_network_switch }
#if 0
,
{ "Call with network switch and ICE", call_with_network_switch_and_ice }
#endif
};
test_suite_t call_test_suite = {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment