From d5d0f39517abafa1f82a690969cae4d2b929507e Mon Sep 17 00:00:00 2001 From: Ghislain MARY Date: Wed, 12 Oct 2016 11:50:33 +0200 Subject: [PATCH] Implement section 5.5 of RFC 6141, recover from UAC connection loss during re-INVITE. --- coreapi/bellesip_sal/sal_op_call.c | 9 +- coreapi/callbacks.c | 8 +- coreapi/linphonecall.c | 40 ++++-- coreapi/linphonecore.c | 6 + coreapi/private.h | 1 + include/sal/sal.h | 1 + tester/call_single_tester.c | 189 +++++++++++++++++++++++++++++ 7 files changed, 242 insertions(+), 12 deletions(-) diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 956c4a556..8f7fa89ef 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -208,7 +208,7 @@ static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) { if (op->base.local_media) sdp_process(op); } -static void cancelling_invite(SalOp* op ){ +void sal_call_cancel_invite(SalOp* op) { belle_sip_request_t* cancel; ms_message("Cancelling INVITE request from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op)); cancel = belle_sip_client_transaction_create_cancel(op->pending_client_trans); @@ -229,6 +229,10 @@ static void cancelling_invite(SalOp* op ){ break; } } +} + +static void cancelling_invite(SalOp *op) { + sal_call_cancel_invite(op); op->state=SalOpStateTerminating; } @@ -339,6 +343,9 @@ static void call_process_response(void *op_base, const belle_sip_response_event_ } }else if (strcmp("UPDATE",method)==0){ op->base.root->callbacks.call_accepted(op); /*INVITE*/ + }else if (strcmp("CANCEL",method)==0){ + sal_op_set_error_info_from_response(op,response); + op->base.root->callbacks.call_failure(op); } break; case SalOpStateTerminating: diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index f010c9e3e..26bfec92c 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -669,6 +669,8 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t case LinphoneCallPausedByRemote: if (sal_media_description_has_dir(rmd,SalStreamSendRecv) || sal_media_description_has_dir(rmd,SalStreamRecvOnly)){ call_resumed(lc,call); + }else if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){ + call_paused_by_remote(lc,call); /* This can happen if the 200 OK of the re-INVITE has not reached the other party and that this one re-sends a new re-INVITE */ }else{ call_updated_by_remote(lc, call); } @@ -684,6 +686,7 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t break; case LinphoneCallStreamsRunning: case LinphoneCallConnected: + case LinphoneCallUpdatedByRemote: // Can happen on UAC connectivity loss if (sal_media_description_has_dir(rmd,SalStreamSendOnly) || sal_media_description_has_dir(rmd,SalStreamInactive)){ call_paused_by_remote(lc,call); }else{ @@ -697,7 +700,6 @@ static void call_updated(LinphoneCore *lc, LinphoneCall *call, SalOp *op, bool_t case LinphoneCallUpdating: case LinphoneCallPausing: case LinphoneCallResuming: - case LinphoneCallUpdatedByRemote: sal_call_decline(call->op,SalReasonInternalError,NULL); /*no break*/ case LinphoneCallIdle: @@ -943,6 +945,10 @@ static void call_failure(SalOp *op){ msg=_("Incompatible media parameters."); linphone_core_notify_display_status(lc,msg); break; + case SalReasonNoMatch: + /* Call leg does not exist response for case of section 5.5 of RFC 6141 */ + linphone_call_reinvite_to_recover_from_connection_loss(call); + return; /* Do not continue... */ default: linphone_core_notify_display_status(lc,_("Call failed.")); } diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 244673afd..9252bda1b 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -5029,8 +5029,12 @@ void linphone_call_set_broken(LinphoneCall *call){ * hence, there is nothing special to do*/ //break; case LinphoneCallStreamsRunning: + case LinphoneCallUpdating: + case LinphoneCallPausing: + case LinphoneCallResuming: case LinphoneCallPaused: case LinphoneCallPausedByRemote: + case LinphoneCallUpdatedByRemote: /*during these states, the dialog is established. A failure of a transaction is not expected to close it. * Instead we have to repair the dialog by sending a reINVITE*/ call->broken = TRUE; @@ -5052,9 +5056,18 @@ static void linphone_call_repair_by_invite_with_replaces(LinphoneCall *call) { linphone_core_start_invite(call->core, call, NULL); } -void linphone_call_repair_if_broken(LinphoneCall *call){ +void linphone_call_reinvite_to_recover_from_connection_loss(LinphoneCall *call) { LinphoneCallParams *params; + 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, IR_Controlling); + } + params = linphone_core_create_call_params(call->core, call); + linphone_core_update_call(call->core, call, params); + linphone_call_params_unref(params); +} +void linphone_call_repair_if_broken(LinphoneCall *call){ if (!call->broken) return; if (!call->core->media_network_reachable) return; @@ -5068,19 +5081,26 @@ void linphone_call_repair_if_broken(LinphoneCall *call){ switch (call->state){ + case LinphoneCallUpdating: + case LinphoneCallPausing: + if (sal_call_dialog_request_pending(call->op)) { + /* Need to cancel first re-INVITE as described in section 5.5 of RFC 6141 */ + sal_call_cancel_invite(call->op); + } + break; case LinphoneCallStreamsRunning: case LinphoneCallPaused: case LinphoneCallPausedByRemote: if (!sal_call_dialog_request_pending(call->op)) { - 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, IR_Controlling); - } - params = linphone_core_create_call_params(call->core, call); - linphone_core_update_call(call->core, call, params); - linphone_call_params_unref(params); + linphone_call_reinvite_to_recover_from_connection_loss(call); } - break; + break; + case LinphoneCallUpdatedByRemote: + if (sal_call_dialog_request_pending(call->op)) { + sal_call_decline(call->op, SalReasonServiceUnavailable, NULL); + } + linphone_call_reinvite_to_recover_from_connection_loss(call); + break; case LinphoneCallOutgoingEarlyMedia: case LinphoneCallOutgoingInit: case LinphoneCallOutgoingProgress: @@ -5088,7 +5108,7 @@ void linphone_call_repair_if_broken(LinphoneCall *call){ linphone_call_repair_by_invite_with_replaces(call); break; default: - ms_warning("linphone_call_resume_if_broken(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state)); + ms_warning("linphone_call_repair_if_broken(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state)); call->broken = FALSE; break; } diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 057b3dde7..0fb6f8125 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -3625,8 +3625,14 @@ int linphone_core_update_call(LinphoneCore *lc, LinphoneCall *call, const Linpho case LinphoneCallStreamsRunning: case LinphoneCallPaused: case LinphoneCallPausedByRemote: + case LinphoneCallUpdatedByRemote: nextstate=LinphoneCallUpdating; break; + case LinphoneCallPausing: + case LinphoneCallResuming: + case LinphoneCallUpdating: + nextstate=call->state; + break; default: ms_error("linphone_core_update_call() is not allowed in [%s] state",linphone_call_state_to_string(call->state)); return -1; diff --git a/coreapi/private.h b/coreapi/private.h index d5a418c84..831274bbe 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -390,6 +390,7 @@ void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState stat LinphonePlayer *linphone_call_build_player(LinphoneCall*call); void linphone_call_refresh_sockets(LinphoneCall *call); void linphone_call_replace_op(LinphoneCall *call, SalOp *op); +void linphone_call_reinvite_to_recover_from_connection_loss(LinphoneCall *call); LinphoneCallParams * linphone_call_params_new(void); SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params); diff --git a/include/sal/sal.h b/include/sal/sal.h index a2f5072bd..2a12a2444 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -737,6 +737,7 @@ int sal_call_notify_ringing(SalOp *h, bool_t early_media); int sal_call_accept(SalOp*h); int sal_call_decline(SalOp *h, SalReason reason, const char *redirection /*optional*/); int sal_call_update(SalOp *h, const char *subject, bool_t no_user_consent); +void sal_call_cancel_invite(SalOp *op); SalMediaDescription * sal_call_get_remote_media_description(SalOp *h); LINPHONE_PUBLIC SalMediaDescription * sal_call_get_final_media_description(SalOp *h); int sal_call_refer(SalOp *h, const char *refer_to); diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c index 872a77efb..9aca8104c 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -4309,6 +4309,191 @@ end: linphone_core_manager_destroy(pauline); } +static void recovered_call_on_network_switch_during_reinvite_1(void) { + LinphoneCall *incoming_call; + LinphoneCall *outgoing_call; + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); + + linphone_core_invite_address(marie->lc, pauline->identity); + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1))) goto end; + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingRinging, 1))) goto end; + + incoming_call = linphone_core_get_current_call(pauline->lc); + linphone_core_accept_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); + + outgoing_call = linphone_core_get_current_call(marie->lc); + linphone_core_pause_call(marie->lc, outgoing_call); + linphone_core_set_network_reachable(marie->lc, FALSE); + wait_for(marie->lc, pauline->lc, &marie->stat.number_of_NetworkReachableFalse, 1); + linphone_core_set_network_reachable(marie->lc, TRUE); + wait_for(marie->lc, pauline->lc, &marie->stat.number_of_NetworkReachableTrue, 2); + + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallPaused, 1)); + linphone_core_terminate_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); +end: + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +static void configure_video_policies_for_network_switch(LinphoneCore *marie, LinphoneCore *pauline) { + LinphoneVideoPolicy policy; + policy.automatically_accept = FALSE; + policy.automatically_initiate = FALSE; + + linphone_core_enable_video_capture(marie, TRUE); + linphone_core_enable_video_display(marie, TRUE); + linphone_core_enable_video_capture(pauline, TRUE); + linphone_core_enable_video_display(pauline, TRUE); + linphone_core_set_video_policy(marie, &policy); + linphone_core_set_video_policy(pauline, &policy); + lp_config_set_int(pauline->config, "sip", "defer_update_default", TRUE); +} + +static void recovered_call_on_network_switch_during_reinvite_2(void) { + LinphoneCall *incoming_call; + LinphoneCall *outgoing_call; + LinphoneCallParams *params; + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); + + configure_video_policies_for_network_switch(marie->lc, pauline->lc); + linphone_core_invite_address(marie->lc, pauline->identity); + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1))) goto end; + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingRinging, 1))) goto end; + + incoming_call = linphone_core_get_current_call(pauline->lc); + linphone_core_accept_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); + + outgoing_call = linphone_core_get_current_call(marie->lc); + params = linphone_core_create_call_params(marie->lc, outgoing_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_update_call(marie->lc, outgoing_call, params); + linphone_core_set_network_reachable(marie->lc, FALSE); + wait_for(marie->lc, pauline->lc, &marie->stat.number_of_NetworkReachableFalse, 1); + linphone_core_set_network_reachable(marie->lc, TRUE); + wait_for(marie->lc, pauline->lc, &marie->stat.number_of_NetworkReachableTrue, 2); + + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallUpdatedByRemote, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneRegistrationOk, 2)); + wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000); + params = linphone_core_create_call_params(pauline->lc, incoming_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_accept_call_update(pauline->lc, incoming_call, params); + + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2)); + wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000); + linphone_core_terminate_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); +end: + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +static void recovered_call_on_network_switch_during_reinvite_3(void) { + LinphoneCall *incoming_call; + LinphoneCall *outgoing_call; + LinphoneCallParams *params; + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); + + configure_video_policies_for_network_switch(marie->lc, pauline->lc); + linphone_core_invite_address(marie->lc, pauline->identity); + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1))) goto end; + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingRinging, 1))) goto end; + + incoming_call = linphone_core_get_current_call(pauline->lc); + linphone_core_accept_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); + + outgoing_call = linphone_core_get_current_call(marie->lc); + params = linphone_core_create_call_params(marie->lc, outgoing_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_update_call(marie->lc, outgoing_call, params); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallUpdatedByRemote, 1)); + + linphone_core_set_network_reachable(pauline->lc, FALSE); + wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_NetworkReachableFalse, 1); + linphone_core_set_network_reachable(pauline->lc, TRUE); + wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_NetworkReachableTrue, 2); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneRegistrationOk, 2)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallUpdatedByRemote, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 2)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2)); + + params = linphone_core_create_call_params(marie->lc, outgoing_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_update_call(marie->lc, outgoing_call, params); + wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000); + params = linphone_core_create_call_params(pauline->lc, incoming_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_accept_call_update(pauline->lc, incoming_call, params); + + wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000); + linphone_core_terminate_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); +end: + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + +static void recovered_call_on_network_switch_during_reinvite_4(void) { + LinphoneCall *incoming_call; + LinphoneCall *outgoing_call; + LinphoneCallParams *params; + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); + + configure_video_policies_for_network_switch(marie->lc, pauline->lc); + linphone_core_invite_address(marie->lc, pauline->identity); + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallIncomingReceived, 1))) goto end; + if (!BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallOutgoingRinging, 1))) goto end; + + incoming_call = linphone_core_get_current_call(pauline->lc); + linphone_core_accept_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1)); + + outgoing_call = linphone_core_get_current_call(marie->lc); + params = linphone_core_create_call_params(marie->lc, outgoing_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_update_call(marie->lc, outgoing_call, params); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallUpdatedByRemote, 1)); + + params = linphone_core_create_call_params(pauline->lc, incoming_call); + linphone_call_params_enable_video(params, TRUE); + linphone_core_accept_call_update(pauline->lc, incoming_call, params); + linphone_core_set_network_reachable(pauline->lc, FALSE); + wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_NetworkReachableFalse, 1); + linphone_core_set_network_reachable(pauline->lc, TRUE); + wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_NetworkReachableTrue, 2); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneRegistrationOk, 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, &pauline->stat.number_of_LinphoneCallStreamsRunning, 2)); + + wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000); + linphone_core_terminate_call(pauline->lc, incoming_call); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallReleased, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallReleased, 1)); +end: + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + static void _call_with_network_switch(bool_t use_ice, bool_t with_socket_refresh, bool_t enable_rtt) { LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "pauline_tcp_rc"); @@ -5124,6 +5309,10 @@ test_t call_tests[] = { TEST_NO_TAG("Recovered call on network switch in early state 2", recovered_call_on_network_switch_in_early_state_2), TEST_NO_TAG("Recovered call on network switch in early state 3", recovered_call_on_network_switch_in_early_state_3), TEST_NO_TAG("Recovered call on network switch in early state 4", recovered_call_on_network_switch_in_early_state_4), + TEST_NO_TAG("Recovered call on network switch during re-invite 1", recovered_call_on_network_switch_during_reinvite_1), + TEST_NO_TAG("Recovered call on network switch during re-invite 2", recovered_call_on_network_switch_during_reinvite_2), + TEST_NO_TAG("Recovered call on network switch during re-invite 3", recovered_call_on_network_switch_during_reinvite_3), + TEST_NO_TAG("Recovered call on network switch during re-invite 4", recovered_call_on_network_switch_during_reinvite_4), TEST_ONE_TAG("Call with network switch and ICE", call_with_network_switch_and_ice, "ICE"), TEST_ONE_TAG("Call with network switch, ICE and RTT", call_with_network_switch_ice_and_rtt, "ICE"), TEST_NO_TAG("Call with network switch with socket refresh", call_with_network_switch_and_socket_refresh),