From 9a671657c6edb30c188450ff257cb3dd88d839ee Mon Sep 17 00:00:00 2001 From: Simon Morlat Date: Tue, 3 Sep 2013 22:31:30 +0200 Subject: [PATCH] make use of bellesip's new dialog's request queue to fix bugs around transfer notification add notification of failed transfers (new test in call suite for that) In case of failed transfer, referer call is resumed (if necessary) and notified of the failure. remove deprecated code. --- configure.ac | 2 +- coreapi/bellesip_sal/sal_impl.c | 18 +------- coreapi/bellesip_sal/sal_op_call.c | 4 +- coreapi/bellesip_sal/sal_op_call_transfer.c | 46 +++++++++++++-------- coreapi/bellesip_sal/sal_op_events.c | 8 +++- coreapi/bellesip_sal/sal_op_info.c | 2 +- coreapi/bellesip_sal/sal_op_presence.c | 2 +- coreapi/callbacks.c | 24 ++++++++--- coreapi/linphonecall.c | 19 ++++----- coreapi/linphonecore.c | 9 +--- coreapi/private.h | 3 +- include/sal/sal.h | 4 -- tester/call_tester.c | 37 ++++++++++++++++- tester/liblinphone_tester.h | 1 + 14 files changed, 105 insertions(+), 74 deletions(-) diff --git a/configure.ac b/configure.ac index c4042828a..90191ab53 100644 --- a/configure.ac +++ b/configure.ac @@ -681,7 +681,7 @@ if test x$enable_msg_storage != xfalse; then fi -PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.1.0]) +PKG_CHECK_MODULES(BELLESIP, [belle-sip >= 1.2.0]) SIPSTACK_CFLAGS="$BELLESIP_CFLAGS" SIPSTACK_LIBS="$BELLESIP_LIBS" diff --git a/coreapi/bellesip_sal/sal_impl.c b/coreapi/bellesip_sal/sal_impl.c index 7930a0f04..f6f99cd93 100644 --- a/coreapi/bellesip_sal/sal_impl.c +++ b/coreapi/bellesip_sal/sal_impl.c @@ -565,26 +565,16 @@ void sal_use_session_timers(Sal *ctx, int expires){ ctx->session_expires=expires; return ; } -void sal_use_double_registrations(Sal *ctx, bool_t enabled){ - ms_warning("sal_use_double_registrations is deprecated"); - return ; -} -void sal_reuse_authorization(Sal *ctx, bool_t enabled){ - ms_warning("sal_reuse_authorization is deprecated"); - return ; -} + void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec){ ctx->one_matching_codec=one_matching_codec; } + void sal_use_rport(Sal *ctx, bool_t use_rports){ belle_sip_provider_enable_rport(ctx->prov,use_rports); ms_message("Sal use rport [%s]",use_rports?"enabled":"disabled"); return ; } -void sal_use_101(Sal *ctx, bool_t use_101){ - ms_warning("sal_use_101 is deprecated"); - return ; -} static void set_tls_properties(Sal *ctx){ belle_sip_listening_point_t *lp=belle_sip_provider_get_listening_point(ctx->prov,"TLS"); @@ -705,10 +695,6 @@ const char* sal_op_type_to_string(const SalOpType type) { } } -void sal_expire_old_registration_contacts(Sal *ctx, bool_t enabled){ - ms_warning("sal_expire_old_registration_contacts not implemented "); -} - void sal_use_dates(Sal *ctx, bool_t enabled){ ctx->use_dates=enabled; } diff --git a/coreapi/bellesip_sal/sal_op_call.c b/coreapi/bellesip_sal/sal_op_call.c index 1a7b191a8..ce3595cdc 100644 --- a/coreapi/bellesip_sal/sal_op_call.c +++ b/coreapi/bellesip_sal/sal_op_call.c @@ -710,7 +710,7 @@ SalMediaDescription * sal_call_get_final_media_description(SalOp *h){ int sal_call_send_dtmf(SalOp *h, char dtmf){ if (h->dialog){ - belle_sip_request_t *req=belle_sip_dialog_create_request(h->dialog,"INFO"); + belle_sip_request_t *req=belle_sip_dialog_create_queued_request(h->dialog,"INFO"); if (req){ int bodylen; char dtmf_body[128]={0}; @@ -789,7 +789,7 @@ void sal_call_send_vfu_request(SalOp *op){ size_t content_lenth = sizeof(info_body) - 1; belle_sip_dialog_state_t dialog_state=op->dialog?belle_sip_dialog_get_state(op->dialog):BELLE_SIP_DIALOG_NULL; /*no dialog = dialog in NULL state*/ if (dialog_state == BELLE_SIP_DIALOG_CONFIRMED) { - belle_sip_request_t* info = belle_sip_dialog_create_request(op->dialog,"INFO"); + belle_sip_request_t* info = belle_sip_dialog_create_queued_request(op->dialog,"INFO"); int error=TRUE; if (info) { belle_sip_message_add_header(BELLE_SIP_MESSAGE(info),BELLE_SIP_HEADER(belle_sip_header_content_type_create("application","media_control+xml"))); diff --git a/coreapi/bellesip_sal/sal_op_call_transfer.c b/coreapi/bellesip_sal/sal_op_call_transfer.c index c996ab723..964a02cf2 100644 --- a/coreapi/bellesip_sal/sal_op_call_transfer.c +++ b/coreapi/bellesip_sal/sal_op_call_transfer.c @@ -133,39 +133,49 @@ SalOp *sal_call_get_replaces(SalOp *op){ return NULL; } -static int send_notify_for_refer(SalOp* op, const char *sipfrag){ - belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); +static int send_notify_for_refer(SalOp* op, int code, const char *reason){ + belle_sip_request_t* notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"); + char *sipfrag=belle_sip_strdup_printf("SIP/2.0 %i %s\r\n",code,reason); size_t content_length=strlen(sipfrag); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) - ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,-1))); + ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_ACTIVE,-1))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),belle_sip_header_create("Event","refer")); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(belle_sip_header_content_type_create("message","sipfrag"))); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify),BELLE_SIP_HEADER(belle_sip_header_content_length_create(content_length))); - belle_sip_message_set_body(BELLE_SIP_MESSAGE(notify),sipfrag,content_length); - + belle_sip_message_assign_body(BELLE_SIP_MESSAGE(notify),sipfrag,content_length); return sal_op_send_request(op,notify); } +static void notify_last_response(SalOp *op, SalOp *newcall){ + belle_sip_client_transaction_t *tr=newcall->pending_client_trans; + belle_sip_response_t *resp=NULL; + if (tr){ + resp=belle_sip_transaction_get_response((belle_sip_transaction_t*)tr); + } + if (resp==NULL){ + send_notify_for_refer(op, 100, "Trying"); + }else{ + send_notify_for_refer(op, belle_sip_response_get_status_code(resp), belle_sip_response_get_reason_phrase(resp)); + } +} + int sal_call_notify_refer_state(SalOp *op, SalOp *newcall){ if(belle_sip_dialog_get_state(op->dialog) == BELLE_SIP_DIALOG_TERMINATED){ return 0; } belle_sip_dialog_state_t state=newcall->dialog?belle_sip_dialog_get_state(newcall->dialog):BELLE_SIP_DIALOG_NULL; switch(state) { - case BELLE_SIP_DIALOG_NULL: - case BELLE_SIP_DIALOG_EARLY: - send_notify_for_refer(op,"SIP/2.0 100 Trying\r\n"); - break; - case BELLE_SIP_DIALOG_CONFIRMED: - if(send_notify_for_refer(op,"SIP/2.0 200 Ok\r\n")) { - /* we need previous notify transaction to complete, so buffer the request for later*/ - /*op->sipfrag_pending="SIP/2.0 200 Ok\r\n";*/ - ms_error("Cannot notify 200 ok frag to [%p] for new op [%p]",op,newcall); - } - break; - default: - break; + case BELLE_SIP_DIALOG_EARLY: + send_notify_for_refer(op, 100, "Trying"); + break; + case BELLE_SIP_DIALOG_CONFIRMED: + send_notify_for_refer(op, 200, "Ok"); + break; + case BELLE_SIP_DIALOG_TERMINATED: + case BELLE_SIP_DIALOG_NULL: + notify_last_response(op,newcall); + break; } return 0; } diff --git a/coreapi/bellesip_sal/sal_op_events.c b/coreapi/bellesip_sal/sal_op_events.c index 2c490ce6b..cf67c992d 100644 --- a/coreapi/bellesip_sal/sal_op_events.c +++ b/coreapi/bellesip_sal/sal_op_events.c @@ -248,6 +248,10 @@ int sal_subscribe(SalOp *op, const char *from, const char *to, const char *event belle_sip_transaction_t *last=belle_sip_dialog_get_last_transaction(op->dialog); belle_sip_message_t *msg=BELLE_SIP_MESSAGE(belle_sip_transaction_get_request(last)); req=belle_sip_dialog_create_request(op->dialog,"SUBSCRIBE"); + if (!req) { + ms_error("Cannot create subscribe refresh."); + return -1; + } if (expires==-1){ belle_sip_header_expires_t *eh=belle_sip_message_get_header_by_type(msg,belle_sip_header_expires_t); expires=belle_sip_header_expires_get_expires(eh); @@ -293,7 +297,7 @@ int sal_notify(SalOp *op, const SalBody *body){ if (!op->dialog) return -1; - if (!(notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"))) return -1; + if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; if (set_event_name(op,(belle_sip_message_t*)notify)==-1){ belle_sip_object_unref(notify); @@ -310,7 +314,7 @@ int sal_notify(SalOp *op, const SalBody *body){ int sal_notify_close(SalOp *op){ belle_sip_request_t* notify; if (!op->dialog) return -1; - if (!(notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"))) return -1; + if (!(notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"))) return -1; set_event_name(op,(belle_sip_message_t*)notify); belle_sip_message_add_header(BELLE_SIP_MESSAGE(notify) ,BELLE_SIP_HEADER(belle_sip_header_subscription_state_create(BELLE_SIP_SUBSCRIPTION_STATE_TERMINATED,-1))); diff --git a/coreapi/bellesip_sal/sal_op_info.c b/coreapi/bellesip_sal/sal_op_info.c index 3b329515f..9abc73818 100644 --- a/coreapi/bellesip_sal/sal_op_info.c +++ b/coreapi/bellesip_sal/sal_op_info.c @@ -21,7 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. int sal_send_info(SalOp *op, const char *from, const char *to, const SalBody *body){ if (op->dialog){ - belle_sip_request_t *req=belle_sip_dialog_create_request(op->dialog,"INFO"); + belle_sip_request_t *req=belle_sip_dialog_create_queued_request(op->dialog,"INFO"); sal_op_add_body(op,(belle_sip_message_t*)req,body); return sal_op_send_request(op,req); } diff --git a/coreapi/bellesip_sal/sal_op_presence.c b/coreapi/bellesip_sal/sal_op_presence.c index ca6b86cab..5ea4cfe7b 100644 --- a/coreapi/bellesip_sal/sal_op_presence.c +++ b/coreapi/bellesip_sal/sal_op_presence.c @@ -306,7 +306,7 @@ int sal_subscribe_presence(SalOp *op, const char *from, const char *to, int expi static belle_sip_request_t *create_presence_notify(SalOp *op){ - belle_sip_request_t* notify=belle_sip_dialog_create_request(op->dialog,"NOTIFY"); + belle_sip_request_t* notify=belle_sip_dialog_create_queued_request(op->dialog,"NOTIFY"); if (!notify) return NULL; belle_sip_message_add_header((belle_sip_message_t*)notify,belle_sip_header_create("Event","presence")); diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 16fa1ed1f..cba47183e 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -467,7 +467,6 @@ static void call_resumed(LinphoneCore *lc, LinphoneCall *call){ if(lc->vtable.display_status) lc->vtable.display_status(lc,_("We have been resumed.")); linphone_call_set_state(call,LinphoneCallStreamsRunning,"Connected (streams running)"); - linphone_call_set_transfer_state(call, LinphoneCallIdle); } static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ @@ -569,6 +568,7 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de char *msg603=_("Call declined."); const char *msg=details; LinphoneCall *call=(LinphoneCall*)sal_op_get_user_pointer(op); + LinphoneCall *referer=call->referer; if (call==NULL){ ms_warning("Call faillure reported on already terminated call."); @@ -650,11 +650,7 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de } linphone_core_stop_ringing(lc); - linphone_call_stop_media_streams (call); - if (call->referer && linphone_call_get_state(call->referer)==LinphoneCallPaused && call->referer->was_automatically_paused){ - /*resume to the call that send us the refer automatically*/ - linphone_core_resume_call(lc,call->referer); - } + linphone_call_stop_media_streams(call); #ifdef BUILD_UPNP linphone_call_delete_upnp_session(call); @@ -673,6 +669,22 @@ static void call_failure(SalOp *op, SalError error, SalReason sr, const char *de } else { linphone_call_set_state(call,LinphoneCallError,msg); } + + if (referer){ + /* + * 1- resume call automatically if we had to pause it before to execute the transfer + * 2- notify other party of the transfer faillure + * This must be done at the end because transferer call can't be resumed until transfer-target call is changed to error state. + * This must be done in this order because if the notify transaction will prevent the resume transaction to take place. + * On the contrary, the notify transaction is queued and then executed after the resume completes. + **/ + if (linphone_call_get_state(referer)==LinphoneCallPaused && referer->was_automatically_paused){ + /*resume to the call that send us the refer automatically*/ + linphone_core_resume_call(lc,referer); + referer->was_automatically_paused=FALSE; + } + linphone_core_notify_refer_state(lc,referer,call); + } } static void call_released(SalOp *op){ diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index db5af1ba7..ae2ff5f01 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -642,10 +642,6 @@ static void linphone_call_set_terminated(LinphoneCall *call){ linphone_core_stop_dtmf(lc); call->ringing_beep=FALSE; } - if (call->referer){ - linphone_call_unref(call->referer); - call->referer=NULL; - } } void linphone_call_fix_call_parameters(LinphoneCall *call){ @@ -727,7 +723,7 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const default: break; } - linphone_call_set_terminated (call); + linphone_call_set_terminated(call); } if (cstate == LinphoneCallConnected) { call->log->status=LinphoneCallSuccess; @@ -744,9 +740,9 @@ void linphone_call_set_state(LinphoneCall *call, LinphoneCallState cstate, const call->op=NULL; } /*it is necessary to reset pointers to other call to prevent circular references that would result in memory never freed.*/ - if (call->transferer){ - linphone_call_unref(call->transferer); - call->transferer=NULL; + if (call->referer){ + linphone_call_unref(call->referer); + call->referer=NULL; } if (call->transfer_target){ linphone_call_unref(call->transfer_target); @@ -782,8 +778,9 @@ static void linphone_call_destroy(LinphoneCall *obj) if (obj->refer_to){ ms_free(obj->refer_to); } - if (obj->transferer){ - linphone_call_unref(obj->transferer); + if (obj->referer){ + linphone_call_unref(obj->referer); + obj->referer=NULL; } if (obj->transfer_target){ linphone_call_unref(obj->transfer_target); @@ -961,7 +958,7 @@ const char *linphone_call_get_refer_to(const LinphoneCall *call){ * The call in which the transfer request was received is returned in this case. **/ LinphoneCall *linphone_call_get_transferer_call(const LinphoneCall *call){ - return call->transferer; + return call->referer; } /** diff --git a/coreapi/linphonecore.c b/coreapi/linphonecore.c index 6aa9fb015..ee9585728 100644 --- a/coreapi/linphonecore.c +++ b/coreapi/linphonecore.c @@ -617,9 +617,6 @@ static void sip_config_read(LinphoneCore *lc) } sal_use_rport(lc->sal,lp_config_get_int(lc->config,"sip","use_rport",1)); - sal_use_101(lc->sal,lp_config_get_int(lc->config,"sip","use_101",1)); - sal_reuse_authorization(lc->sal, lp_config_get_int(lc->config,"sip","reuse_authorization",0)); - sal_expire_old_registration_contacts(lc->sal,lp_config_get_int(lc->config,"sip","expire_old_registration_contacts",0)); ipv6=lp_config_get_int(lc->config,"sip","use_ipv6",-1); if (ipv6==-1){ @@ -733,7 +730,6 @@ static void sip_config_read(LinphoneCore *lc) lc->sip_conf.tcp_tls_keepalive=lp_config_get_int(lc->config,"sip","tcp_tls_keepalive",0); linphone_core_enable_keep_alive(lc, (lc->sip_conf.keepalive_period > 0)); sal_use_one_matching_codec_policy(lc->sal,lp_config_get_int(lc->config,"sip","only_one_codec",0)); - sal_use_double_registrations(lc->sal,lp_config_get_int(lc->config,"sip","use_double_registrations",1)); sal_use_dates(lc->sal,lp_config_get_int(lc->config,"sip","put_date",0)); } @@ -2328,7 +2324,7 @@ void linphone_core_start_refered_call(LinphoneCore *lc, LinphoneCall *call){ if (call->refer_pending){ LinphoneCallParams *cp=linphone_core_create_default_call_parameters(lc); LinphoneCall *newcall; - cp->has_video &= !!lc->video_policy.automatically_initiate; + cp->has_video = call->current_params.has_video; /*start the call to refer-target with video enabled if original call had video*/ cp->referer=call; ms_message("Starting new call to refered address %s",call->refer_to); call->refer_pending=FALSE; @@ -2681,9 +2677,6 @@ LinphoneCall * linphone_core_invite_address_with_params(LinphoneCore *lc, const linphone_call_unref(call); return NULL; } - if (params && params->referer){ - call->transferer=linphone_call_ref(params->referer); - } /* this call becomes now the current one*/ lc->current_call=call; diff --git a/coreapi/private.h b/coreapi/private.h index b508f366a..7c8dd1667 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -157,7 +157,6 @@ struct _LinphoneCall SalMediaDescription *localdesc; SalMediaDescription *resultdesc; LinphoneCallDir dir; - LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/ struct _RtpProfile *audio_profile; struct _RtpProfile *video_profile; struct _LinphoneCallLog *log; @@ -198,7 +197,7 @@ struct _LinphoneCall int ping_time; unsigned int remote_session_id; unsigned int remote_session_ver; - LinphoneCall *transferer; /*if this call is the result of a transfer, transferer points to the call from which the transfer request was received.*/ + LinphoneCall *referer; /*when this call is the result of a transfer, referer is set to the original call that caused the transfer*/ LinphoneCall *transfer_target;/*if this call received a transfer request, then transfer_target points to the new call created to the refer target */ bool_t refer_pending; bool_t media_pending; diff --git a/include/sal/sal.h b/include/sal/sal.h index 239bd949c..4305e4df0 100644 --- a/include/sal/sal.h +++ b/include/sal/sal.h @@ -436,13 +436,9 @@ void sal_disable_tunnel(Sal *ctx); * */ unsigned int sal_get_keepalive_period(Sal *ctx); void sal_use_session_timers(Sal *ctx, int expires); -void sal_use_double_registrations(Sal *ctx, bool_t enabled); -void sal_expire_old_registration_contacts(Sal *ctx, bool_t enabled); void sal_use_dates(Sal *ctx, bool_t enabled); -void sal_reuse_authorization(Sal *ctx, bool_t enabled); void sal_use_one_matching_codec_policy(Sal *ctx, bool_t one_matching_codec); void sal_use_rport(Sal *ctx, bool_t use_rports); -void sal_use_101(Sal *ctx, bool_t use_101); void sal_enable_auto_contacts(Sal *ctx, bool_t enabled); void sal_set_root_ca(Sal* ctx, const char* rootCa); const char *sal_get_root_ca(Sal* ctx); diff --git a/tester/call_tester.c b/tester/call_tester.c index 8486d003f..74364fb66 100644 --- a/tester/call_tester.c +++ b/tester/call_tester.c @@ -75,6 +75,7 @@ void linphone_transfer_state_changed(LinphoneCore *lc, LinphoneCall *transfered, case LinphoneCallOutgoingEarlyMedia :counters->number_of_LinphoneTransferCallOutgoingEarlyMedia++;break; case LinphoneCallConnected :counters->number_of_LinphoneTransferCallConnected++;break; case LinphoneCallStreamsRunning :counters->number_of_LinphoneTransferCallStreamsRunning++;break; + case LinphoneCallError :counters->number_of_LinphoneTransferCallError++;break; default: CU_FAIL("unexpected event");break; } @@ -993,7 +994,7 @@ static void simple_call_transfer(void) { ms_list_free(lcs); } -static void mean_call_transfer(void) { +static void unattended_call_transfer(void) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); LinphoneCoreManager* laure = linphone_core_manager_new( "laure_rc"); @@ -1039,6 +1040,37 @@ static void mean_call_transfer(void) { ms_list_free(lcs); } +static void unattended_call_transfer_with_error(void) { + LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); + LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); + LinphoneCall* pauline_called_by_marie; + + MSList* lcs=ms_list_append(NULL,marie->lc); + lcs=ms_list_append(lcs,pauline->lc); + + CU_ASSERT_TRUE(call(marie,pauline)); + pauline_called_by_marie=linphone_core_get_current_call(marie->lc); + + reset_counters(&marie->stat); + reset_counters(&pauline->stat); + + linphone_core_transfer_call(marie->lc,pauline_called_by_marie,"unknown_user"); + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallRefered,1,2000)); + + /*Pauline starts the transfer*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallOutgoingInit,1,2000)); + /* and immediately get an error*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&pauline->stat.number_of_LinphoneCallError,1,2000)); + + /*the error must be reported back to marie*/ + CU_ASSERT_TRUE(wait_for_list(lcs,&marie->stat.number_of_LinphoneTransferCallError,1,2000)); + + linphone_core_manager_destroy(marie); + linphone_core_manager_destroy(pauline); + ms_list_free(lcs); +} + + static void call_transfer_existing_call_outgoing_call(void) { LinphoneCoreManager* marie = linphone_core_manager_new( "marie_rc"); LinphoneCoreManager* pauline = linphone_core_manager_new( "pauline_rc"); @@ -1143,7 +1175,8 @@ test_t call_tests[] = { { "Call with privacy", call_with_privacy }, { "Simple conference", simple_conference }, { "Simple call transfer", simple_call_transfer }, - { "Mean call transfer", mean_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 with ICE", call_with_ice }, { "Call with custom headers",call_with_custom_headers} diff --git a/tester/liblinphone_tester.h b/tester/liblinphone_tester.h index 36dc323af..70aeaf83e 100644 --- a/tester/liblinphone_tester.h +++ b/tester/liblinphone_tester.h @@ -109,6 +109,7 @@ typedef struct _stats { int number_of_LinphoneTransferCallOutgoingEarlyMedia; int number_of_LinphoneTransferCallConnected; int number_of_LinphoneTransferCallStreamsRunning; + int number_of_LinphoneTransferCallError; int number_of_LinphoneMessageReceived; int number_of_LinphoneMessageReceivedLegacy;