diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index f3b486b58..224a802b5 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -357,6 +357,7 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_ belle_sip_server_transaction_t *server_transaction=belle_sip_transaction_terminated_event_get_server_transaction(event); belle_sip_request_t* req; belle_sip_response_t* resp; + int code = 0; bool_t release_call=FALSE; if (client_transaction) { @@ -366,12 +367,19 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_ req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)); resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(server_transaction)); } - if (op->state ==SalOpStateTerminating + if (resp) code = belle_sip_response_get_status_code(resp); + + if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(req))==0 - && (!resp || (belle_sip_response_get_status_code(resp) !=401 - && belle_sip_response_get_status_code(resp) !=407)) + && (!resp || (belle_sip_response_get_status_code(resp) != 401 + && belle_sip_response_get_status_code(resp) != 407)) && op->dialog==NULL) { release_call=TRUE; + }else if (op->state == SalOpStateEarly && code < 200){ + /*call terminated early*/ + sal_error_info_set(&op->error_info,SalReasonIOError,503,"I/O error",NULL); + op->base.root->callbacks.call_failure(op); + release_call=TRUE; } if (server_transaction){ if (op->pending_server_trans==server_transaction){ @@ -546,10 +554,9 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t drop_op = TRUE; } break; - } /* else same behavior as for EARLY state*/ + } /* else same behavior as for EARLY state, thus NO BREAK*/ } case BELLE_SIP_DIALOG_EARLY: { - //hmm probably a cancel if (strcmp("CANCEL",method)==0) { if(belle_sip_request_event_get_server_transaction(event)) { /*first answer 200 ok to cancel*/ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index d9cd98eb4..4a4c2d854 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -4096,6 +4096,7 @@ static void linphone_call_lost(LinphoneCall *call, LinphoneReason reason){ linphone_core_notify_display_warning(lc, temp); linphone_core_terminate_call(lc,call); linphone_core_play_named_tone(lc,LinphoneToneCallLost); + ms_free(temp); } static void change_ice_media_destinations(LinphoneCall *call) { @@ -4721,11 +4722,14 @@ void linphone_call_set_broken(LinphoneCall *call){ case LinphoneCallOutgoingEarlyMedia: case LinphoneCallIncomingReceived: case LinphoneCallIncomingEarlyMedia: - linphone_call_lost(call, LinphoneReasonIOError); + /*during the early states, the SAL layer reports the failure from the dialog or transaction layer, + * hence, there is nothing special to do*/ break; case LinphoneCallStreamsRunning: case LinphoneCallPaused: case LinphoneCallPausedByRemote: + /*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; break; default: diff --git a/tester/call_tester.c b/tester/call_tester.c index bcd069bbd..04d911e57 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -3771,6 +3771,8 @@ static void video_call_snapshot(void) { end_call(marie, pauline); } ms_free(filename); + linphone_call_params_unref(marieParams); + linphone_call_params_unref(paulineParams); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -5079,6 +5081,41 @@ static void call_record_with_custom_rtp_modifier(void) { custom_rtp_modifier(FALSE, TRUE); } +static void _call_with_network_switch_in_early_state(bool_t network_loosed_by_caller){ + LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new(transport_supported(LinphoneTransportTls) ? "pauline_rc" : "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; + if (network_loosed_by_caller){ + linphone_core_set_network_reachable(marie->lc, FALSE); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->stat.number_of_LinphoneCallError, 1)); + linphone_core_set_network_reachable(marie->lc, TRUE); + linphone_core_terminate_call(pauline->lc, linphone_core_get_current_call(pauline->lc)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallEnd, 1)); + }else{ + linphone_core_set_network_reachable(pauline->lc, FALSE); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallError, 1)); + linphone_core_set_network_reachable(pauline->lc, TRUE); + linphone_core_terminate_call(marie->lc, linphone_core_get_current_call(marie->lc)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &marie->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_in_early_state_1(){ + _call_with_network_switch_in_early_state(TRUE); +} + +static void call_with_network_switch_in_early_state_2(){ + _call_with_network_switch_in_early_state(FALSE); +} + 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"); @@ -5507,6 +5544,8 @@ test_t call_tests[] = { { "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 with network switch", call_with_network_switch }, + { "Call with network switch in early state 1", call_with_network_switch_in_early_state_1 }, + { "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 } }; diff --git a/tester/tester.c b/tester/tester.c index 79c576ec6..398040b2d 100644 --- a/tester/tester.c +++ b/tester/tester.c @@ -99,6 +99,7 @@ static void auth_info_requested(LinphoneCore *lc, const char *realm, const char void reset_counters( stats* counters) { if (counters->last_received_chat_message) linphone_chat_message_unref(counters->last_received_chat_message); + if (counters->last_received_info_message) linphone_info_message_destroy(counters->last_received_info_message); memset(counters,0,sizeof(stats)); } @@ -373,6 +374,7 @@ void linphone_core_manager_destroy(LinphoneCoreManager* mgr) { if (mgr->stat.last_received_chat_message) { linphone_chat_message_unref(mgr->stat.last_received_chat_message); } + if (mgr->stat.last_received_info_message) linphone_info_message_destroy(mgr->stat.last_received_info_message); if (mgr->lc){ const char *record_file=linphone_core_get_record_file(mgr->lc); int unterminated_calls;