diff --git a/coreapi/bellesip_sal/sal_impl.h b/coreapi/bellesip_sal/sal_impl.h index e0fc4fc77..174d2b435 100644 --- a/coreapi/bellesip_sal/sal_impl.h +++ b/coreapi/bellesip_sal/sal_impl.h @@ -130,6 +130,7 @@ SalOp* sal_op_ref(SalOp* op); void* sal_op_unref(SalOp* op); void sal_op_release_impl(SalOp *op); +void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces); void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message); int sal_op_send_request(SalOp* op, belle_sip_request_t* request); int sal_op_send_request_with_expires(SalOp* op, belle_sip_request_t* request,int expires); diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 06dea0cea..956c4a556 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -1095,3 +1095,16 @@ bool_t sal_call_compare_op(const SalOp *op1, const SalOp *op2) { bool_t sal_call_dialog_request_pending(const SalOp *op) { return belle_sip_dialog_request_pending(op->dialog) ? TRUE : FALSE; } + +const char * sal_call_get_local_tag(SalOp *op) { + return belle_sip_dialog_get_local_tag(op->dialog); +} + +const char * sal_call_get_remote_tag(SalOp *op) { + return belle_sip_dialog_get_remote_tag(op->dialog); +} + +void sal_call_set_replaces(SalOp *op, const char *call_id, const char *from_tag, const char *to_tag) { + belle_sip_header_replaces_t *replaces = belle_sip_header_replaces_create(call_id, from_tag, to_tag); + sal_op_set_replaces(op, replaces); +} diff --git a/coreapi/bellesip_sal/sal_op_call_transfer.c b/coreapi/bellesip_sal/sal_op_call_transfer.c index a0aac3c20..8adb8564f 100644 --- a/coreapi/bellesip_sal/sal_op_call_transfer.c +++ b/coreapi/bellesip_sal/sal_op_call_transfer.c @@ -22,13 +22,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. /*call transfer*/ -static void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces) { - if (op->replaces){ - belle_sip_object_unref(op->replaces); - } - op->replaces=replaces; - belle_sip_object_ref(op->replaces); -} static void sal_op_set_referred_by(SalOp* op,belle_sip_header_referred_by_t* referred_by) { if (op->referred_by){ belle_sip_object_unref(op->referred_by); diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index aa1b7ca55..2608b453e 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -30,6 +30,11 @@ SalOp * sal_op_new(Sal *sal){ return op; } +void sal_op_kill_dialog(SalOp *op) { + ms_warning("op [%p]: force kill of dialog [%p]", op, op->dialog); + belle_sip_dialog_delete(op->dialog); +} + void sal_op_release(SalOp *op){ /*if in terminating state, keep this state because it means we are waiting for a response to be able to terminate the operation.*/ if (op->state!=SalOpStateTerminating) @@ -235,6 +240,14 @@ int sal_ping(SalOp *op, const char *from, const char *to){ return sal_op_send_request(op,sal_op_build_request(op,"OPTIONS")); } +void sal_op_set_replaces(SalOp* op,belle_sip_header_replaces_t* replaces) { + if (op->replaces){ + belle_sip_object_unref(op->replaces); + } + op->replaces=replaces; + belle_sip_object_ref(op->replaces); +} + void sal_op_set_remote_ua(SalOp*op,belle_sip_message_t* message) { belle_sip_header_user_agent_t* user_agent=belle_sip_message_get_header_by_type(message,belle_sip_header_user_agent_t); char user_agent_string[256]; diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index d86e942a6..f010c9e3e 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -244,8 +244,12 @@ static LinphoneCall * look_for_broken_call_to_replace(SalOp *h, LinphoneCore *lc const bctbx_list_t *calls = linphone_core_get_calls(lc); const bctbx_list_t *it = calls; while (it != NULL) { + LinphoneCall *replaced_call = NULL; LinphoneCall *call = (LinphoneCall *)bctbx_list_get_data(it); - if (call->broken && sal_call_compare_op(h, call->op)) { + SalOp *replaced_op = sal_call_get_replaces(h); + if (replaced_op) replaced_call = (LinphoneCall*)sal_op_get_user_pointer(replaced_op); + if ((call->broken && sal_call_compare_op(h, call->op)) + || (replaced_call && replaced_call == call)){ return call; } it = bctbx_list_next(it); @@ -443,8 +447,7 @@ static void call_ringing(SalOp *h){ /*already doing early media */ return; } - if (lc->ringstream!=NULL) return;/*already ringing !*/ - start_remote_ring(lc, call); + if (lc->ringstream == NULL) start_remote_ring(lc, call); ms_message("Remote ringing..."); linphone_core_notify_display_status(lc,_("Remote ringing...")); linphone_call_set_state(call,LinphoneCallOutgoingRinging,"Remote ringing"); diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 48d4cb77f..9ce22d197 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -5042,6 +5042,16 @@ void linphone_call_set_broken(LinphoneCall *call){ } } +static void linphone_call_repair_by_invite_with_replaces(LinphoneCall *call) { + const char *call_id = sal_op_get_call_id(call->op); + const char *from_tag = sal_call_get_local_tag(call->op); + const char *to_tag = sal_call_get_remote_tag(call->op); + sal_op_kill_dialog(call->op); + linphone_call_create_op(call); + sal_call_set_replaces(call->op, call_id, from_tag, to_tag); + linphone_core_start_invite(call->core, call, NULL); +} + void linphone_call_repair_if_broken(LinphoneCall *call){ LinphoneCallParams *params; @@ -5066,6 +5076,12 @@ void linphone_call_repair_if_broken(LinphoneCall *call){ linphone_call_params_unref(params); } break; + case LinphoneCallOutgoingEarlyMedia: + case LinphoneCallOutgoingInit: + case LinphoneCallOutgoingProgress: + case LinphoneCallOutgoingRinging: + 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)); call->broken = FALSE; @@ -5084,9 +5100,30 @@ void linphone_call_refresh_sockets(LinphoneCall *call){ } void linphone_call_replace_op(LinphoneCall *call, SalOp *op) { + switch (linphone_call_get_state(call)) { + case LinphoneCallConnected: + case LinphoneCallStreamsRunning: + sal_call_terminate(call->op); + break; + default: + break; + } + sal_op_kill_dialog(call->op); sal_op_release(call->op); call->op = op; sal_op_set_user_pointer(call->op, call); sal_call_set_local_media_description(call->op, call->localdesc); - sal_call_notify_ringing(call->op, (linphone_call_get_state(call) == LinphoneCallIncomingEarlyMedia) ? TRUE : FALSE); + switch (linphone_call_get_state(call)) { + case LinphoneCallIncomingEarlyMedia: + case LinphoneCallIncomingReceived: + sal_call_notify_ringing(call->op, (linphone_call_get_state(call) == LinphoneCallIncomingEarlyMedia) ? TRUE : FALSE); + break; + case LinphoneCallConnected: + case LinphoneCallStreamsRunning: + sal_call_accept(call->op); + break; + default: + ms_warning("linphone_call_replace_op(): don't know what to do in state [%s]", linphone_call_state_to_string(call->state)); + break; + } } diff --git a/include/sal/sal.h b/include/sal/sal.h index 7f09e7b64..a2f5072bd 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -679,6 +679,7 @@ SalOp *sal_op_ref(SalOp* h); void sal_op_stop_refreshing(SalOp *op); int sal_op_refresh(SalOp *op); +void sal_op_kill_dialog(SalOp *op); void sal_op_release(SalOp *h); /*same as release, but does not stop refresher if any*/ void* sal_op_unref(SalOp* op); @@ -753,6 +754,9 @@ int sal_call_is_offerer(const SalOp *h); int sal_call_notify_refer_state(SalOp *h, SalOp *newcall); bool_t sal_call_compare_op(const SalOp *op1, const SalOp *op2); bool_t sal_call_dialog_request_pending(const SalOp *op); +const char * sal_call_get_local_tag(SalOp *op); +const char * sal_call_get_remote_tag(SalOp *op); +void sal_call_set_replaces(SalOp *op, const char *call_id, const char *from_tag, const char *to_tag); /* Call test API */ diff --git a/tester/call_single_tester.c b/tester/call_single_tester.c index 8ff6e99b7..6e265113b 100644 --- a/tester/call_single_tester.c +++ b/tester/call_single_tester.c @@ -4227,6 +4227,35 @@ static void recovered_call_on_network_switch_in_early_state_2(void) { 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); + //linphone_core_iterate(pauline->lc); + 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_LinphoneCallStreamsRunning, 1)); + BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 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 recovered_call_on_network_switch_in_early_state_3(void) { + LinphoneCall *incoming_call; + 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; + 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); @@ -4247,7 +4276,7 @@ end: linphone_core_manager_destroy(pauline); } -static void recovered_call_on_network_switch_in_early_state_3(void) { +static void recovered_call_on_network_switch_in_early_state_4(void) { LinphoneCall *incoming_call; LinphoneCoreManager* marie = linphone_core_manager_new("marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new("pauline_tcp_rc"); @@ -5092,6 +5121,7 @@ test_t call_tests[] = { TEST_NO_TAG("Recovered call on network switch in early state 1", recovered_call_on_network_switch_in_early_state_1), 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_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),