Try to repair calls in early state when a network loss occurs.

This commit is contained in:
Ghislain MARY 2016-09-14 14:27:06 +02:00
parent 6b36c21fa7
commit 73539fefa1
6 changed files with 144 additions and 11 deletions

View file

@ -295,7 +295,7 @@ static void call_process_response(void *op_base, const belle_sip_response_event_
}
} else if (code >=200
&& code<300
&& strcmp("UPDATE",belle_sip_request_get_method(req))==0) {
&& strcmp("UPDATE",method)==0) {
handle_sdp_from_response(op,response);
op->base.root->callbacks.call_accepted(op);
}
@ -496,6 +496,7 @@ static int process_sdp_for_invite(SalOp* op,belle_sip_request_t* invite) {
belle_sdp_session_description_t* sdp;
int err=0;
SalReason reason;
if (extract_sdp(op,BELLE_SIP_MESSAGE(invite),&sdp,&reason)==0) {
if (sdp){
op->sdp_offering=FALSE;
@ -878,9 +879,9 @@ int sal_call_accept(SalOp*h){
return -1;
}
ms_message("Accepting server transaction [%p] on op [%p]", transaction, h);
/* sends a 200 OK */
response = sal_op_create_response_from_request(h,belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(transaction)),200);
if (response==NULL){
ms_error("Fail to build answer for call");
return -1;
@ -1086,6 +1087,11 @@ int sal_call_is_offerer(const SalOp *h){
return h->sdp_offering;
}
bool_t sal_call_compare_op(const SalOp *op1, const SalOp *op2) {
if (strcmp(op1->base.call_id, op2->base.call_id) == 0) return TRUE;
return FALSE;
}
bool_t sal_call_dialog_request_pending(const SalOp *op) {
return belle_sip_dialog_request_pending(op->dialog) ? TRUE : FALSE;
}

View file

@ -240,9 +240,24 @@ static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const L
}
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 *call = (LinphoneCall *)bctbx_list_get_data(it);
if (call->broken && sal_call_compare_op(h, call->op)) {
return call;
}
it = bctbx_list_next(it);
}
return NULL;
}
static void call_received(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
LinphoneCall *call;
LinphoneCall *replaced_call;
char *alt_contact;
LinphoneAddress *from_addr=NULL;
LinphoneAddress *to_addr=NULL;
@ -250,6 +265,13 @@ static void call_received(SalOp *h){
SalMediaDescription *md;
const char * p_asserted_id;
/* Look if this INVITE is for a call that has already been notified but broken because of network failure */
replaced_call = look_for_broken_call_to_replace(h, lc);
if (replaced_call != NULL) {
linphone_call_replace_op(replaced_call, h);
return;
}
/* first check if we can answer successfully to this invite */
if (linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed) {
LinphonePresenceActivity *activity = linphone_presence_model_get_activity(lc->presence_model);

View file

@ -5027,7 +5027,7 @@ void linphone_call_set_broken(LinphoneCall *call){
case LinphoneCallIncomingEarlyMedia:
/*during the early states, the SAL layer reports the failure from the dialog or transaction layer,
* hence, there is nothing special to do*/
break;
//break;
case LinphoneCallStreamsRunning:
case LinphoneCallPaused:
case LinphoneCallPausedByRemote:
@ -5056,13 +5056,15 @@ void linphone_call_repair_if_broken(LinphoneCall *call){
case LinphoneCallStreamsRunning:
case LinphoneCallPaused:
case LinphoneCallPausedByRemote:
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);
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);
}
params = linphone_core_create_call_params(call->core, call);
linphone_core_update_call(call->core, call, params);
linphone_call_params_unref(params);
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));
@ -5080,3 +5082,11 @@ void linphone_call_refresh_sockets(LinphoneCall *call){
}
}
}
void linphone_call_replace_op(LinphoneCall *call, SalOp *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);
}

View file

@ -389,6 +389,7 @@ void linphone_call_log_destroy(LinphoneCallLog *cl);
void linphone_call_set_transfer_state(LinphoneCall* call, LinphoneCallState state);
LinphonePlayer *linphone_call_build_player(LinphoneCall*call);
void linphone_call_refresh_sockets(LinphoneCall *call);
void linphone_call_replace_op(LinphoneCall *call, SalOp *op);
LinphoneCallParams * linphone_call_params_new(void);
SalMediaProto get_proto_from_call_params(const LinphoneCallParams *params);

View file

@ -751,6 +751,8 @@ bool_t sal_call_autoanswer_asked(SalOp *op);
void sal_call_send_vfu_request(SalOp *h);
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);
/* Call test API */

View file

@ -4189,6 +4189,95 @@ static void call_with_network_switch_in_early_state_2(void){
_call_with_network_switch_in_early_state(FALSE);
}
static void recovered_call_on_network_switch_in_early_state_1(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(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_LinphoneCallOutgoingRinging, 2));
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));
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_2(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);
wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_NetworkReachableTrue, 2);
wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000);
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));
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("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);
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, &marie->stat.number_of_LinphoneCallStreamsRunning, 1));
BC_ASSERT_TRUE(wait_for(marie->lc, pauline->lc, &pauline->stat.number_of_LinphoneCallStreamsRunning, 1));
BC_ASSERT_TRUE(sal_call_dialog_request_pending(incoming_call->op));
wait_for_until(marie->lc, pauline->lc, NULL, 1, 2000);
BC_ASSERT_FALSE(sal_call_dialog_request_pending(incoming_call->op));
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");
@ -5000,6 +5089,9 @@ test_t call_tests[] = {
TEST_NO_TAG("Call with network switch", call_with_network_switch),
TEST_NO_TAG("Call with network switch in early state 1", call_with_network_switch_in_early_state_1),
TEST_NO_TAG("Call with network switch in early state 2", call_with_network_switch_in_early_state_2),
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_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),