Allows incoming call to be accepted even if an outgoing call is in progress. In such case, outgoing call is automatically canceled.

This commit is contained in:
Jehan Monnier 2015-05-04 12:23:44 +02:00
parent 705384ebb4
commit e8aef044fb
5 changed files with 159 additions and 27 deletions

View file

@ -245,18 +245,6 @@ static bool_t already_a_call_with_remote_address(const LinphoneCore *lc, const L
return FALSE;
}
static bool_t already_an_outgoing_call_pending(LinphoneCore *lc){
MSList *elem;
for(elem=lc->calls;elem!=NULL;elem=elem->next){
LinphoneCall *call=(LinphoneCall*)elem->data;
if (call->state==LinphoneCallOutgoingInit
|| call->state==LinphoneCallOutgoingProgress
|| call->state==LinphoneCallOutgoingRinging){
return TRUE;
}
}
return FALSE;
}
static void call_received(SalOp *h){
LinphoneCore *lc=(LinphoneCore *)sal_get_user_pointer(sal_op_get_sal(h));
@ -264,9 +252,9 @@ static void call_received(SalOp *h){
char *alt_contact;
LinphoneAddress *from_addr=NULL;
LinphoneAddress *to_addr=NULL;
/*this mode is deprcated because probably useless*/
bool_t prevent_colliding_calls=lp_config_get_int(lc->config,"sip","prevent_colliding_calls",FALSE);
LinphoneAddress *from_address_to_search_if_me=NULL; /*address used to know if I'm the caller*/
SalMediaDescription *md;
const char * p_asserted_id;
/* first check if we can answer successfully to this invite */
if (linphone_presence_model_get_basic_status(lc->presence_model) == LinphonePresenceBasicStatusClosed) {
@ -300,9 +288,9 @@ static void call_received(SalOp *h){
sal_op_release(h);
return;
}
p_asserted_id = sal_custom_header_find(sal_op_get_recv_custom_header(h),"P-Asserted-Identity");
/*in some situation, better to trust the network rather than the UAC*/
if (lp_config_get_int(lc->config,"sip","call_logs_use_asserted_id_instead_of_from",0)) {
const char * p_asserted_id = sal_custom_header_find(sal_op_get_recv_custom_header(h),"P-Asserted-Identity");
LinphoneAddress *p_asserted_id_addr;
if (!p_asserted_id) {
ms_warning("No P-Asserted-Identity header found so cannot use it for op [%p] instead of from",h);
@ -321,13 +309,26 @@ static void call_received(SalOp *h){
from_addr=linphone_address_new(sal_op_get_from(h));
to_addr=linphone_address_new(sal_op_get_to(h));
if ((already_a_call_with_remote_address(lc,from_addr) && prevent_colliding_calls) || already_an_outgoing_call_pending(lc)){
ms_warning("Receiving a call while one is initiated, refusing this one with busy message.");
if (sal_op_get_privacy(h) == SalPrivacyNone) {
from_address_to_search_if_me=linphone_address_clone(from_addr);
} else if (p_asserted_id) {
from_address_to_search_if_me = linphone_address_new(p_asserted_id);
} else {
ms_warning ("Hidden from identity, don't know if it's me");
}
if (from_address_to_search_if_me && already_a_call_with_remote_address(lc,from_address_to_search_if_me)){
char *addr = linphone_address_as_string(from_addr);
ms_warning("Receiving a call while one with same address [%s] is initiated, refusing this one with busy message.",addr);
sal_call_decline(h,SalReasonBusy,NULL);
sal_op_release(h);
linphone_address_destroy(from_addr);
linphone_address_destroy(to_addr);
linphone_address_destroy(from_address_to_search_if_me);
ms_free(addr);
return;
} else if (from_address_to_search_if_me) {
linphone_address_destroy(from_address_to_search_if_me);
}
call=linphone_call_new_incoming(lc,from_addr,to_addr,h);

View file

@ -3721,6 +3721,7 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
SalOp *replaced;
SalMediaDescription *new_md;
bool_t was_ringing=FALSE;
MSList * iterator;
if (call==NULL){
//if just one call is present answer the only one ...
@ -3741,6 +3742,28 @@ int linphone_core_accept_call_with_params(LinphoneCore *lc, LinphoneCall *call,
break;
}
for (iterator=ms_list_copy(linphone_core_get_calls(lc));iterator!=NULL;iterator=iterator->next) {
LinphoneCall *a_call=(LinphoneCall*)iterator->data;
if (a_call==call) continue;
switch(a_call->state){
case LinphoneCallOutgoingInit:
case LinphoneCallOutgoingProgress:
case LinphoneCallOutgoingRinging:
case LinphoneCallOutgoingEarlyMedia:
ms_message("Already existing call [%p] in state [%s], canceling it before accepting new call [%p]" ,a_call
,linphone_call_state_to_string(a_call->state)
,call);
linphone_core_terminate_call(lc,a_call);
break;
default:
break; /*nothing to do*/
}
}
if (iterator) ms_list_free(iterator);
/* check if this call is supposed to replace an already running one*/
replaced=sal_call_get_replaces(call->op);
if (replaced){
@ -4138,9 +4161,17 @@ static int remote_address_compare(LinphoneCall *call, const LinphoneAddress *rad
* @ingroup call_control
*/
LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address){
LinphoneCall *call=NULL;
LinphoneAddress *raddr=linphone_address_new(remote_address);
if (raddr) {
call=linphone_core_get_call_by_remote_address2(lc, raddr);
linphone_address_unref(raddr);
}
return call;
}
LinphoneCall *linphone_core_get_call_by_remote_address2(LinphoneCore *lc, LinphoneAddress *raddr){
MSList *elem=ms_list_find_custom(lc->calls,(int (*)(const void*,const void *))remote_address_compare,raddr);
linphone_address_unref(raddr);
if (elem) return (LinphoneCall*) elem->data;
return NULL;
}

View file

@ -2237,6 +2237,17 @@ LINPHONE_PUBLIC LinphoneCallParams *linphone_core_create_call_params(LinphoneCor
LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address(LinphoneCore *lc, const char *remote_address);
/**
* Get the call with the remote_address specified
* @param lc
* @param remote_address
* @return the LinphoneCall of the call if found
*
* @ingroup call_control
*/
LINPHONE_PUBLIC LinphoneCall *linphone_core_get_call_by_remote_address2(LinphoneCore *lc, LinphoneAddress *remote_address);
/**
* Send the specified dtmf.
*

View file

@ -207,6 +207,7 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
LinphoneCallParams *caller_params = caller_test_params->base;
LinphoneCallParams *callee_params = callee_test_params->base;
bool_t did_receive_call;
LinphoneCall *callee_call=NULL;
setup_sdp_handling(caller_test_params, caller_mgr);
setup_sdp_handling(callee_test_params, callee_mgr);
@ -229,7 +230,8 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
if (!did_receive_call) return 0;
CU_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc));
if (linphone_core_get_calls_nb(callee_mgr->lc)<=1)
CU_ASSERT_TRUE(linphone_core_inc_invite_pending(callee_mgr->lc));
CU_ASSERT_EQUAL(caller_mgr->stat.number_of_LinphoneCallOutgoingProgress,initial_caller.number_of_LinphoneCallOutgoingProgress+1);
@ -247,6 +249,8 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
CU_ASSERT_PTR_NOT_NULL(linphone_core_get_current_call_remote_address(callee_mgr->lc));
callee_call=linphone_core_get_call_by_remote_address2(callee_mgr->lc,caller_mgr->identity);
if(!linphone_core_get_current_call(caller_mgr->lc) || !linphone_core_get_current_call(callee_mgr->lc) || !linphone_core_get_current_call_remote_address(callee_mgr->lc)) {
return 0;
} else if (caller_mgr->identity){
@ -256,21 +260,23 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
if (linphone_call_params_get_privacy(linphone_call_get_current_params(linphone_core_get_current_call(caller_mgr->lc))) == LinphonePrivacyNone) {
/*don't check in case of p asserted id*/
if (!lp_config_get_int(callee_mgr->lc->config,"sip","call_logs_use_asserted_id_instead_of_from",0))
CU_ASSERT_TRUE(linphone_address_weak_equal(callee_from,linphone_core_get_current_call_remote_address(callee_mgr->lc)));
CU_ASSERT_TRUE(linphone_address_weak_equal(callee_from,linphone_call_get_remote_address(callee_call)));
} else {
CU_ASSERT_FALSE(linphone_address_weak_equal(callee_from,linphone_core_get_current_call_remote_address(callee_mgr->lc)));
CU_ASSERT_FALSE(linphone_address_weak_equal(callee_from,linphone_call_get_remote_address(linphone_core_get_current_call(callee_mgr->lc))));
}
linphone_address_destroy(callee_from);
}
if (callee_params){
linphone_core_accept_call_with_params(callee_mgr->lc,linphone_core_get_current_call(callee_mgr->lc),callee_params);
linphone_core_accept_call_with_params(callee_mgr->lc,callee_call,callee_params);
}else if (build_callee_params){
LinphoneCallParams *default_params=linphone_core_create_call_params(callee_mgr->lc,linphone_core_get_current_call(callee_mgr->lc));
LinphoneCallParams *default_params=linphone_core_create_call_params(callee_mgr->lc,callee_call);
ms_message("Created default call params with video=%i", linphone_call_params_video_enabled(default_params));
linphone_core_accept_call_with_params(callee_mgr->lc,linphone_core_get_current_call(callee_mgr->lc),default_params);
linphone_core_accept_call_with_params(callee_mgr->lc,callee_call,default_params);
linphone_call_params_destroy(default_params);
}else{
linphone_core_accept_call(callee_mgr->lc,linphone_core_get_current_call(callee_mgr->lc));
linphone_core_accept_call(callee_mgr->lc,callee_call);
}
CU_ASSERT_TRUE(wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallConnected,initial_callee.number_of_LinphoneCallConnected+1));
@ -291,7 +297,7 @@ bool_t call_with_params2(LinphoneCoreManager* caller_mgr
|| (linphone_core_get_media_encryption(caller_mgr->lc) == LinphoneMediaEncryptionDTLS) /*also take care of caller policy*/ )
wait_for(callee_mgr->lc,caller_mgr->lc,&callee_mgr->stat.number_of_LinphoneCallEncryptedOn,initial_callee.number_of_LinphoneCallEncryptedOn+1);
{
const LinphoneCallParams* call_param = linphone_call_get_current_params(linphone_core_get_current_call(callee_mgr->lc));
const LinphoneCallParams* call_param = linphone_call_get_current_params(callee_call);
CU_ASSERT_EQUAL(linphone_call_params_get_media_encryption(call_param),linphone_core_get_media_encryption(caller_mgr->lc));
call_param = linphone_call_get_current_params(linphone_core_get_current_call(caller_mgr->lc));
CU_ASSERT_EQUAL(linphone_call_params_get_media_encryption(call_param),linphone_core_get_media_encryption(caller_mgr->lc));
@ -902,6 +908,21 @@ static void early_declined_call(void) {
linphone_core_manager_destroy(pauline);
}
static void call_busy_when_calling_self(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCall *out_call=linphone_core_invite_address(marie->lc,marie->identity);
linphone_call_ref(out_call);
/*wait until flexisip transfers the busy...*/
CU_ASSERT_TRUE(wait_for_until(marie->lc,marie->lc,&marie->stat.number_of_LinphoneCallError,1,33000));
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallError,1);
CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonBusy);
linphone_call_unref(out_call);
linphone_core_manager_destroy(marie);
}
static void call_declined(void) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
@ -4040,6 +4061,7 @@ test_t call_tests[] = {
{ "Early cancelled call", early_cancelled_call},
{ "Call with DNS timeout", call_with_dns_time_out },
{ "Cancelled ringing call", cancelled_ringing_call },
{ "Call busy when calling self", call_busy_when_calling_self},
{ "Simple call", simple_call },
{ "Call with timeouted bye", call_with_timeouted_bye },
{ "Direct call over IPv6", direct_call_over_ipv6},

View file

@ -115,6 +115,70 @@ static void call_waiting_indication_with_privacy(void) {
call_waiting_indication_with_param(TRUE);
}
static void incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallState state) {
LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc");
LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc");
LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc");
MSList* lcs;
LinphoneCallParams *laure_params=linphone_core_create_default_call_parameters(laure->lc);
LinphoneCallParams *marie_params=linphone_core_create_default_call_parameters(marie->lc);
lcs=ms_list_append(NULL,marie->lc);
lcs=ms_list_append(lcs,pauline->lc);
lcs=ms_list_append(lcs,laure->lc);
if (state==LinphoneCallOutgoingRinging || state==LinphoneCallOutgoingEarlyMedia) {
CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address_with_params(marie->lc,pauline->identity,marie_params));
CU_ASSERT_TRUE(wait_for(marie->lc
,pauline->lc
,&pauline->stat.number_of_LinphoneCallIncomingReceived
,1));
if (state==LinphoneCallOutgoingEarlyMedia)
linphone_core_accept_early_media(pauline->lc,linphone_core_get_current_call(pauline->lc));
CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingProgress,1);
CU_ASSERT_TRUE(wait_for(marie->lc
,pauline->lc
,state==LinphoneCallOutgoingEarlyMedia?&marie->stat.number_of_LinphoneCallOutgoingEarlyMedia:&marie->stat.number_of_LinphoneCallOutgoingRinging
,1));
} else if (state==LinphoneCallOutgoingProgress) {
CU_ASSERT_PTR_NOT_NULL(linphone_core_invite_address(marie->lc,pauline->identity));
} else {
ms_error("Unsupported state");
return;
}
CU_ASSERT_TRUE(call_with_caller_params(laure,marie,laure_params));
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000));
linphone_core_terminate_all_calls(marie->lc);
CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallEnd,1,10000));
CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneCallEnd,1,10000));
CU_ASSERT_TRUE(wait_for_list(lcs,&laure->stat.number_of_LinphoneCallEnd,1,10000));
linphone_core_manager_destroy(marie);
linphone_core_manager_destroy(pauline);
linphone_core_manager_destroy(laure);
ms_list_free(lcs);
}
static void incoming_call_accepted_when_outgoing_call_in_progress(void) {
incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallOutgoingProgress);
}
static void incoming_call_accepted_when_outgoing_call_in_outgoing_ringing(void) {
incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallOutgoingRinging);
}
static void incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_media(void) {
incoming_call_accepted_when_outgoing_call_in_state(LinphoneCallOutgoingEarlyMedia);
}
static void simple_conference_base(LinphoneCoreManager* marie, LinphoneCoreManager* pauline, LinphoneCoreManager* laure) {
stats initial_marie_stat;
@ -437,7 +501,10 @@ test_t multi_call_tests[] = {
{ "Simple call transfer", simple_call_transfer },
{ "Unattended call transfer", unattended_call_transfer },
{ "Unattended call transfer with error", unattended_call_transfer_with_error },
{ "Call transfer existing call outgoing call", call_transfer_existing_call_outgoing_call }
{ "Call transfer existing call outgoing call", call_transfer_existing_call_outgoing_call },
{ "Incoming call accepted when outgoing call in progress",incoming_call_accepted_when_outgoing_call_in_progress},
{ "Incoming call accepted when outgoing call in outgoing ringing",incoming_call_accepted_when_outgoing_call_in_outgoing_ringing},
{ "Incoming call accepted when outgoing call in outgoing ringing early media",incoming_call_accepted_when_outgoing_call_in_outgoing_ringing_early_media},
};
test_suite_t multi_call_test_suite = {