diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index c74922b57..b9ee908ee 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -173,7 +173,7 @@ static void cancelling_invite(SalOp* op ){ op->state=SalOpStateTerminating; } -static void call_response_event(void *op_base, const belle_sip_response_event_t *event){ +static void call_process_response(void *op_base, const belle_sip_response_event_t *event){ SalOp* op = (SalOp*)op_base; belle_sip_request_t* ack; belle_sip_dialog_state_t dialog_state; @@ -198,16 +198,20 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t case BELLE_SIP_DIALOG_EARLY: { if (strcmp("INVITE",belle_sip_request_get_method(req))==0 ) { if (op->state == SalOpStateTerminating) { + /*check if CANCEL was sent before*/ if (strcmp("CANCEL",belle_sip_request_get_method(belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(op->pending_client_trans))))!=0) { + /*it wasn't sent */ if (code<200) { cancelling_invite(op); - } else if (code<400) { - sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE")); - } else { - /*nop ?*/ + }else{ + /* no need to send the INVITE because the UAS rejected the INVITE*/ + if (op->dialog==NULL) call_set_released(op); } } else { - /*nop, already cancelled*/ + /*it was sent already, so just expect the 487 or any error response to send the call_released() notification*/ + if (code>=300){ + if (op->dialog==NULL) call_set_released(op); + } } } else if (code >= 180 && code<300) { handle_sdp_from_response(op,response); @@ -221,54 +225,51 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t break; case BELLE_SIP_DIALOG_CONFIRMED: { switch (op->state) { - case SalOpStateEarly:/*invite case*/ - case SalOpStateActive: /*re-invite case*/ - if (code >=200 - && code<300 - && strcmp("INVITE",belle_sip_request_get_method(req))==0) { - handle_sdp_from_response(op,response); - ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog)); - if (ack==NULL) { - ms_error("This call has been already terminated."); - return ; + case SalOpStateEarly:/*invite case*/ + case SalOpStateActive: /*re-invite case*/ + if (code >=200 + && code<300 + && strcmp("INVITE",belle_sip_request_get_method(req))==0) { + handle_sdp_from_response(op,response); + ack=belle_sip_dialog_create_ack(op->dialog,belle_sip_dialog_get_local_seq_number(op->dialog)); + if (ack==NULL) { + ms_error("This call has been already terminated."); + return ; + } + if (op->sdp_answer){ + set_sdp(BELLE_SIP_MESSAGE(ack),op->sdp_answer); + belle_sip_object_unref(op->sdp_answer); + op->sdp_answer=NULL; + } + belle_sip_dialog_send_ack(op->dialog,ack); + op->base.root->callbacks.call_accepted(op); /*INVITE*/ + op->state=SalOpStateActive; + } else if (code >= 300 && strcmp("INVITE",belle_sip_request_get_method(req))==0){ + call_set_error(op,response); + } else { + /*ignoring*/ } - if (op->sdp_answer){ - set_sdp(BELLE_SIP_MESSAGE(ack),op->sdp_answer); - belle_sip_object_unref(op->sdp_answer); - op->sdp_answer=NULL; - } - belle_sip_dialog_send_ack(op->dialog,ack); - op->base.root->callbacks.call_accepted(op); /*INVITE*/ - - op->state=SalOpStateActive; - } else if (code >= 300 && strcmp("INVITE",belle_sip_request_get_method(req))==0){ - call_set_error(op,response); - } else { - /*ignoring*/ - } - break; - case SalOpStateTerminating: - //FIXME send bye - case SalOpStateTerminated: - default: - ms_error("call op [%p] receive answer [%i] not implemented",op,code); + break; + case SalOpStateTerminating: + sal_op_send_request(op,belle_sip_dialog_create_request(op->dialog,"BYE")); + break; + case SalOpStateTerminated: + default: + ms_error("Call op [%p] receives unexpected answer [%i] while in state [%s].",op,code, sal_op_state_to_string(op->state)); } - break; } + break; case BELLE_SIP_DIALOG_TERMINATED: { if (code >= 300){ call_set_error(op,response); } - break; } - /* no break */ + break; default: { ms_error("call op [%p] receive answer [%i] not implemented",op,code); } - /* no break */ + break; } - - } static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { @@ -553,7 +554,7 @@ int sal_call(SalOp *op, const char *from, const char *to){ void sal_op_call_fill_cbs(SalOp*op) { op->callbacks.process_io_error=call_process_io_error; - op->callbacks.process_response_event=call_response_event; + op->callbacks.process_response_event=call_process_response; op->callbacks.process_timeout=call_process_timeout; op->callbacks.process_transaction_terminated=call_process_transaction_terminated; op->callbacks.process_request_event=process_request_event; @@ -741,15 +742,16 @@ int sal_call_terminate(SalOp *op){ if (op->dir == SalOpDirIncoming) { sal_call_decline(op, SalReasonDeclined,NULL); op->state=SalOpStateTerminated; - } else if (op->pending_client_trans - && belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){ - cancelling_invite(op); - break; - } else { - /*just schedule call released*/ - if (op->dialog==NULL) belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack) - ,(belle_sip_callback_t) call_set_released - , op); + } else if (op->pending_client_trans){ + if (belle_sip_transaction_get_state(BELLE_SIP_TRANSACTION(op->pending_client_trans)) == BELLE_SIP_TRANSACTION_PROCEEDING){ + cancelling_invite(op); + }else{ + /* Case where the CANCEL cannot be sent because no provisional response was received so far. + * The Op must be kept for the time of the transaction in case a response is received later. + * The state is passed to Terminating to remember to terminate later. + */ + op->state=SalOpStateTerminating; + } } break; } diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index ae9baedc8..5fe48f8c3 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -30,7 +30,9 @@ SalOp * sal_op_new(Sal *sal){ } void sal_op_release(SalOp *op){ - op->state=SalOpStateTerminated; + /*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) + op->state=SalOpStateTerminated; sal_op_set_user_pointer(op,NULL);/*mandatory because releasing op doesn't not mean freeing op. Make sure back pointer will not be used later*/ if (op->refresher) { belle_sip_refresher_stop(op->refresher); @@ -413,6 +415,7 @@ bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,S return FALSE; } } + void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) { /*check if dialog has changed*/ if (dialog && dialog != op->dialog) { diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 64e06a9f5..426c85c3e 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -4917,7 +4917,7 @@ int linphone_core_get_device_rotation(LinphoneCore *lc ) { * **/ void linphone_core_set_device_rotation(LinphoneCore *lc, int rotation) { - ms_message("%s : rotation=%d\n", __FUNCTION__, rotation); + if (rotation!=lc->device_rotation) ms_message("%s : rotation=%d\n", __FUNCTION__, rotation); lc->device_rotation = rotation; #ifdef VIDEO_ENABLED { diff --git a/tester/call_tester.c b/tester/call_tester.c index 309af77f4..59d36a4a5 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -322,6 +322,32 @@ static void call_with_dns_time_out(void) { linphone_core_manager_destroy(marie); } +static void early_cancelled_call(void) { + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_alt_rc"); + + LinphoneCall* out_call = linphone_core_invite(pauline->lc,"sip:marie@sip.example.org"); + + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallOutgoingInit,1)); + linphone_core_terminate_call(pauline->lc,out_call); + + /*since everything is executed in a row, no response can be received from the server, thus the CANCEL cannot be sent. + It will ring at Marie's side.*/ + + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallEnd,1)); + + CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1); + + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallIncomingReceived,1)); + /* now the CANCEL should have been sent and the the call at marie's side should terminate*/ + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallEnd,1)); + + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); +} + static void cancelled_ringing_call(void) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); @@ -1037,6 +1063,7 @@ test_t call_tests[] = { { "Early declined call", early_declined_call }, { "Call declined", call_declined }, { "Cancelled call", cancelled_call }, + { "Early cancelled call", early_cancelled_call}, { "Call with DNS timeout", call_with_dns_time_out }, { "Cancelled ringing call", cancelled_ringing_call }, { "Simple call", simple_call },