From 0d15447140747b35c001fa83277ff8d5e6f3ed89 Mon Sep 17 00:00:00 2001 From: Jehan Monnier Date: Thu, 28 Mar 2013 18:29:26 +0100 Subject: [PATCH] better management of call state machine --- coreapi/bellesip_sal/sal_impl.c | 2 +- coreapi/bellesip_sal/sal_op_call.c | 192 ++++++++++++++++--------- coreapi/bellesip_sal/sal_op_impl.c | 9 +- coreapi/bellesip_sal/sal_op_presence.c | 8 +- coreapi/bellesip_sal/sal_sdp.c | 6 +- tester/call_tester.c | 25 +++- tester/liblinphone_tester.c | 3 +- 7 files changed, 161 insertions(+), 84 deletions(-) diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 3bd0fc1eb..861615fda 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -301,7 +301,6 @@ static void process_response_event(void *user_ctx, const belle_sip_response_even if (op->state == SalOpStateTerminating && strcmp("BYE",belle_sip_request_get_method(request))!=0) { /*only bye are completed*/ belle_sip_message("Op is in state terminating, nothing else to do "); - return; } else { if (op->pending_auth_transaction){ belle_sip_object_unref(op->pending_auth_transaction); @@ -349,6 +348,7 @@ static void process_transaction_terminated(void *user_ctx, const belle_sip_trans ms_error("Unhandled transaction terminated [%p]",trans); } if (op) sal_op_unref(op); /*no longuer need to ref op*/ + } static void process_auth_requested(void *sal, belle_sip_auth_event_t *auth_event) { diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 2bcb0b89a..b9797ec12 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -19,6 +19,26 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "sal_impl.h" #include "offeranswer.h" +static void call_set_released(SalOp* op){ + op->state=SalOpStateTerminated; + op->base.root->callbacks.call_released(op); +} +static void call_set_error(SalOp* op,belle_sip_response_t* response){ + SalError error=SalErrorUnknown; + SalReason sr=SalReasonUnknown; + belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason"); + char* reason=(char*)belle_sip_response_get_reason_phrase(response); + int code = belle_sip_response_get_status_code(response); + if (reason_header){ + reason = ms_strdup_printf("%s %s",reason,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header))); + } + sal_compute_sal_errors_from_code(code,&error,&sr); + op->base.root->callbacks.call_failure(op,error,sr,reason,code); + if (reason_header != NULL){ + ms_free(reason); + } + call_set_released(op); +} static void sdp_process(SalOp *h){ ms_message("Doing SDP offer/answer process of type %s",h->sdp_offering ? "outgoing" : "incoming"); @@ -85,7 +105,7 @@ static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event if (!op->dialog) { /*call terminated very early*/ op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Service Unavailable",503); - op->state=SalOpStateTerminated; + call_set_released(op); } else { /*dialog will terminated shortly, nothing to do*/ } @@ -93,18 +113,51 @@ static void call_process_io_error(void *user_ctx, const belle_sip_io_error_event static void process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { SalOp* op=(SalOp*)ctx; - if (op->dialog) { - if (belle_sip_dialog_get_previous_state(op->dialog) == BELLE_SIP_DIALOG_CONFIRMED) { - /*this is probably a "normal termination from a BYE*/ - op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); - op->state=SalOpStateTerminating; - } else { - /*let the process response handle this case*/ - } + if (op->dialog && op->dialog==belle_sip_dialog_terminated_get_dialog(event)) { + switch(belle_sip_dialog_get_previous_state(op->dialog)) { + case BELLE_SIP_DIALOG_CONFIRMED: + if (op->state!=SalOpStateTerminated && op->state!=SalOpStateTerminating) { + /*this is probably a "normal termination from a BYE*/ + op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); + op->state=SalOpStateTerminating; + } + break; + default: { + belle_sip_transaction_t* trans=belle_sip_dialog_get_last_transaction(op->dialog); + belle_sip_response_t* response=belle_sip_transaction_get_response(trans); + int code = belle_sip_response_get_status_code(response); + if (BELLE_SIP_OBJECT_IS_INSTANCE_OF(trans,belle_sip_client_transaction_t)) { + switch (code) { + case 487: + call_set_released(op); + break; + case 401: + case 407: + if (op->state!=SalOpStateTerminating) { + /*normal termination for chalanged dialog */ + break; + } + default: + call_set_error(op,response); + } + } else { + belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack) + ,(belle_sip_callback_t) call_set_released + , op); + } + } + } belle_sip_object_unref(op->dialog); op->dialog=NULL; + sal_op_unref(op); + } else { + ms_error("dialog unknown for op "); } + + ms_message("Dialog [%p] terminated for op [%p]",belle_sip_dialog_terminated_get_dialog(event) + ,op); + } static void handle_sdp_from_response(SalOp* op,belle_sip_response_t* response) { belle_sdp_session_description_t* sdp; @@ -119,7 +172,7 @@ static void cancelling_invite(SalOp* op ){ ms_message("Cancelling INVITE requets from [%s] to [%s] ",sal_op_get_from(op), sal_op_get_to(op)); cancel = belle_sip_client_transaction_create_cancel(op->pending_inv_client_trans); sal_op_send_request(op,cancel); - op->state=SalOpStateTerminated; + op->state=SalOpStateTerminating; } static void call_response_event(void *op_base, const belle_sip_response_event_t *event){ SalOp* op = (SalOp*)op_base; @@ -129,67 +182,45 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t belle_sip_request_t* req; belle_sip_response_t* response=belle_sip_response_event_get_response(event); int code = belle_sip_response_get_status_code(response); - char* reason; - SalError error=SalErrorUnknown; - SalReason sr=SalReasonUnknown; - belle_sip_header_t* reason_header = belle_sip_message_get_header(BELLE_SIP_MESSAGE(response),"Reason"); + + if (!client_transaction) { ms_warning("Discarding state less response [%i] on op [%p]",code,op); return; } req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(client_transaction)); - reason=(char*)belle_sip_response_get_reason_phrase(response); - if (reason_header){ - reason = ms_strdup_printf("%s %s",reason,belle_sip_header_extension_get_value(BELLE_SIP_HEADER_EXTENSION(reason_header))); - } - /*FIXME should be limited to early/NULL dialog*/ - if (code >=400) { - sal_compute_sal_errors_from_code(code,&error,&sr); - op->base.root->callbacks.call_failure(op,error,sr,reason,code); - op->state=SalOpStateTerminated; - if (reason_header != NULL){ - ms_free(reason); - } - return; - } set_or_update_dialog(op,belle_sip_response_event_get_dialog(event)); - - /*check if op is terminating*/ - - - if (op->state == SalOpStateTerminating) { - if( strcmp("INVITE",belle_sip_request_get_method(req))==0 - && (!op->dialog - || belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_NULL - || belle_sip_dialog_get_state(op->dialog)==BELLE_SIP_DIALOG_EARLY)) { - /*FIXME if DIALOG_CONFIRM then ACK+BYE*/ - cancelling_invite(op); - - return; - } else if (strcmp("BYE",belle_sip_request_get_method(req))==0) { - return; /*probably a 200ok for a bye*/ - } - } - if (!op->dialog) { - ms_message("call op [%p] receive out of dialog answer [%i]",op,code); - return; - } - dialog_state=belle_sip_dialog_get_state(op->dialog); + dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; switch(dialog_state) { - case BELLE_SIP_DIALOG_NULL: { - ms_error("call op [%p] receive an unexpected answer [%i]",op,code); - break; - } + case BELLE_SIP_DIALOG_NULL: case BELLE_SIP_DIALOG_EARLY: { - if (code >= 180) { - handle_sdp_from_response(op,response); - op->base.root->callbacks.call_ringing(op); + if (strcmp("INVITE",belle_sip_request_get_method(req))==0 ) { + if ( op->state == SalOpStateTerminating) { + 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 ?*/ + } + break; + } else if (code >= 180) { + handle_sdp_from_response(op,response); + op->base.root->callbacks.call_ringing(op); + break; + } else { + /*nop error*/ + } + + } else if (strcmp("CANCEL",belle_sip_request_get_method(req))==0 + || strcmp("BYE",belle_sip_request_get_method(req))==0) { + break;/*200ok for cancel or BYE*/ } else { - ms_error("call op [%p] receive an unexpected answer [%i]",op,code); + /*nop error*/ } - break; + ms_error("call op [%p] receive an unexpected answer [%i]",op,code); } case BELLE_SIP_DIALOG_CONFIRMED: { switch (op->state) { @@ -213,18 +244,28 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t op->base.root->callbacks.call_accepted(op); /*INVITE*/ op->state=SalOpStateActive; - } else { + } else { /*nop*/ } break; - + case SalOpStateTerminating: + //FIXME send bye case SalOpStateTerminated: default: ms_error("call op [%p] receive answer [%i] not implemented",op,code); } break; } - case BELLE_SIP_DIALOG_TERMINATED: + case BELLE_SIP_DIALOG_TERMINATED: { + /*if (code>=400 && strcmp("INVITE",belle_sip_request_get_method(req))==0){ + call_set_error(op,response); + call_set_released(op); + op->state=SalOpStateTerminated; + break; + }*/ + break; + } + /* no break */ default: { ms_error("call op [%p] receive answer [%i] not implemented",op,code); } @@ -234,15 +275,12 @@ static void call_response_event(void *op_base, const belle_sip_response_event_t } -static void call_set_released(SalOp* op){ - op->base.root->callbacks.call_released(op); -} - static void call_process_timeout(void *user_ctx, const belle_sip_timeout_event_t *event) { SalOp* op=(SalOp*)user_ctx; if (!op->dialog) { /*call terminated very early*/ op->base.root->callbacks.call_failure(op,SalErrorNoResponse,SalReasonUnknown,"Request Timeout",408); + call_set_released(op); op->state=SalOpStateTerminated; } else { /*dialog will terminated shortly, nothing to do*/ @@ -261,9 +299,11 @@ static void call_process_transaction_terminated(void *user_ctx, const belle_sip_ req=belle_sip_transaction_get_request(BELLE_SIP_TRANSACTION(server_transaction)); resp=belle_sip_transaction_get_response(BELLE_SIP_TRANSACTION(server_transaction)); } - if (strcmp("BYE",belle_sip_request_get_method(req))==0 + if (op->state ==SalOpStateTerminating && + strcmp("BYE",belle_sip_request_get_method(req))==0 && (!resp || (belle_sip_response_get_status_code(resp) !=401 && belle_sip_response_get_status_code(resp) !=407))) { + call_set_released(op); } @@ -302,11 +342,17 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t belle_sip_response_t* resp; belle_sip_header_t* call_info; - if (server_transaction) belle_sip_object_ref(server_transaction); /*ACK does'nt create srv transaction*/ + if (server_transaction){ + belle_sip_object_ref(server_transaction); /*ACK does'nt create srv transaction*/ + belle_sip_transaction_set_application_data(BELLE_SIP_TRANSACTION(server_transaction),op); + sal_op_ref(op); + } + if (strcmp("INVITE",belle_sip_request_get_method(req))==0) { if (op->pending_server_trans)belle_sip_object_unref(op->pending_server_trans); /*updating pending invite transaction*/ op->pending_server_trans=server_transaction; + belle_sip_object_ref(op->pending_server_trans); } if (!op->dialog) { @@ -386,7 +432,9 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t } else if(strcmp("BYE",belle_sip_request_get_method(req))==0) { resp=belle_sip_response_create_from_request(req,200); belle_sip_server_transaction_send_response(server_transaction,resp); - /*call end is notified by dialog deletion*/ + op->base.root->callbacks.call_terminated(op,op->dir==SalOpDirIncoming?sal_op_get_from(op):sal_op_get_to(op)); + op->state=SalOpStateTerminating; + /*call end not notified by dialog deletion because transaction can end before dialog*/ } else if(strcmp("INVITE",belle_sip_request_get_method(req))==0) { /*re-invite*/ if (op->base.remote_media){ @@ -429,6 +477,7 @@ static void process_request_event(void *op_base, const belle_sip_request_event_t /* no break */ } + if (server_transaction) belle_sip_object_unref(server_transaction); } @@ -640,7 +689,10 @@ int sal_call_terminate(SalOp *op){ cancelling_invite(op); break; } else { - ms_error("Don't know how to termination NUlL dialog for op [%p]",op); + /*just schedule call released*/ + belle_sip_main_loop_do_later(belle_sip_stack_get_main_loop(op->base.root->stack) + ,(belle_sip_callback_t) call_set_released + , op); } break; } diff --git a/coreapi/bellesip_sal/sal_op_impl.c b/coreapi/bellesip_sal/sal_op_impl.c index a9c99b5cd..2f9b8484d 100644 --- a/coreapi/bellesip_sal/sal_op_impl.c +++ b/coreapi/bellesip_sal/sal_op_impl.c @@ -268,12 +268,17 @@ bool_t sal_compute_sal_errors(belle_sip_response_t* response,SalError* sal_err,S } void set_or_update_dialog(SalOp* op, belle_sip_dialog_t* dialog) { /*check if dialog has changed*/ - if (dialog != op->dialog) { + if (dialog && dialog != op->dialog) { ms_message("Dialog set from [%p] to [%p] for op [%p]",op->dialog,dialog,op); /*fixme, shouldn't we cancel previous dialog*/ - if (op->dialog)belle_sip_object_unref(op->dialog); + if (op->dialog) { + belle_sip_dialog_set_application_data(op->dialog,NULL); + belle_sip_object_unref(op->dialog); + sal_op_unref(op); + } op->dialog=dialog; belle_sip_dialog_set_application_data(op->dialog,op); + sal_op_ref(op); belle_sip_object_ref(op->dialog); } } diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index 8020436fc..fe0597c20 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -404,8 +404,12 @@ static void add_presence_info(belle_sip_message_t *notify, SalPresenceStatus onl static void presence_process_io_error(void *user_ctx, const belle_sip_io_error_event_t *event){ ms_error("presence_process_io_error not implemented yet"); } -static void presence_process_dialog_terminated(void *op, const belle_sip_dialog_terminated_event_t *event) { - if (((SalOp*)op)->dialog) ((SalOp*)op)->dialog=NULL; +static void presence_process_dialog_terminated(void *ctx, const belle_sip_dialog_terminated_event_t *event) { + SalOp* op= (SalOp*)ctx; + if (op->dialog) { + sal_op_unref(op); + op->dialog=NULL; + } } static void presence_response_event(void *op_base, const belle_sip_response_event_t *event){ diff --git a/coreapi/bellesip_sal/sal_sdp.c b/coreapi/bellesip_sal/sal_sdp.c index 3fa6e2a2d..a0586e824 100644 --- a/coreapi/bellesip_sal/sal_sdp.c +++ b/coreapi/bellesip_sal/sal_sdp.c @@ -159,6 +159,7 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal const char *mtype,*proto; SalStreamDescription *stream; belle_sdp_media_t* media; + belle_sip_list_t* mime_params=NULL; belle_sip_list_t* mime_param_it=NULL; belle_sdp_mime_parameter_t* mime_param; PayloadType *pt; @@ -232,7 +233,8 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal } /* for each payload type */ - for(mime_param_it=belle_sdp_media_description_build_mime_parameters(media_desc) + mime_params=belle_sdp_media_description_build_mime_parameters(media_desc); + for(mime_param_it=mime_params ;mime_param_it!=NULL ;mime_param_it=mime_param_it->next) { mime_param=BELLE_SDP_MIME_PARAMETER(mime_param_it->data) @@ -248,7 +250,7 @@ int sdp_to_media_description(belle_sdp_session_description_t *session_desc, Sal ms_message("Found payload %s/%i fmtp=%s",pt->mime_type,pt->clock_rate, pt->send_fmtp ? pt->send_fmtp : ""); } - if (mime_param_it) belle_sip_list_free_with_data(mime_param_it,belle_sip_object_unref); + if (mime_params) belle_sip_list_free_with_data(mime_params,belle_sip_object_unref); /* read crypto lines if any */ if (stream->proto == SalProtoRtpSavp) { diff --git a/tester/call_tester.c b/tester/call_tester.c index 9c98fa00d..ccac96a7d 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -187,6 +187,13 @@ static void simple_call(void) { CU_ASSERT_TRUE(wait_for(lc_pauline,lc_marie,&stat_pauline->number_of_LinphoneCallEnd,1)); CU_ASSERT_TRUE(wait_for(lc_pauline,lc_marie,&stat_marie->number_of_LinphoneCallEnd,1)); } + linphone_core_destroy(marie->lc); + marie->lc=NULL; + CU_ASSERT_EQUAL(stat_marie->number_of_LinphoneCallReleased,1); + linphone_core_destroy(pauline->lc); + pauline->lc=NULL; + CU_ASSERT_EQUAL(stat_pauline->number_of_LinphoneCallReleased,1); + linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); } @@ -268,6 +275,8 @@ static void cancelled_call(void) { //CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonCanceled); CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1); CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallIncomingReceived,0); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1)); + linphone_call_unref(out_call); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); @@ -285,6 +294,7 @@ static void call_with_dns_time_out(void) { CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingInit,1); CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallOutgoingProgress,1); CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallError,1); + CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallReleased,1); linphone_core_manager_destroy(marie); } @@ -297,10 +307,11 @@ static void cancelled_ringing_call(void) { CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallIncomingReceived,1)); linphone_core_terminate_call(pauline->lc,out_call); - 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_LinphoneCallEnd,1)); - //CU_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined); - //CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonDeclined); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallReleased,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1)); + CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEnd,1); + CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1); + linphone_call_unref(out_call); linphone_core_manager_destroy(marie); linphone_core_manager_destroy(pauline); @@ -318,8 +329,10 @@ static void early_declined_call(void) { if (in_call) { linphone_call_ref(in_call); linphone_core_terminate_call(marie->lc,in_call); - 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_LinphoneCallEnd,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&marie->stat.number_of_LinphoneCallReleased,1)); + CU_ASSERT_TRUE(wait_for(pauline->lc,marie->lc,&pauline->stat.number_of_LinphoneCallReleased,1)); + CU_ASSERT_EQUAL(marie->stat.number_of_LinphoneCallEnd,1); + CU_ASSERT_EQUAL(pauline->stat.number_of_LinphoneCallEnd,1); CU_ASSERT_EQUAL(linphone_call_get_reason(in_call),LinphoneReasonDeclined); CU_ASSERT_EQUAL(linphone_call_get_reason(out_call),LinphoneReasonDeclined); linphone_call_unref(in_call); diff --git a/tester/liblinphone_tester.c b/tester/liblinphone_tester.c index 4513fbf81..6f7840a03 100644 --- a/tester/liblinphone_tester.c +++ b/tester/liblinphone_tester.c @@ -165,6 +165,7 @@ static void enable_codec(LinphoneCore* lc,const char* type,int rate) { if((pt = linphone_core_find_payload_type(lc,type,rate,1))) { linphone_core_enable_payload_type(lc,pt, 1); } + ms_list_free(codecs); } LinphoneCoreManager* linphone_core_manager_new2(const char* path, const char* rc_file, int check_for_proxies) { @@ -194,7 +195,7 @@ LinphoneCoreManager* linphone_core_manager_new(const char* path, const char* rc_ } void linphone_core_manager_destroy(LinphoneCoreManager* mgr) { - linphone_core_destroy(mgr->lc); + if (mgr->lc) linphone_core_destroy(mgr->lc); if (mgr->identity) linphone_address_destroy(mgr->identity); free(mgr); }