diff --git a/coreapi/callbacks.c b/coreapi/callbacks.c index 83aad97d3..a1add1c39 100644 --- a/coreapi/callbacks.c +++ b/coreapi/callbacks.c @@ -688,6 +688,13 @@ static void call_paused_by_remote(LinphoneCore *lc, LinphoneCall *call){ static void call_updated_by_remote(LinphoneCore *lc, LinphoneCall *call){ linphone_core_notify_display_status(lc,_("Call is updated by remote.")); linphone_call_set_state(call, LinphoneCallUpdatedByRemote,"Call updated by remote"); + + if (call->ice_session && check_ice_reinvite_needs_defered_response(call)){ + call->defer_update = TRUE; + ms_message("LinphoneCall [%p]: Ice reinvite received, but one or more check list are not completed. Response will be sent later, once ICE has completed.", call); + call->incoming_ice_reinvite_pending = TRUE; + } + if (call->defer_update == FALSE){ if (call->state == LinphoneCallUpdatedByRemote){ linphone_call_accept_update(call, NULL); diff --git a/coreapi/linphonecall.c b/coreapi/linphonecall.c index 87319d4fe..00265f377 100644 --- a/coreapi/linphonecall.c +++ b/coreapi/linphonecall.c @@ -4662,7 +4662,7 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ switch (ice_session_state(call->ice_session)) { case IS_Completed: case IS_Failed: - /* At least one ICE session has succeeded, so perform a call update. */ + /* At least one ICE check list has succeeded, so perform a call update. */ if (ice_session_has_completed_check_list(call->ice_session) == TRUE) { const LinphoneCallParams *current_param = linphone_call_get_current_params(call); if (ice_session_role(call->ice_session) == IR_Controlling && current_param->update_call_when_ice_completed ) { @@ -4670,6 +4670,9 @@ static void handle_ice_events(LinphoneCall *call, OrtpEvent *ev){ params->internal_call_update = TRUE; linphone_call_update(call, params); linphone_call_params_unref(params); + }else if (ice_session_role(call->ice_session) == IR_Controlled && call->incoming_ice_reinvite_pending){ + linphone_call_accept_update(call, NULL); + call->incoming_ice_reinvite_pending = FALSE; } start_dtls_on_all_streams(call); } diff --git a/coreapi/misc.c b/coreapi/misc.c index fe10698aa..167e4044b 100644 --- a/coreapi/misc.c +++ b/coreapi/misc.c @@ -813,6 +813,7 @@ void linphone_call_stop_ice_for_inactive_streams(LinphoneCall *call, SalMediaDes linphone_call_update_ice_state_in_call_stats(call); } + void _update_local_media_description_from_ice(SalMediaDescription *desc, IceSession *session, bool_t use_nortpproxy) { IceCandidate *rtp_candidate = NULL; IceCandidate *rtcp_candidate = NULL; @@ -1684,6 +1685,38 @@ static bool_t _check_for_ice_restart_and_set_remote_credentials(IceSession *ice_ return ice_restarted; } +/*the purpose of this function is to detect a situation where a check list is still running while a reINVITE +with remote-candidates is received*/ +bool_t check_ice_reinvite_needs_defered_response(LinphoneCall *call){ + SalMediaDescription *md = sal_call_get_remote_media_description(call->op); + int i,j; + IceCheckList *cl; + + if (ice_session_state(call->ice_session) != IS_Running ) return FALSE; + + for (i = 0; i < md->nb_streams; i++) { + SalStreamDescription *stream = &md->streams[i]; + cl = ice_session_check_list(call->ice_session, i); + + if (cl==NULL) continue; + if (stream->ice_mismatch == TRUE) { + return FALSE; + } + if (stream->rtp_port == 0) { + continue; + } + + if (ice_check_list_state(cl) != ICL_Running) continue; + + for (j = 0; j < SAL_MEDIA_DESCRIPTION_MAX_ICE_CANDIDATES; j++) { + const SalIceRemoteCandidate *remote_candidate = &stream->ice_remote_candidates[j]; + if (remote_candidate->addr[0] != '\0') return TRUE; + + } + } + return FALSE; +} + static void _create_ice_check_lists_and_parse_ice_attributes(LinphoneCall *call, const SalMediaDescription *md, bool_t ice_restarted) { const SalStreamDescription *stream; IceCheckList *cl = NULL; diff --git a/coreapi/private.h b/coreapi/private.h index b0940a30d..306bd9810 100644 --- a/coreapi/private.h +++ b/coreapi/private.h @@ -405,6 +405,7 @@ struct _LinphoneCall{ bool_t reinvite_on_cancel_response_requested; bool_t non_op_error; /*set when the LinphoneErrorInfo was set at higher level than sal*/ + bool_t incoming_ice_reinvite_pending; bctbx_list_t *callbacks; /* A list of LinphoneCallCbs object */ LinphoneCallCbs *current_cbs; /* The current LinphoneCallCbs object used to call a callback */ @@ -1996,6 +1997,7 @@ char *linphone_presence_model_to_xml(LinphonePresenceModel *model) ; #define LINPHONE_SQLITE3_VFS "sqlite3bctbx_vfs" void linphone_call_check_ice_session(LinphoneCall *call, IceRole role, bool_t is_reinvite); +bool_t check_ice_reinvite_needs_defered_response(LinphoneCall *call); bool_t linphone_call_state_is_early(LinphoneCallState state);