From c329b10669977a69d1a2c0bdd3ed3ee20e7c6301 Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Fri, 22 Jan 2016 17:51:27 +0100 Subject: [PATCH] implement independant controls for media and sip network reachabilities. --- coreapi/linphonecore.c | 78 ++++++++++++++++++++++++++++++++---------- coreapi/linphonecore.h | 16 +++++++++ coreapi/private.h | 8 +++-- coreapi/proxy.c | 8 ++--- coreapi/upnp.c | 2 +- tester/call_tester.c | 76 +++++++++++++++++++++++++++++++++++++--- 6 files changed, 155 insertions(+), 33 deletions(-) diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 0a98e31c6..0584c9b4f 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -102,6 +102,8 @@ static FILE * liblinphone_log_collection_file = NULL; static size_t liblinphone_log_collection_file_size = 0; static bool_t liblinphone_serialize_logs = FALSE; static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); +static void set_sip_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime); +static void set_media_network_reachable(LinphoneCore* lc,bool_t isReachable); static void linphone_core_run_hooks(LinphoneCore *lc); #include "enum.h" @@ -2556,7 +2558,7 @@ void linphone_core_iterate(LinphoneCore *lc){ if (lc->network_reachable_to_be_notified) { lc->network_reachable_to_be_notified=FALSE; - linphone_core_notify_network_reachable(lc,lc->network_reachable); + linphone_core_notify_network_reachable(lc,lc->sip_network_reachable); } if (linphone_core_get_global_state(lc) == LinphoneGlobalStartup) { if (sal_get_root_ca(lc->sal)) { @@ -2696,7 +2698,7 @@ void linphone_core_iterate(LinphoneCore *lc){ linphone_core_run_hooks(lc); linphone_core_do_plugin_tasks(lc); - if (lc->network_reachable && lc->netup_time!=0 && (current_real_time-lc->netup_time)>3){ + if (lc->sip_network_reachable && lc->netup_time!=0 && (current_real_time-lc->netup_time)>3){ /*not do that immediately, take your time.*/ linphone_core_send_initial_subscribes(lc); } @@ -6166,7 +6168,7 @@ void sip_config_uninit(LinphoneCore *lc) lp_config_set_int(lc->config,"sip","register_only_when_network_is_up",config->register_only_when_network_is_up); lp_config_set_int(lc->config,"sip","register_only_when_upnp_is_ok",config->register_only_when_upnp_is_ok); - if (lc->network_reachable) { + if (lc->sip_network_reachable) { for(elem=config->proxies;elem!=NULL;elem=ms_list_next(elem)){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)(elem->data); _linphone_proxy_config_unregister(cfg); /* to unregister without changing the stored flag enable_register */ @@ -6431,17 +6433,17 @@ static void linphone_core_uninit(LinphoneCore *lc) ms_list_free_with_data(lc->vtable_refs,(void (*)(void *))v_table_reference_destroy); } -static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t curtime){ +static void set_sip_network_reachable(LinphoneCore* lc,bool_t is_sip_reachable, time_t curtime){ // second get the list of available proxies const MSList *elem=linphone_core_get_proxy_config_list(lc); - if (lc->network_reachable==isReachable) return; // no change, ignore. + if (lc->sip_network_reachable==is_sip_reachable) return; // no change, ignore. lc->network_reachable_to_be_notified=TRUE; - ms_message("Network state is now [%s]",isReachable?"UP":"DOWN"); + ms_message("SIP network reachability state is now [%s]",is_sip_reachable?"UP":"DOWN"); for(;elem!=NULL;elem=elem->next){ LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data; if (linphone_proxy_config_register_enabled(cfg) ) { - if (!isReachable) { + if (!is_sip_reachable) { linphone_proxy_config_stop_refreshing(cfg); linphone_proxy_config_set_state(cfg, LinphoneRegistrationNone,"Registration impossible (network down)"); }else{ @@ -6450,26 +6452,23 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu } } lc->netup_time=curtime; - lc->network_reachable=isReachable; + lc->sip_network_reachable=is_sip_reachable; - if (!lc->network_reachable){ + if (!lc->sip_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); - if (lp_config_get_int(lc->config, "net", "recreate_sockets_when_network_is_up", 0)){ - ms_list_for_each(lc->calls, (MSIterateFunc)linphone_call_refresh_sockets); - } } #ifdef BUILD_UPNP if(lc->upnp == NULL) { - if(isReachable && linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) { + if(is_sip_reachable && linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) { lc->upnp = linphone_upnp_context_new(lc); } } else { - if(!isReachable && linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) { + if(!is_sip_reachable && linphone_core_get_firewall_policy(lc) == LinphonePolicyUseUpnp) { linphone_upnp_context_destroy(lc->upnp); lc->upnp = NULL; } @@ -6477,9 +6476,37 @@ static void set_network_reachable(LinphoneCore* lc,bool_t isReachable, time_t cu #endif } +void linphone_core_repair_calls(LinphoneCore *lc){ + if (lc->calls && lp_config_get_int(lc->config, "sip", "repair_broken_calls", 1) && lc->media_network_reachable){ + /*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); + } +} + +static void set_media_network_reachable(LinphoneCore* lc, bool_t is_media_reachable){ + if (lc->media_network_reachable==is_media_reachable) return; // no change, ignore. + ms_message("Media network reachability state is now [%s]",is_media_reachable?"UP":"DOWN"); + lc->media_network_reachable=is_media_reachable; + + if (!lc->media_network_reachable){ + /*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{ + if (lp_config_get_int(lc->config, "net", "recreate_sockets_when_network_is_up", 0)){ + ms_list_for_each(lc->calls, (MSIterateFunc)linphone_call_refresh_sockets); + } + linphone_core_repair_calls(lc); + } +} + +static void set_network_reachable(LinphoneCore *lc, bool_t is_network_reachable, time_t curtime){ + set_sip_network_reachable(lc, is_network_reachable, curtime); + set_media_network_reachable(lc, is_network_reachable); +} + void linphone_core_refresh_registers(LinphoneCore* lc) { const MSList *elem; - if (!lc->network_reachable) { + if (!lc->sip_network_reachable) { ms_warning("Refresh register operation not available (network unreachable)"); return; } @@ -6503,17 +6530,30 @@ void __linphone_core_invalidate_registers(LinphoneCore* lc){ } } -void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t isReachable) { - //first disable automatic mode +static void disable_internal_network_reachability_detection(LinphoneCore *lc){ if (lc->auto_net_state_mon) { ms_message("Disabling automatic network state monitoring"); lc->auto_net_state_mon=FALSE; } - set_network_reachable(lc,isReachable, ms_time(NULL)); +} + +void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t isReachable) { + disable_internal_network_reachability_detection(lc); + set_network_reachable(lc, isReachable, ms_time(NULL)); +} + +void linphone_core_set_media_network_reachable(LinphoneCore *lc, bool_t is_reachable){ + disable_internal_network_reachability_detection(lc); + set_media_network_reachable(lc, is_reachable); +} + +void linphone_core_set_sip_network_reachable(LinphoneCore *lc, bool_t is_reachable){ + disable_internal_network_reachability_detection(lc); + set_sip_network_reachable(lc, is_reachable, ms_time(NULL)); } bool_t linphone_core_is_network_reachable(LinphoneCore* lc) { - return lc->network_reachable; + return lc->sip_network_reachable; } ortp_socket_t linphone_core_get_sip_socket(LinphoneCore *lc){ return sal_get_socket(lc->sal); diff --git a/coreapi/linphonecore.h b/coreapi/linphonecore.h index b23c305a2..d4925f50a 100644 --- a/coreapi/linphonecore.h +++ b/coreapi/linphonecore.h @@ -3743,6 +3743,22 @@ LINPHONE_PUBLIC void linphone_core_set_network_reachable(LinphoneCore* lc,bool_t */ LINPHONE_PUBLIC bool_t linphone_core_is_network_reachable(LinphoneCore* lc); +/** + * @ingroup network_parameters + * This method is called by the application to notify the linphone core library when the SIP network is reachable. + * This is for advanced usage, when SIP and RTP layers are required to use different interfaces. + * Most applications just need linphone_core_set_network_reachable(). + */ +LINPHONE_PUBLIC void linphone_core_set_sip_network_reachable(LinphoneCore* lc,bool_t value); + +/** + * @ingroup network_parameters + * This method is called by the application to notify the linphone core library when the media (RTP) network is reachable. + * This is for advanced usage, when SIP and RTP layers are required to use different interfaces. + * Most applications just need linphone_core_set_network_reachable(). + */ +LINPHONE_PUBLIC void linphone_core_set_media_network_reachable(LinphoneCore* lc,bool_t value); + /** * @ingroup network_parameters * enable signaling keep alive. small udp packet sent periodically to keep udp NAT association diff --git a/coreapi/private.h b/coreapi/private.h index d6b683041..6f3dfe7ce 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -921,13 +921,16 @@ struct _LinphoneCore bool_t preview_finished; bool_t auto_net_state_mon; - bool_t network_reachable; + bool_t sip_network_reachable; + bool_t media_network_reachable; + bool_t network_reachable_to_be_notified; /*set to true when state must be notified in next iterate*/ - bool_t use_preview_window; bool_t network_last_status; bool_t ringstream_autorelease; + bool_t vtables_running; + bool_t pad[3]; char localip[LINPHONE_IPADDR_SIZE]; int device_rotation; int max_calls; @@ -1037,6 +1040,7 @@ 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_repair_calls(LinphoneCore *lc); void linphone_core_preempt_sound_resources(LinphoneCore *lc); int _linphone_core_pause_call(LinphoneCore *lc, LinphoneCall *call); diff --git a/coreapi/proxy.c b/coreapi/proxy.c index 3cb0726d1..01c8ddcb7 100644 --- a/coreapi/proxy.c +++ b/coreapi/proxy.c @@ -1362,7 +1362,7 @@ static bool_t can_register(LinphoneProxyConfig *cfg){ } #endif //BUILD_UPNP if (lc->sip_conf.register_only_when_network_is_up){ - return lc->network_reachable; + return lc->sip_network_reachable; } return TRUE; } @@ -1434,13 +1434,9 @@ void linphone_proxy_config_set_state(LinphoneProxyConfig *cfg, LinphoneRegistrat cfg->state=state; } - 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); - } + linphone_core_repair_calls(lc); } } else { /*state already reported*/ diff --git a/coreapi/upnp.c b/coreapi/upnp.c index 5ec118247..ef8b544aa 100644 --- a/coreapi/upnp.c +++ b/coreapi/upnp.c @@ -367,7 +367,7 @@ void linphone_upnp_context_destroy(UpnpContext *lupnp) { ms_mutex_lock(&lupnp->mutex); - if(lupnp->lc->network_reachable) { + if(lupnp->lc->sip_network_reachable) { /* Send port binding removes */ if(lupnp->sip_udp != NULL) { linphone_upnp_context_send_remove_port_binding(lupnp, lupnp->sip_udp, TRUE); diff --git a/tester/call_tester.c b/tester/call_tester.c index 57956511f..e78efa5ba 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -5474,6 +5474,7 @@ static void _call_with_network_switch(bool_t use_ice, bool_t with_socket_refresh LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); MSList *lcs = NULL; + int ice_reinvite = use_ice ? 1 : 0; bool_t call_ok; lcs = ms_list_append(lcs, marie->lc); @@ -5503,9 +5504,10 @@ static void _call_with_network_switch(bool_t use_ice, bool_t with_socket_refresh 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)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallUpdating, 1+ice_reinvite)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallUpdatedByRemote, 1+ice_reinvite)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2+ice_reinvite)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2+ice_reinvite)); /*check that media is back*/ check_media_direction(marie, linphone_core_get_current_call(marie->lc), lcs, LinphoneMediaDirectionSendRecv, LinphoneMediaDirectionInvalid); @@ -5532,6 +5534,69 @@ static void call_with_network_switch_and_socket_refresh(void){ _call_with_network_switch(TRUE, TRUE); } +static void call_with_sip_and_rtp_independant_switches(){ + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); + MSList *lcs = NULL; + bool_t call_ok; + bool_t use_ice = TRUE; + bool_t with_socket_refresh = TRUE; + + lcs = ms_list_append(lcs, marie->lc); + lcs = ms_list_append(lcs, pauline->lc); + + if (use_ice){ + linphone_core_set_firewall_policy(marie->lc,LinphonePolicyUseIce); + linphone_core_set_firewall_policy(pauline->lc,LinphonePolicyUseIce); + } + if (with_socket_refresh){ + lp_config_set_int(linphone_core_get_config(marie->lc), "net", "recreate_sockets_when_network_is_up", 1); + lp_config_set_int(linphone_core_get_config(pauline->lc), "net", "recreate_sockets_when_network_is_up", 1); + } + + linphone_core_set_media_network_reachable(marie->lc, TRUE); + + 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 SIP network and reconnects*/ + linphone_core_set_sip_network_reachable(marie->lc, FALSE); + wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000); + + /*marie will reconnect and register*/ + linphone_core_set_sip_network_reachable(marie->lc, TRUE); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneRegistrationOk, 2)); + wait_for_until(marie->lc, pauline->lc, NULL, 0, 1000); + /*at this stage, no reINVITE is expected to be send*/ + BC_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallUpdating, 1, int, "%i"); /*1: because of ICE reinvite*/ + + /*now we notify the a reconnection of media network*/ + linphone_core_set_media_network_reachable(marie->lc, FALSE); + linphone_core_set_media_network_reachable(marie->lc, TRUE); + + /*pauline shall receive a reINVITE to update the session*/ + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallUpdating, 2)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallUpdatedByRemote, 2)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 3)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 3)); + + /*check that media is back*/ + check_media_direction(marie, linphone_core_get_current_call(marie->lc), lcs, LinphoneMediaDirectionSendRecv, LinphoneMediaDirectionInvalid); + 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: + ms_list_free(lcs); + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + + #ifdef CALL_LOGS_STORAGE_ENABLED static void call_logs_if_no_db_set(void) { @@ -5987,8 +6052,8 @@ test_t call_tests[] = { { "Call established with rejected info during re-invite",call_established_with_rejected_info_during_reinvite}, { "Call redirected by callee", call_redirect}, { "Call with specified codec bitrate", call_with_specified_codec_bitrate}, - { "Call with no audio codec", call_with_no_audio_codec}, - { "Video call with no audio and no video codec", video_call_with_no_audio_and_no_video_codec}, + { "Call with no audio codec", call_with_no_audio_codec}, + { "Video call with no audio and no video codec", video_call_with_no_audio_and_no_video_codec}, { "Call with in-dialog UPDATE request", call_with_in_dialog_update }, { "Call with in-dialog codec change", call_with_in_dialog_codec_change }, { "Call with in-dialog codec change no sdp", call_with_in_dialog_codec_change_no_sdp }, @@ -6025,6 +6090,7 @@ test_t call_tests[] = { { "Call with network switch in early state 2", call_with_network_switch_in_early_state_2 }, { "Call with network switch and ICE", call_with_network_switch_and_ice }, { "Call with network switch with socket refresh", call_with_network_switch_and_socket_refresh }, + { "Call with SIP and RTP independant switches", call_with_sip_and_rtp_independant_switches}, { "Call with rtcp-mux", call_with_rtcp_mux}, { "Call with rtcp-mux not accepted", call_with_rtcp_mux_not_accepted}, { "Call with ICE and rtcp-mux", call_with_ice_and_rtcp_mux},