Repair call where a connection loss occurs on the caller side by sending a new INVITE with a Replaces header.

This commit is contained in:
Ghislain MARY 2016-09-30 16:52:10 +02:00
parent 73539fefa1
commit 1fce1b1581
8 changed files with 106 additions and 12 deletions

View file

@ -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);

View file

@ -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);
}

View file

@ -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);

View file

@ -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];

View file

@ -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");

View file

@ -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;
}
}

View file

@ -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 */

View file

@ -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),