8669 lines
292 KiB
C
Executable File
8669 lines
292 KiB
C
Executable File
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include <limits.h>
|
|
#include "CCProvider.h"
|
|
#include "cpr_types.h"
|
|
#include "cpr_stdlib.h"
|
|
#include "cpr_stdio.h"
|
|
#include "cpr_string.h"
|
|
#include "cpr_rand.h"
|
|
#include "cpr_timers.h"
|
|
#include "cpr_errno.h"
|
|
#include "phone.h"
|
|
#include "lsm.h"
|
|
#include "fsm.h"
|
|
#include "sm.h"
|
|
#include "ccapi.h"
|
|
#include "ccsip_cc.h"
|
|
#include "phone_debug.h"
|
|
#include "fim.h"
|
|
#include "config.h"
|
|
#include "sdp.h"
|
|
#include "ccsip_sdp.h" // Temporary include
|
|
#include "rtp_defs.h"
|
|
#include "debug.h"
|
|
#include "gsm_sdp.h"
|
|
#include "vcm.h"
|
|
#include "uiapi.h"
|
|
#include "gsm.h"
|
|
#include "phntask.h"
|
|
#include "prot_configmgr.h"
|
|
#include "sip_interface_regmgr.h"
|
|
#include "dialplanint.h"
|
|
#include "subapi.h"
|
|
#include "text_strings.h"
|
|
#include "platform_api.h"
|
|
#include "peer_connection_types.h"
|
|
//#include "prlog.h"
|
|
#include "sessionHash.h"
|
|
|
|
extern void update_kpmlconfig(int kpmlVal);
|
|
extern boolean g_disable_mass_reg_debug_print;
|
|
void escalateDeescalate();
|
|
|
|
#define FSMDEF_NO_NUMBER (NULL)
|
|
#define DIGIT_POUND ('#')
|
|
#define FSMDEF_MAX_DCBS (LSM_MAX_CALLS)
|
|
#define FSMDEF_CC_CALLER_ID ((cc_state_data_t *)(&(dcb->caller_id)))
|
|
#define RINGBACK_DELAY 90
|
|
|
|
// Minimum and maximum hold reversion timer in seconds
|
|
#define MIN_HOLD_REVERSION_INTERVAL_TIMER 10
|
|
#define MAX_HOLD_REVERSION_INTERVAL_TIMER 1200
|
|
|
|
fsmdef_dcb_t *fsmdef_dcbs;
|
|
|
|
static const char *fsmdef_state_names[] = {
|
|
"IDLE",
|
|
"COLLECTING_INFO",
|
|
"CALL_SENT",
|
|
"OUTGOING_PROCEEDING",
|
|
"KPML_COLLECTING_INFO",
|
|
"OUTGOING_ALERTING",
|
|
"INCOMING_ALERTING",
|
|
"CONNECTING",
|
|
"JOINING",
|
|
"CONNECTED",
|
|
"CONNECTED MEDIA PEND",
|
|
"RELEASING",
|
|
"HOLD_PENDING",
|
|
"HOLDING",
|
|
"RESUME_PENDING",
|
|
"PRESERVED"
|
|
};
|
|
|
|
|
|
static sm_rcs_t fsmdef_ev_createoffer(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_createanswer(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_setlocaldesc(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_setremotedesc(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_setpeerconnection(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_localdesc(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_remotedesc(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_addstream(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_removestream(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_addcandidate(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_default(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_default_feature_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_idle_setup(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_idle_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_idle_offhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_idle_dialstring(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_onhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_collectinginfo_release(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_collectinginfo_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_offhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_digit_begin(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_dialstring(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_proceeding(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_callsent_release(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_callsent_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_out_alerting(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_inalerting_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_inalerting_offhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_handle_inalerting_offhook_answer(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connected(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connected_line(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connected_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connecting_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connected_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connected_media_pend_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_connected_media_pend_feature_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_release(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_release_complete(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_releasing_release(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_releasing_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_releasing_onhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_hold_pending_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_hold_pending_feature_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_holding_release(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_holding_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_holding_feature_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_holding_onhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_holding_offhook(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_session_audit(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_resume_pending_feature(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_resume_pending_feature_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_preserved_feature(sm_event_t *event);
|
|
static void fsmdef_ev_join(cc_feature_data_t *data);
|
|
|
|
static sm_rcs_t fsmdef_cfwd_clear_ccm(fsm_fcb_t *fcb);
|
|
static sm_rcs_t fsmdef_process_dialstring_for_callfwd(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_process_cfwd_softkey_event(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_cfwd_clear_ccm(fsm_fcb_t *fcb);
|
|
static sm_rcs_t fsmdef_ev_joining_connected_ack(sm_event_t *event);
|
|
static sm_rcs_t fsmdef_ev_joining_offhook(sm_event_t *event);
|
|
|
|
static void fsmdef_b2bjoin_invoke(fsmdef_dcb_t *dcb,
|
|
cc_feature_data_t *join_data);
|
|
static void fsmdef_select_invoke(fsmdef_dcb_t *dcb,
|
|
cc_feature_data_t *select_data);
|
|
static void fsmdef_handle_join_pending(fsmdef_dcb_t *dcb);
|
|
static void fsmdef_append_dialstring_to_feature_uri(fsmdef_dcb_t *dcb,
|
|
const char *dialstring);
|
|
static boolean fsmdef_is_feature_uri_configured(cc_features_t ftr_id);
|
|
static void fsmdef_set_call_info_cc_call_state(fsmdef_dcb_t *dcb,
|
|
cc_states_t state,
|
|
cc_causes_t cause);
|
|
static boolean fsmdef_extract_join_target(sm_event_t *event);
|
|
static void fsmdef_ev_notify_feature(cc_feature_t *msg, fsmdef_dcb_t *dcb);
|
|
static void fsmdef_notify_hook_event(fsm_fcb_t *fcb, cc_msgs_t msg,
|
|
char *global_call_id,
|
|
callid_t prim_call_id,
|
|
cc_hold_resume_reason_e consult_reason,
|
|
monitor_mode_t monitor_mode,
|
|
cfwdall_mode_t cfwdall_mode);
|
|
static void fsmdef_update_callinfo_security_status(fsmdef_dcb_t *dcb,
|
|
cc_feature_data_call_info_t *call_info);
|
|
static void fsmdef_update_calltype (fsm_fcb_t *fcb, cc_feature_t *msg);
|
|
|
|
|
|
/*
|
|
* TODO <emannion> Update events for correct JSEP transitions
|
|
* Instead of providing events for all states
|
|
*/
|
|
static sm_function_t fsmdef_function_table[FSMDEF_S_MAX][CC_MSG_MAX] =
|
|
{
|
|
/* FSMDEF_S_IDLE ------------------------------------------------------------ */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_idle_setup, // New incoming
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_release_complete,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_idle_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_idle_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_LINE */ fsmdef_ev_idle_offhook,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_idle_dialstring, // new outgoing
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_COLLECT_INFO ---------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_collectinginfo_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_collectinginfo_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_digit_begin,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_dialstring,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_CALL_SENT ------------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_proceeding,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_out_alerting,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_connected,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_callsent_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_callsent_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_OUTGOING_PROCEEDING --------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_out_alerting,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_connected,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_callsent_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_callsent_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_KPML_COLLECT_INFO ----------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_out_alerting,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_connected,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_callsent_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_collectinginfo_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_digit_begin,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_OUTGOING_ALERTING ----------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_out_alerting,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_connected,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_callsent_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_callsent_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_INCOMING_ALERTING ----------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_inalerting_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_inalerting_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_inalerting_offhook,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_CONNECTING ------------------------------------------------------ */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_connected_ack,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_connecting_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_connected_line,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_JOINING --------------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_joining_connected_ack,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_connecting_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_joining_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_connected_line,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_CONNECTED ------------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_connected_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_connected_line,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_CONNECTED_MEDIA_PEND ------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_connected_media_pend_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_connected_media_pend_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_connected_line,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_RELEASING ------------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_releasing_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_release_complete,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_releasing_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_releasing_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_connected_line,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_HOLD_PENDING ---------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_holding_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_hold_pending_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_hold_pending_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_HOLDING --------------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_holding_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_holding_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_holding_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_holding_offhook,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_holding_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_RESUME_PENDING -------------------------------------------------- */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_resume_pending_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_resume_pending_feature_ack,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
},
|
|
|
|
/* FSMDEF_S_PRESERVED ------------------------------------------------------ */
|
|
{
|
|
/* CC_MSG_SETUP */ fsmdef_ev_default,
|
|
/* CC_MSG_SETUP_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_PROCEEDING */ fsmdef_ev_default,
|
|
/* CC_MSG_ALERTING */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED */ fsmdef_ev_default,
|
|
/* CC_MSG_CONNECTED_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_RELEASE */ fsmdef_ev_release,
|
|
/* CC_MSG_RELEASE_COMPLETE */ fsmdef_ev_default,
|
|
/* CC_MSG_FEATURE */ fsmdef_ev_preserved_feature,
|
|
/* CC_MSG_FEATURE_ACK */ fsmdef_ev_default,
|
|
/* CC_MSG_OFFHOOK */ fsmdef_ev_default,
|
|
/* CC_MSG_ONHOOK */ fsmdef_ev_onhook,
|
|
/* CC_MSG_LINE */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_BEGIN */ fsmdef_ev_default,
|
|
/* CC_MSG_DIGIT_END */ fsmdef_ev_default,
|
|
/* CC_MSG_DIALSTRING */ fsmdef_ev_default,
|
|
/* CC_MSG_MWI */ fsmdef_ev_default,
|
|
/* CC_MSG_SESSION_AUDIT */ fsmdef_ev_session_audit,
|
|
/* CC_MSG_CREATEOFFER */ fsmdef_ev_createoffer,
|
|
/* CC_MSG_CREATEANSWER */ fsmdef_ev_createanswer,
|
|
/* CC_MSG_SETLOCALDESC */ fsmdef_ev_setlocaldesc,
|
|
/* CC_MSG_SETREMOTEDESC */ fsmdef_ev_setremotedesc,
|
|
/* CC_MSG_LOCALDESC */ fsmdef_ev_localdesc,
|
|
/* CC_MSG_REMOTEDESC */ fsmdef_ev_remotedesc,
|
|
/* CC_MSG_SETPEERCONNECTION */fsmdef_ev_setpeerconnection,
|
|
/* CC_MSG_ADDSTREAM */ fsmdef_ev_addstream,
|
|
/* CC_MSG_REMOVESTREAM */ fsmdef_ev_removestream,
|
|
/* CC_MSG_ADDCANDIDATE */ fsmdef_ev_addcandidate
|
|
}
|
|
};
|
|
|
|
static sm_table_t fsmdef_sm_table;
|
|
sm_table_t *pfsmdef_sm_table = &fsmdef_sm_table;
|
|
|
|
/*--------------------------------------------------------------------------
|
|
* Global data
|
|
*--------------------------------------------------------------------------
|
|
*/
|
|
uint16_t g_numofselected_calls = 0;
|
|
boolean g_b2bjoin_pending = FALSE;
|
|
callid_t g_b2bjoin_callid = CC_NO_CALL_ID;
|
|
|
|
static sdp_direction_e s_default_video_dir = SDP_DIRECTION_SENDRECV;
|
|
static sdp_direction_e s_session_video_dir = SDP_MAX_QOS_DIRECTIONS;
|
|
|
|
|
|
void set_default_video_pref(int pref) {
|
|
s_default_video_dir = pref;
|
|
}
|
|
|
|
void set_next_sess_video_pref(int pref) {
|
|
s_session_video_dir = pref;
|
|
}
|
|
|
|
const char *
|
|
fsmdef_state_name (int state)
|
|
{
|
|
if ((state <= FSMDEF_S_MIN) || (state >= FSMDEF_S_MAX)) {
|
|
return (get_debug_string(GSM_UNDEFINED));
|
|
}
|
|
|
|
return (fsmdef_state_names[state]);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_get_dcb_by_call_id
|
|
*
|
|
* return the dcb referenced by the given call_id
|
|
*/
|
|
fsmdef_dcb_t *
|
|
fsmdef_get_dcb_by_call_id (callid_t call_id)
|
|
{
|
|
static const char fname[] = "fsmdef_get_dcb_by_call_id";
|
|
fsmdef_dcb_t *dcb;
|
|
fsmdef_dcb_t *dcb_found = NULL;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->call_id == call_id) {
|
|
dcb_found = dcb;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (dcb_found) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_PTR),
|
|
dcb->call_id, dcb->line, fname, dcb_found);
|
|
}
|
|
|
|
return (dcb_found);
|
|
}
|
|
|
|
|
|
/*
|
|
* fsmdef_check_if_chaperone_call_exist
|
|
*
|
|
* return the dcb referenced by the given call_id
|
|
*/
|
|
boolean
|
|
fsmdef_check_if_chaperone_call_exist (void)
|
|
{
|
|
static const char fname[] = "fsmdef_check_if_chaperone_call_exist";
|
|
fsmdef_dcb_t *dcb;
|
|
boolean result = FALSE;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if(dcb->policy == CC_POLICY_CHAPERONE){
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (result) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_PTR),
|
|
dcb->call_id, dcb->line, fname, dcb);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
void fsmdef_get_rtp_stat (fsmdef_dcb_t *dcb , cc_kfact_t *kfactor)
|
|
{
|
|
static const char fname[] ="fsmdef_get_rtp_stat";
|
|
|
|
int call_stats_flag;
|
|
fsmdef_media_t *media;
|
|
media = gsmsdp_find_audio_media(dcb);
|
|
|
|
if (!media) {
|
|
GSM_ERR_MSG(GSM_F_PREFIX"dcb media pointer invalid\n", fname);
|
|
return;
|
|
}
|
|
|
|
memset(kfactor, 0, sizeof(cc_kfact_t));
|
|
config_get_value(CFGID_CALL_STATS, &call_stats_flag, sizeof(call_stats_flag));
|
|
|
|
if (call_stats_flag) {
|
|
vcmGetRtpStats(media->cap_index, dcb->group_id,
|
|
media->refid,
|
|
lsm_get_ms_ui_call_handle(dcb->line, dcb->call_id, CC_NO_CALL_ID), &(kfactor->rxstats[0]), &(kfactor->txstats[0]));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The function sets or clears the local hold status to the given media or
|
|
* for all of the media.
|
|
*
|
|
* @param[in]dcb - pointer to fsmdef_dcb_t.
|
|
* @param[in]media - specify which media to use. The value of
|
|
* NULL indicates for all media.
|
|
* @param[in]set - set local hold or clear local hold.
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre (dcb not_eq NULL)
|
|
*/
|
|
static void
|
|
fsmdef_update_media_hold_status (fsmdef_dcb_t *dcb, fsmdef_media_t *media,
|
|
boolean set)
|
|
{
|
|
fsmdef_media_t *start_media, *end_media;
|
|
|
|
if (media == NULL) {
|
|
/* NULL value of the given media indicates for all media */
|
|
start_media = GSMSDP_FIRST_MEDIA_ENTRY(dcb);
|
|
end_media = NULL; /* NULL means till the end of the list */
|
|
} else {
|
|
/* given media, uses the provided media */
|
|
start_media = media;
|
|
end_media = media;
|
|
}
|
|
|
|
GSMSDP_FOR_MEDIA_LIST(media, start_media, end_media, dcb) {
|
|
if (GSMSDP_MEDIA_ENABLED(media)) {
|
|
if (set) {
|
|
FSM_SET_FLAGS(media->hold, FSM_HOLD_LCL);
|
|
} else {
|
|
FSM_RESET_FLAGS(media->hold, FSM_HOLD_LCL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The function checks to see whether all media streams are
|
|
* in locally held or not.
|
|
*
|
|
* @param[in]dcb - pointer to fsmdef_dcb_t.
|
|
*
|
|
* @return TRUE - all media streams are in local hold.
|
|
* FALSE - not all media streams are in local hold.
|
|
*
|
|
* @pre (dcb not_eq NULL)
|
|
*/
|
|
static boolean
|
|
fsmdef_all_media_are_local_hold (fsmdef_dcb_t *dcb)
|
|
{
|
|
fsmdef_media_t *media;
|
|
/*
|
|
* Check the local hold status of each media to see if the
|
|
* media is already locally held or not.
|
|
*/
|
|
GSMSDP_FOR_ALL_MEDIA(media, dcb) {
|
|
if (!GSMSDP_MEDIA_ENABLED(media)) {
|
|
continue;
|
|
}
|
|
if (!FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
|
|
/* found one media that is not on hold */
|
|
return (FALSE);
|
|
}
|
|
}
|
|
/* all media streams are local held */
|
|
return (TRUE);
|
|
}
|
|
|
|
/**
|
|
* The function gets the numbmer of media in local hold.
|
|
*
|
|
* @param[in]dcb - pointer to fsmdef_dcb_t.
|
|
*
|
|
* @return uint16_t for the number of media in local hold.
|
|
*
|
|
* @pre (dcb not_eq NULL)
|
|
*/
|
|
static unsigned int
|
|
fsmdef_num_media_in_local_hold (fsmdef_dcb_t *dcb)
|
|
{
|
|
fsmdef_media_t *media;
|
|
unsigned int num_local_hold = 0;
|
|
|
|
/* Check the local hold status of the media(s) */
|
|
GSMSDP_FOR_ALL_MEDIA(media, dcb) {
|
|
if (!GSMSDP_MEDIA_ENABLED(media)) {
|
|
continue;
|
|
}
|
|
if (FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
|
|
num_local_hold++;
|
|
}
|
|
}
|
|
return (num_local_hold);
|
|
}
|
|
|
|
/**
|
|
* The function is a convenient function to set each local hold in
|
|
* SDP for each media if it is marked as locally held.
|
|
*
|
|
* @param[in]dcb - pointer to fsmdef_dcb_t.
|
|
*
|
|
* @return None
|
|
*
|
|
* @pre (dcb not_eq NULL)
|
|
*/
|
|
static void
|
|
fsmdef_set_per_media_local_hold_sdp (fsmdef_dcb_t *dcb)
|
|
{
|
|
fsmdef_media_t *media;
|
|
|
|
GSMSDP_FOR_ALL_MEDIA(media, dcb) {
|
|
if (!GSMSDP_MEDIA_ENABLED(media)) {
|
|
continue;
|
|
}
|
|
if (FSM_CHK_FLAGS(media->hold, FSM_HOLD_LCL)) {
|
|
/* set local hold to this media entry */
|
|
gsmsdp_set_local_hold_sdp(dcb, media);
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
fsmdef_init_dcb (fsmdef_dcb_t *dcb, callid_t call_id,
|
|
fsmdef_call_types_t call_type,
|
|
string_t called_number, line_t line, fsm_fcb_t *fcb)
|
|
{
|
|
string_t calling_name;
|
|
int blocking;
|
|
char name[MAX_LINE_NAME_SIZE];
|
|
|
|
dcb->call_id = call_id;
|
|
dcb->line = line;
|
|
|
|
dcb->spoof_ringout_requested = FALSE;
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
|
|
dcb->log_disp = CC_CALL_LOG_DISP_UNKNWN;
|
|
|
|
fsmutil_init_groupid(dcb, call_id, call_type);
|
|
|
|
/*
|
|
* Fill in as much of the caller_id data as possible.
|
|
* Different data is available based on the call_type.
|
|
*/
|
|
switch (call_type) {
|
|
case FSMDEF_CALL_TYPE_OUTGOING:
|
|
config_get_value(CFGID_CALLERID_BLOCKING, &blocking, sizeof(blocking));
|
|
if (line != 0) {
|
|
sip_config_get_display_name(line, name, sizeof(name));
|
|
}
|
|
if (blocking & 1 || line == 0) {
|
|
calling_name = SIP_HEADER_ANONYMOUS_STR;
|
|
} else {
|
|
calling_name = name;
|
|
}
|
|
dcb->caller_id.calling_name = strlib_update(dcb->caller_id.calling_name,
|
|
calling_name);
|
|
dcb->caller_id.calling_number =
|
|
strlib_update(dcb->caller_id.calling_number, name);
|
|
|
|
/*
|
|
* called_xxx data will be set when the fsmdef receives the dialstring.
|
|
*/
|
|
dcb->caller_id.called_name = strlib_empty();
|
|
dcb->caller_id.called_number = strlib_empty();
|
|
dcb->caller_id.orig_rpid_number = strlib_empty();
|
|
|
|
dcb->inbound = FALSE;
|
|
|
|
break;
|
|
|
|
case FSMDEF_CALL_TYPE_INCOMING:
|
|
case FSMDEF_CALL_TYPE_FORWARD:
|
|
/*
|
|
* calling_xxx data will be set when the fsmdef receives the setup.
|
|
*/
|
|
dcb->caller_id.calling_name = strlib_empty();
|
|
dcb->caller_id.calling_number = strlib_empty();
|
|
|
|
dcb->caller_id.last_redirect_name = strlib_empty();
|
|
dcb->caller_id.last_redirect_number = strlib_empty();
|
|
dcb->caller_id.orig_called_name = strlib_empty();
|
|
dcb->caller_id.orig_called_number = strlib_empty();
|
|
dcb->caller_id.orig_rpid_number = strlib_empty();
|
|
|
|
sip_config_get_display_name(line, name, sizeof(name));
|
|
dcb->caller_id.called_name =
|
|
strlib_update(dcb->caller_id.called_name, name);
|
|
dcb->caller_id.called_number =
|
|
strlib_update(dcb->caller_id.called_number, called_number);
|
|
|
|
dcb->inbound = TRUE;
|
|
|
|
break;
|
|
|
|
case FSMDEF_CALL_TYPE_NONE:
|
|
dcb->caller_id.calling_name = strlib_empty();
|
|
dcb->caller_id.calling_number = strlib_empty();
|
|
dcb->caller_id.called_name = strlib_empty();
|
|
dcb->caller_id.called_number = strlib_empty();
|
|
dcb->caller_id.alt_calling_number = strlib_empty();
|
|
dcb->caller_id.last_redirect_name = strlib_empty();
|
|
dcb->caller_id.last_redirect_number = strlib_empty();
|
|
dcb->caller_id.orig_called_name = strlib_empty();
|
|
dcb->caller_id.orig_called_number = strlib_empty();
|
|
dcb->caller_id.orig_rpid_number = strlib_empty();
|
|
dcb->inbound = FALSE;
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
} /* switch (call_type) { */
|
|
|
|
dcb->caller_id.display_calling_number = TRUE;
|
|
dcb->caller_id.display_called_number = TRUE;
|
|
|
|
/* Initially, assume ui update is required for a new call. */
|
|
dcb->ui_update_required = TRUE;
|
|
dcb->placed_call_update_required = TRUE;
|
|
|
|
dcb->is_conf_call = FALSE; /*Initially, set to conf call false*/
|
|
|
|
dcb->digit_cnt = 0;
|
|
|
|
dcb->call_type = call_type;
|
|
dcb->orientation = CC_ORIENTATION_NONE;
|
|
|
|
dcb->caller_id.call_instance_id = 0;
|
|
|
|
dcb->msgs_sent = FSMDEF_MSG_NONE;
|
|
dcb->msgs_rcvd = FSMDEF_MSG_NONE;
|
|
|
|
dcb->send_release = FALSE;
|
|
|
|
dcb->inband = FALSE;
|
|
dcb->inband_received = FALSE;
|
|
dcb->outofband = 0;
|
|
|
|
dcb->remote_sdp_present = FALSE;
|
|
dcb->remote_sdp_in_ack = FALSE;
|
|
|
|
dcb->sdp = NULL;
|
|
dcb->src_sdp_version = 0;
|
|
|
|
dcb->dial_mode = DIAL_MODE_NUMERIC;
|
|
|
|
dcb->hold_reason = CC_REASON_NONE;
|
|
|
|
dcb->pd_updated = FALSE;
|
|
|
|
dcb->alerting_tone = VCM_NO_TONE;
|
|
dcb->tone_direction = VCM_PLAY_TONE_TO_EAR;
|
|
|
|
dcb->alert_info = ALERTING_NONE;
|
|
|
|
dcb->dialplan_tone = FALSE;
|
|
|
|
dcb->active_tone = VCM_NO_TONE;
|
|
|
|
dcb->monrec_tone_action = FSMDEF_MRTONE_NO_ACTION;
|
|
dcb->monitor_tone_direction = VCM_PLAY_TONE_TO_EAR;
|
|
dcb->recorder_tone_direction = VCM_PLAY_TONE_TO_EAR;
|
|
|
|
dcb->play_tone_action = FSMDEF_PLAYTONE_NO_ACTION;
|
|
|
|
dcb->fcb = fcb;
|
|
|
|
dcb->early_error_release = FALSE;
|
|
|
|
dcb->active_feature = CC_FEATURE_NONE;
|
|
|
|
/* Release transient timers if any have been allocated */
|
|
if (dcb->err_onhook_tmr) {
|
|
(void) cprDestroyTimer(dcb->err_onhook_tmr);
|
|
dcb->err_onhook_tmr = NULL;
|
|
}
|
|
if (dcb->req_pending_tmr) {
|
|
(void) cprDestroyTimer(dcb->req_pending_tmr);
|
|
dcb->req_pending_tmr = NULL;
|
|
}
|
|
|
|
FSM_SET_SECURITY_STATUS(dcb, CC_SECURITY_UNKNOWN);
|
|
FSM_SET_POLICY(dcb, CC_POLICY_UNKNOWN);
|
|
dcb->session = PRIMARY;
|
|
|
|
dcb->dsp_out_of_resources = FALSE;
|
|
|
|
if (dcb->selected) {
|
|
g_numofselected_calls--;
|
|
}
|
|
|
|
dcb->selected = FALSE;
|
|
|
|
dcb->select_pending = FALSE;
|
|
dcb->call_not_counted_in_mnc_bt = FALSE;
|
|
if (g_disable_mass_reg_debug_print == FALSE) {
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"call_not_counted_in_mnc_bt = FALSE\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, line, call_id, "fsmdef_init_dcb"));
|
|
}
|
|
|
|
/* clear all bit flags */
|
|
dcb->flags = 0;
|
|
dcb->onhook_received = FALSE;
|
|
|
|
dcb->cur_video_avail = SDP_DIRECTION_INACTIVE;
|
|
|
|
if ( s_session_video_dir != SDP_MAX_QOS_DIRECTIONS &&
|
|
call_type == FSMDEF_CALL_TYPE_OUTGOING ) {
|
|
dcb->video_pref = s_session_video_dir;
|
|
s_session_video_dir = SDP_MAX_QOS_DIRECTIONS;
|
|
} else {
|
|
dcb->video_pref = s_default_video_dir;
|
|
}
|
|
|
|
gsmsdp_init_media_list(dcb);
|
|
|
|
dcb->join_call_id = CC_NO_CALL_ID;
|
|
dcb->callref = 0;
|
|
|
|
dcb->ice_ufrag = NULL;
|
|
dcb->ice_pwd = NULL;
|
|
dcb->ice_default_candidate_addr[0] = '\0';
|
|
|
|
dcb->digest_alg[0] = '\0';
|
|
dcb->digest[0] = '\0';
|
|
}
|
|
|
|
|
|
static void
|
|
fsmdef_free_dcb (fsmdef_dcb_t *dcb)
|
|
{
|
|
if (dcb == NULL) {
|
|
return;
|
|
}
|
|
|
|
strlib_free(dcb->caller_id.calling_name);
|
|
strlib_free(dcb->caller_id.calling_number);
|
|
strlib_free(dcb->caller_id.alt_calling_number);
|
|
strlib_free(dcb->caller_id.called_name);
|
|
strlib_free(dcb->caller_id.called_number);
|
|
|
|
strlib_free(dcb->caller_id.last_redirect_name);
|
|
strlib_free(dcb->caller_id.last_redirect_number);
|
|
strlib_free(dcb->caller_id.orig_called_name);
|
|
strlib_free(dcb->caller_id.orig_called_number);
|
|
strlib_free(dcb->caller_id.orig_rpid_number);
|
|
|
|
/* Cancel any existing error onhook timer */
|
|
if (dcb->err_onhook_tmr) {
|
|
(void) cprCancelTimer(dcb->err_onhook_tmr);
|
|
(void) cprDestroyTimer(dcb->err_onhook_tmr);
|
|
dcb->err_onhook_tmr = NULL;
|
|
}
|
|
|
|
/* Cancel any existing request pending timer */
|
|
if (dcb->req_pending_tmr) {
|
|
(void) cprCancelTimer(dcb->req_pending_tmr);
|
|
(void) cprDestroyTimer(dcb->req_pending_tmr);
|
|
dcb->req_pending_tmr = NULL;
|
|
}
|
|
|
|
/* Cancel any existing ringback delay timer */
|
|
if (dcb->ringback_delay_tmr) {
|
|
(void) cprCancelTimer(dcb->ringback_delay_tmr);
|
|
}
|
|
|
|
// Free the call instance id
|
|
if (dcb->caller_id.call_instance_id != 0) {
|
|
fsmutil_free_ci_id(dcb->caller_id.call_instance_id, dcb->line);
|
|
}
|
|
|
|
/* clean media list */
|
|
gsmsdp_clean_media_list(dcb);
|
|
|
|
gsmsdp_free(dcb);
|
|
|
|
fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE, NULL,
|
|
LSM_NO_LINE, NULL);
|
|
|
|
/*
|
|
* Cache random numbers for SRTP keys
|
|
*/
|
|
gsmsdp_cache_crypto_keys();
|
|
|
|
}
|
|
|
|
void
|
|
fsmdef_free_cb (fim_icb_t *icb, callid_t call_id)
|
|
{
|
|
fsm_fcb_t *fcb = NULL;
|
|
fsmdef_dcb_t *dcb = NULL;
|
|
|
|
if (call_id != CC_NO_CALL_ID) {
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
if (dcb != NULL) {
|
|
fcb = dcb->fcb;
|
|
fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE,
|
|
NULL, LSM_NO_LINE, NULL);
|
|
/* fsmdef_init_dcb(...,NULL) will always set the fcb ptr to NULL,
|
|
so if fsmdef_free_cb were called on that we'd have fcb==NULL here */
|
|
if (fcb != NULL) {
|
|
fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
|
|
}
|
|
} else {
|
|
|
|
fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
|
|
if (fcb != NULL) {
|
|
fsm_init_fcb(fcb, CC_NO_CALL_ID, FSMDEF_NO_DCB, FSM_TYPE_NONE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* ROUTINE: fsmdef_get_new_dcb
|
|
*
|
|
* DESCRIPTION: return a new dcb initialized with the given data
|
|
*
|
|
* PARAMETERS:
|
|
* call_id: call_id
|
|
*
|
|
* RETURNS:
|
|
* dcb: the new dcb
|
|
*
|
|
* NOTES: None
|
|
*/
|
|
fsmdef_dcb_t *
|
|
fsmdef_get_new_dcb (callid_t call_id)
|
|
{
|
|
static const char fname[] = "fsmdef_get_new_dcb";
|
|
fsmdef_dcb_t *dcb = NULL;
|
|
|
|
/*
|
|
* Get a free dcb.
|
|
*/
|
|
if ((dcb = fsmdef_get_dcb_by_call_id(CC_NO_CALL_ID)) == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), call_id, 0, fname,
|
|
"no dcbs available");
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
dcb->call_id = call_id;
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_PTR),
|
|
dcb->call_id, dcb->line, fname, dcb);
|
|
|
|
return (dcb);
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* Returns dcb related to connected call.
|
|
*
|
|
* @param none
|
|
*
|
|
* @return dcb of connected call.
|
|
*
|
|
* @pre none
|
|
*/
|
|
|
|
fsmdef_dcb_t *
|
|
fsmdef_get_connected_call (void)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
fsm_fcb_t *fcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->call_id != CC_NO_CALL_ID) {
|
|
fcb = dcb->fcb;
|
|
if ((fcb != NULL) && (fcb->state == FSMDEF_S_RESUME_PENDING ||
|
|
fcb->state == FSMDEF_S_CONNECTED ||
|
|
fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND)) {
|
|
|
|
return (dcb);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Returns dcb related to alerting out call.
|
|
*
|
|
* @param none
|
|
*
|
|
* @return dcb of outgoing alerting call.
|
|
*
|
|
* @pre none
|
|
*/
|
|
static fsmdef_dcb_t *
|
|
fsmdef_get_alertingout_call (void)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
fsm_fcb_t *fcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->call_id != CC_NO_CALL_ID) {
|
|
fcb = dcb->fcb;
|
|
if ((fcb != NULL) && (fcb->state == FSMDEF_S_OUTGOING_ALERTING)) {
|
|
return (dcb);
|
|
}
|
|
}
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_get_active_call_cnt
|
|
*
|
|
* Return the count of active calls aside from the
|
|
* callid passed in.
|
|
*/
|
|
int
|
|
fsmdef_get_active_call_cnt (callid_t callId)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
int cnt = 0;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if ((dcb->call_id != CC_NO_CALL_ID) && (dcb->call_id != callId)) {
|
|
cnt++;
|
|
}
|
|
}
|
|
|
|
return (cnt);
|
|
}
|
|
|
|
|
|
/*
|
|
* return the dcbs and count of calls that are ringing and
|
|
* are in error state not including the given call_id
|
|
*
|
|
* @param pointer to dcbs array
|
|
* @param call_id that should be ignored from search
|
|
*
|
|
* @return int number of ringing or error state calls
|
|
*
|
|
* @pre none
|
|
*/
|
|
static int
|
|
fsmdef_get_ringing_n_error_call_dcbs (fsmdef_dcb_t **dcbs, callid_t ignore_call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
int cnt = 0;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->spoof_ringout_applied ||
|
|
((dcb->call_id != CC_NO_CALL_ID) &&
|
|
(dcb->call_id != ignore_call_id) &&
|
|
(dcb->fcb && ((dcb->fcb->state <= FSMDEF_S_CONNECTING) ||
|
|
(dcb->fcb->state == FSMDEF_S_RELEASING))))) {
|
|
dcbs[cnt++] = dcb;
|
|
}
|
|
}
|
|
|
|
return (cnt);
|
|
}
|
|
|
|
|
|
int
|
|
fsmdef_get_dcbs_in_held_state (fsmdef_dcb_t **dcbs, callid_t ignore_call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
int cnt = 0;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if ((dcb->call_id != CC_NO_CALL_ID) &&
|
|
(dcb->call_id != ignore_call_id) &&
|
|
(dcb->fcb && (dcb->fcb->state == FSMDEF_S_HOLDING ||
|
|
dcb->fcb->state == FSMDEF_S_HOLD_PENDING))) {
|
|
dcbs[cnt++] = dcb;
|
|
}
|
|
}
|
|
|
|
return (cnt);
|
|
}
|
|
|
|
void fsmdef_call_cc_state_dialing (fsmdef_dcb_t *dcb, boolean suppress)
|
|
{
|
|
cc_state_data_dialing_t data;
|
|
|
|
if ( dcb->caller_id.called_number[0] == '\0' ) {
|
|
data.play_dt = TRUE;
|
|
} else {
|
|
data.play_dt = FALSE;
|
|
}
|
|
|
|
data.suppress_stutter = suppress;
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_DIALING,
|
|
(cc_state_data_t *)(&data));
|
|
}
|
|
|
|
/*
|
|
* fsmdef_get_other_dcb_by_line
|
|
*
|
|
* return the dcb of the call that is active on a given line,
|
|
* not including the given call_id
|
|
*/
|
|
fsmdef_dcb_t *
|
|
fsmdef_get_other_dcb_by_line (callid_t call_id, line_t line)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
fsmdef_dcb_t *dcb_found = NULL;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if ((dcb->call_id != CC_NO_CALL_ID) &&
|
|
(dcb->line == line) && (dcb->call_id != call_id)) {
|
|
dcb_found = dcb;
|
|
}
|
|
}
|
|
|
|
return (dcb_found);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_are_there_selected_calls_onotherline
|
|
*
|
|
* @param line - line number
|
|
*
|
|
* loop thru the dcbs and check if there are selected calls
|
|
* on a line other than this one
|
|
*
|
|
* @return TRUE, there are
|
|
* FALSE there are not
|
|
*/
|
|
boolean fsmdef_are_there_selected_calls_onotherline (line_t line)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->selected) {
|
|
if (dcb->line != line) {
|
|
return (TRUE);
|
|
}
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_are_join_calls_on_same_line
|
|
*
|
|
* @param line - line number
|
|
*
|
|
* loop thru the dcbs and check if the line passed is the
|
|
* same as where the initial join started
|
|
*
|
|
* @return TRUE, they are on the same line
|
|
* FALSE not on the same line
|
|
*/
|
|
boolean fsmdef_are_join_calls_on_same_line (line_t line)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->call_id == g_b2bjoin_callid) {
|
|
if (dcb->line != line) {
|
|
return (FALSE);
|
|
} else {
|
|
return (TRUE);
|
|
}
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Handles media capability update feature event from platform when
|
|
* media stream capability changes.
|
|
*
|
|
* @param[in]msg - pointer to the cc_feature_t.
|
|
*
|
|
* @return None.
|
|
*
|
|
* @pre (msg not_eq NULL)
|
|
*/
|
|
void
|
|
fsmdef_update_media_cap_feature_event (cc_feature_t *msg)
|
|
{
|
|
static const char fname[] = "fsmdef_update_media_cap_feature_event";
|
|
fsmdef_dcb_t *dcb;
|
|
fsm_fcb_t *fcb;
|
|
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"\n", DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, msg->call_id, fname));
|
|
|
|
/*
|
|
* Find the connected call to send the media capability update
|
|
* event to. There can be more than one call chains in
|
|
* connected state for an example, a call that participates in
|
|
* local conference, barged, monitored. All calls that
|
|
* are active are sent the update event. Each call leg will handle
|
|
* the event.
|
|
*
|
|
*/
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->call_id != CC_NO_CALL_ID) {
|
|
fcb = dcb->fcb;
|
|
if ((fcb != NULL) && (fcb->state == FSMDEF_S_RESUME_PENDING ||
|
|
fcb->state == FSMDEF_S_CONNECTED)) {
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_UPD_MEDIA_CAP, NULL);
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Function to find and hold any connected call.
|
|
*
|
|
* @param call_id call id of the call
|
|
* @param wait flag to indicate if the caller has to wait ton invoke the event
|
|
* @param src_id source id of the caller where to post hold or endcall event.
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre (wait == FALSE)
|
|
*/
|
|
|
|
static void
|
|
fsmdef_find_and_hold_connected_call (callid_t call_id, boolean *wait,
|
|
cc_srcs_t src_id)
|
|
{
|
|
fsmdef_dcb_t *con_dcb;
|
|
fsmcnf_ccb_t *ccb;
|
|
fsmcnf_ccb_t *con_ccb;
|
|
fsmxfr_xcb_t *xcb;
|
|
cc_feature_data_t data;
|
|
callid_t other_call_id;
|
|
callid_t other_call_id2;
|
|
|
|
*wait = FALSE;
|
|
|
|
/*
|
|
* Place the connected call, if there is one, on hold,
|
|
* but there are some restrictions to ignore the hold request:
|
|
* 1. the connected call must be different than the one requesting the hold,
|
|
* 2. the connected call is involved with this call in a conference and
|
|
* the conference is active.
|
|
* NOTE: for case 2, why do we not send a hold for each of the calls
|
|
* involved in the conference? Because, the fsmcnf will generate the
|
|
* second hold for the other leg of the conference when it receives
|
|
* this hold.
|
|
* 3. the connected call is in a conference with another call that is
|
|
* involved with a transfer. This is the case when two calls are in a
|
|
* conference and a bridge decides to transfer one leg of the bridge
|
|
* using REFER. I.E., cnf between A-B and A-C. B initiates transfer to D.
|
|
* B holds A-B, sends REFER to A, A holds A-B, initiates A-D, D answers,
|
|
* A hangs up A-B and connects conference. So, when A initiates A-D,
|
|
* we do not want to hold the active bridge between A-C.
|
|
*/
|
|
con_dcb = fsmdef_get_connected_call();
|
|
if ((con_dcb != NULL) && ((con_dcb->call_id != call_id) ||
|
|
(con_dcb->spoof_ringout_applied == FALSE))) {
|
|
ccb = fsmcnf_get_ccb_by_call_id(call_id);
|
|
con_ccb = fsmcnf_get_ccb_by_call_id(con_dcb->call_id);
|
|
|
|
|
|
if ((ccb == NULL) || (con_ccb == NULL) ||
|
|
((ccb == con_ccb) && (ccb->active != TRUE)) || (ccb != con_ccb)) {
|
|
other_call_id = fsmcnf_get_other_call_id(con_ccb, con_dcb->call_id);
|
|
xcb = fsmxfr_get_xcb_by_call_id(other_call_id);
|
|
|
|
other_call_id2 = fsmxfr_get_other_call_id(xcb, other_call_id);
|
|
|
|
if (call_id != other_call_id2) {
|
|
*wait = TRUE;
|
|
|
|
data.hold.call_info.type = CC_FEAT_HOLD;
|
|
data.hold.call_info.data.hold_resume_reason =
|
|
CC_REASON_INTERNAL;
|
|
data.hold.msg_body.num_parts = 0;
|
|
data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
cc_int_feature(src_id, CC_SRC_GSM, con_dcb->call_id,
|
|
con_dcb->line, CC_FEATURE_HOLD, &data);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Function post a event to end the call which are in ringing
|
|
* reorder or busy and connecting state. This would indicate call function to
|
|
* wait on the existing event.
|
|
*
|
|
* @param call_id that should be ignored from search
|
|
* @param pointer to boolean to indicate if the caller has to wait
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre none
|
|
*/
|
|
static void
|
|
fsmdef_find_and_handle_ring_connecting_releasing_calls (callid_t call_id, boolean *wait)
|
|
{
|
|
int i;
|
|
int act_dcb_cnt;
|
|
fsmdef_dcb_t *act_dcb;
|
|
fsmdef_dcb_t *act_dcbs[LSM_MAX_CALLS];
|
|
cc_feature_data_t data;
|
|
|
|
*wait = FALSE;
|
|
|
|
data.endcall.cause = CC_CAUSE_NORMAL;
|
|
data.endcall.dialstring[0] = '\0';
|
|
|
|
act_dcb_cnt = fsmdef_get_ringing_n_error_call_dcbs(act_dcbs, call_id);
|
|
for (i = 0; i < act_dcb_cnt; i++) {
|
|
act_dcb = act_dcbs[i];
|
|
/*
|
|
* Clear all the outgoing ringing lines (if there are any).
|
|
*/
|
|
if (act_dcb->call_type == FSMDEF_CALL_TYPE_OUTGOING ||
|
|
act_dcb->spoof_ringout_applied) {
|
|
*wait = TRUE;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, act_dcb->call_id,
|
|
act_dcb->line, CC_FEATURE_END_CALL, &data);
|
|
|
|
}
|
|
else if (act_dcb->fcb->state == FSMDEF_S_CONNECTING) {
|
|
/* If the call is in connecting state, then wait till SIP
|
|
* response to get the call in connected state.
|
|
* The call can be put on hold only when call is connected.
|
|
*/
|
|
*wait = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
fsmdef_end_call (fsmdef_dcb_t *dcb, cc_causes_t cause)
|
|
{
|
|
cc_feature_data_t data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
data.endcall.cause = cause;
|
|
data.endcall.dialstring[0] = '\0';
|
|
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
|
|
CC_FEATURE_END_CALL, &data);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_clear_preserved_calls
|
|
*
|
|
* Release any calls in the preserved state
|
|
*/
|
|
static void
|
|
fsmdef_clear_preserved_calls (boolean *wait)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
*wait = FALSE;
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if ((dcb->call_id != CC_NO_CALL_ID) &&
|
|
(dcb->fcb->state == FSMDEF_S_PRESERVED)) {
|
|
*wait = TRUE;
|
|
fsmdef_end_call(dcb, CC_CAUSE_NORMAL);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Checks to see if actions are required for existing calls before the new
|
|
* call can be activated.
|
|
*
|
|
* 1. Place the currently active connected call on hold.
|
|
* 2. Clear any ringing calls.
|
|
* 3. Release any call in the preserved state.
|
|
*
|
|
* @param is_newcall To indicate if this is a new call
|
|
* @param src_id source of the caller
|
|
* @param call_id call-id
|
|
* @param line line number
|
|
* @param feature feature which is waiting on hodling call.
|
|
* @param feature data
|
|
*
|
|
* @return TRUE if actions require delay before the new call may begin
|
|
* FALSE if no actions are required an the new call may begin
|
|
*
|
|
* @pre (wait, wait2, wait3 all FALSE)
|
|
*/
|
|
static boolean
|
|
fsmdef_wait_to_start_new_call (boolean is_newcall, cc_srcs_t src_id, callid_t call_id,
|
|
line_t line, cc_features_t feature,
|
|
cc_feature_data_t *data)
|
|
{
|
|
boolean wait = FALSE;
|
|
boolean wait2 = FALSE;
|
|
boolean wait3 = FALSE;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsmdef_find_and_hold_connected_call(call_id, &wait, src_id);
|
|
|
|
fsmdef_find_and_handle_ring_connecting_releasing_calls(call_id, &wait2);
|
|
|
|
fsmdef_clear_preserved_calls(&wait3);
|
|
|
|
/*
|
|
* Requeue the message because we need to wait for call actions to complete
|
|
*/
|
|
if ((wait) || (wait2) || (wait3)) {
|
|
cc_int_feature(src_id, CC_SRC_GSM, call_id, line, feature, data);
|
|
}
|
|
|
|
return (wait | wait2 | wait3);
|
|
}
|
|
|
|
static cc_causes_t
|
|
fsmdef_get_cause (boolean data_valid, cc_feature_data_t *data)
|
|
{
|
|
cc_causes_t cause;
|
|
|
|
if (data_valid) {
|
|
cause = data->endcall.cause;
|
|
} else {
|
|
cause = CC_CAUSE_NORMAL;
|
|
}
|
|
|
|
return (cause);
|
|
}
|
|
|
|
|
|
/**
|
|
* common function to release a call given cause code
|
|
* i.e onhooks the given call.
|
|
*
|
|
* @param[in] fcb The pointer to the fsm_fcb_t structure of this
|
|
* call chain.
|
|
* @param[in] cause release cause code.
|
|
*
|
|
* @param[in] send_release When set to TRUE the function sends release
|
|
* and waits for release complete.
|
|
* When set to FALSE the function cleans up
|
|
* dcb and release fcb.
|
|
*
|
|
* @pre (fcb not_eq NULL)
|
|
*
|
|
* @return sm_rcs_t indicates whether the execution of
|
|
* next statmachine to end (SM_RC_END) or clean up
|
|
* (SM_RC_CLEANUP)
|
|
*
|
|
* @Usage Note: The function uses send_release flag to
|
|
* as an indicator for sending release out or not.
|
|
* If release is sent, the function transitions fsmdef's
|
|
* state to FSMDEF_S_RELEASING and
|
|
* return the SM_RC_END to waite for release complete.
|
|
*
|
|
* Otherwise, it cleans up dcb and releases fcb and
|
|
* return SM_RC_CLEANUP to the caller.
|
|
*
|
|
* If the SM_RC_CLEANUP is returned, the caller should
|
|
* terminate any access or perform any operation
|
|
* afterward.
|
|
*/
|
|
sm_rcs_t
|
|
fsmdef_release (fsm_fcb_t *fcb, cc_causes_t cause, boolean send_release)
|
|
{
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_state_data_t state_data;
|
|
cc_kfact_t kfactor;
|
|
fsmdef_media_t *media;
|
|
char tmp_str[STATUS_LINE_MAX_LEN];
|
|
|
|
if (!dcb) {
|
|
/* Already been released */
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Entered. cause= %s\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, __FUNCTION__), cc_cause_name(cause));
|
|
|
|
if (g_dock_undock_event != MEDIA_INTERFACE_UPDATE_NOT_REQUIRED) {
|
|
ui_update_media_interface_change(dcb->line, dcb->call_id, MEDIA_INTERFACE_UPDATE_FAIL);
|
|
}
|
|
memset(&kfactor, 0, sizeof(cc_kfact_t));
|
|
/* Cancel any existing autoanswer timer */
|
|
(void) cprCancelTimer(dcb->autoAnswerTimer);
|
|
|
|
|
|
/*
|
|
* Let Dialog Manager know that there is ONHOOK event
|
|
*/
|
|
fsmdef_notify_hook_event(fcb, CC_MSG_ONHOOK, NULL, CC_NO_CALL_ID,
|
|
CC_REASON_NONE, CC_MONITOR_NONE,CFWDALL_NONE);
|
|
|
|
media = gsmsdp_find_audio_media(dcb);
|
|
if ((media) && (media->direction != SDP_DIRECTION_INACTIVE)) {
|
|
fsmdef_get_rtp_stat(dcb, &kfactor);
|
|
}
|
|
|
|
if ( cause == CC_SIP_CAUSE_ANSWERED_ELSEWHERE ) {
|
|
ui_log_disposition(dcb->call_id, CC_CALL_LOG_DISP_IGNORE );
|
|
}
|
|
|
|
if ( cause == CC_CAUSE_RESP_TIMEOUT) {
|
|
if ((platGetPhraseText(STR_INDEX_RESP_TIMEOUT,
|
|
(char *) tmp_str,
|
|
STATUS_LINE_MAX_LEN - 1)) == CPR_SUCCESS) {
|
|
lsm_ui_display_status(tmp_str, dcb->line, dcb->call_id);
|
|
}
|
|
}
|
|
|
|
if (send_release) {
|
|
cc_int_release(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
cause, NULL, &kfactor);
|
|
/*
|
|
* Wait around for the release_complete.
|
|
*/
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_RELEASING);
|
|
|
|
/*
|
|
* Only move the UI if we changed the UI's state when the call was
|
|
* received.
|
|
*/
|
|
if ((dcb->line != LSM_NO_LINE) || (cause != CC_CAUSE_BUSY)) {
|
|
state_data.onhook.caller_id = dcb->caller_id;
|
|
state_data.onhook.local = FALSE;
|
|
state_data.onhook.cause = CC_CAUSE_NORMAL;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK,
|
|
&state_data);
|
|
}
|
|
return (SM_RC_END);
|
|
} else {
|
|
/*
|
|
* Only move the UI if we changed the UI's state when the call was
|
|
* initiated.
|
|
*/
|
|
if ((dcb->line != LSM_NO_LINE) || (cause != CC_CAUSE_BUSY)) {
|
|
state_data.onhook.caller_id = dcb->caller_id;
|
|
state_data.onhook.local = FALSE;
|
|
state_data.onhook.cause = CC_CAUSE_NORMAL;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK,
|
|
&state_data);
|
|
}
|
|
|
|
/*
|
|
* Only send a release complete to the remote end if they are waiting
|
|
* for it. This is the case when we have sent a proceeding or we
|
|
* have received a release.
|
|
*/
|
|
if (FSM_CHK_FLAGS(dcb->msgs_sent, FSMDEF_MSG_PROCEEDING) ||
|
|
FSM_CHK_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE)) {
|
|
cc_int_release_complete(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, cause, &kfactor);
|
|
}
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_IDLE);
|
|
fsmdef_free_dcb(dcb);
|
|
fsm_release(fcb, __LINE__, cause);
|
|
/*
|
|
* fsmdef has been released, indiate cleanup FSM chain.
|
|
*/
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* fsmdef_convert_esc_plus
|
|
*
|
|
* replaces an escaped "+" (%2B) with a real "+"
|
|
*/
|
|
static void
|
|
fsmdef_convert_esc_plus (const char *src_number)
|
|
{
|
|
int i, len;
|
|
char *number;
|
|
|
|
len = strlen(src_number) - 2;
|
|
number = (char *) src_number;
|
|
number[0] = '+';
|
|
for (i = 1; i < len; i++) {
|
|
number[i] = number[i + 2];
|
|
}
|
|
number[i] = '\0';
|
|
}
|
|
|
|
static boolean
|
|
fsmdef_compare_caller_id_string (string_t dest, string_t src)
|
|
{
|
|
if ((dest == NULL) && (src == NULL)) {
|
|
/*
|
|
* Strings are same.
|
|
*/
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((dest == NULL) || (src == NULL)) {
|
|
/*
|
|
* Strings differ.
|
|
*/
|
|
return (TRUE);
|
|
}
|
|
|
|
if (strncmp(dest, src, FSMDEF_MAX_CALLER_ID_LEN) != 0) {
|
|
/*
|
|
* Strings differ.
|
|
*/
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* Strings are same.
|
|
*/
|
|
return (FALSE);
|
|
}
|
|
|
|
static boolean
|
|
fsmdef_compare_caller_id (cc_caller_id_t *dest_caller_id,
|
|
cc_caller_id_t *src_caller_id)
|
|
{
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->calling_name,
|
|
src_caller_id->calling_name)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->calling_number,
|
|
src_caller_id->calling_number)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->called_name,
|
|
src_caller_id->called_name)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->called_number,
|
|
src_caller_id->called_number)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->orig_called_name,
|
|
src_caller_id->orig_called_name)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->orig_called_number,
|
|
src_caller_id->orig_called_number)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->last_redirect_name,
|
|
src_caller_id->last_redirect_name)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->last_redirect_number,
|
|
src_caller_id->last_redirect_number)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (fsmdef_compare_caller_id_string(dest_caller_id->orig_rpid_number,
|
|
src_caller_id->orig_rpid_number)) {
|
|
return (TRUE);
|
|
}
|
|
|
|
if (dest_caller_id->display_calling_number != src_caller_id->display_calling_number ||
|
|
dest_caller_id->display_called_number != src_caller_id->display_called_number ||
|
|
dest_caller_id->call_type != src_caller_id->call_type ||
|
|
dest_caller_id->call_instance_id != src_caller_id->call_instance_id) {
|
|
return (TRUE);
|
|
}
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
static void
|
|
fsmdef_mv_caller_id (fsmdef_dcb_t *dcb, cc_caller_id_t *caller_id)
|
|
{
|
|
/*
|
|
* Move the caller ID from the source to the storage in dcb if there
|
|
* is a change in what is already stored in the dcb.
|
|
*/
|
|
if (fsmdef_compare_caller_id(&dcb->caller_id, caller_id)) {
|
|
cc_mv_caller_id(&dcb->caller_id, caller_id);
|
|
dcb->ui_update_required = TRUE;
|
|
}
|
|
}
|
|
|
|
static void
|
|
fsmdef_update_callinfo (fsm_fcb_t *fcb, cc_feature_t *msg)
|
|
{
|
|
static const char fname[] = "fsmdef_update_callinfo";
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_data_t *feat_data = &(msg->data);
|
|
cc_action_data_t action_data;
|
|
cc_caller_id_t *caller_id;
|
|
|
|
if (msg->data_valid == FALSE) {
|
|
/* No data to use for update. Just ignore the event. */
|
|
return;
|
|
}
|
|
|
|
if ((feat_data->call_info.feature_flag & CC_UI_STATE) &&
|
|
(feat_data->call_info.ui_state == CC_UI_STATE_RINGOUT)) {
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, fname,
|
|
"setting spoof_ringout_requested");
|
|
|
|
dcb->spoof_ringout_requested = TRUE;
|
|
} else {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_RQSTD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_requested = FALSE;
|
|
}
|
|
|
|
caller_id = &feat_data->call_info.caller_id;
|
|
|
|
if (feat_data->call_info.feature_flag & CC_CALLER_ID) {
|
|
fsmdef_mv_caller_id(dcb, caller_id);
|
|
}
|
|
|
|
/*
|
|
* If CCM provides a call instance id and it does not match
|
|
* the current call instance id, free the current call instance
|
|
* id and set the call instance id to the newly provided value.
|
|
*/
|
|
if (feat_data->call_info.feature_flag & CC_CALL_INSTANCE &&
|
|
feat_data->call_info.caller_id.call_instance_id != dcb->caller_id.call_instance_id) {
|
|
if (dcb->caller_id.call_instance_id != 0) {
|
|
fsmutil_free_ci_id(dcb->caller_id.call_instance_id, dcb->line);
|
|
}
|
|
dcb->caller_id.call_instance_id =
|
|
feat_data->call_info.caller_id.call_instance_id;
|
|
fsmutil_set_ci_id(dcb->caller_id.call_instance_id, dcb->line);
|
|
dcb->ui_update_required = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Update security status
|
|
*/
|
|
fsmdef_update_callinfo_security_status(dcb, &feat_data->call_info);
|
|
|
|
/*
|
|
* Update call policy
|
|
*/
|
|
if (feat_data->call_info.feature_flag & CC_POLICY) {
|
|
if (dcb->policy != feat_data->call_info.policy) {
|
|
dcb->policy = feat_data->call_info.policy;
|
|
dcb->ui_update_required = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Save orientation so that UI call update can be done at
|
|
* any time that UI needs to be updated.
|
|
*/
|
|
if (feat_data->call_info.feature_flag & CC_ORIENTATION) {
|
|
if (dcb->orientation != feat_data->call_info.orientation) {
|
|
dcb->orientation = feat_data->call_info.orientation;
|
|
dcb->ui_update_required = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* This call info. event may be as part of media effecting
|
|
* signaling event (such as INVITE, re-INVITE, 180 etc.)
|
|
* which will be followed by actual media effected event. It is
|
|
* indicated by SIP stack to improve media cutting through as soonest.
|
|
* If SIP indicates that UI can be delayed then do not update call
|
|
* information now. The UI will be updated as part of media
|
|
* manipulation event that will follow.
|
|
* (Note: call info event is sent separately from the SIP signaling
|
|
* event currently and it is sent before SIP signaling event).
|
|
*/
|
|
if (feat_data->call_info.feature_flag & CC_DELAY_UI_UPDATE) {
|
|
/* Delay UI update */
|
|
} else {
|
|
/*
|
|
* Only perform a UI update if something changed.
|
|
*/
|
|
if (dcb->ui_update_required == TRUE
|
|
|| dcb->spoof_ringout_requested == TRUE) {
|
|
|
|
action_data.update_ui.action = CC_UPDATE_CALLER_INFO;
|
|
action_data.update_ui.data.caller_info = feat_data->call_info;
|
|
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
|
|
&action_data);
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id,
|
|
dcb->line, fname, "UI update");
|
|
} else {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, fname, "No UI update");
|
|
}
|
|
}
|
|
/* update callref */
|
|
if ( dcb->callref == 0 ) {
|
|
dcb->callref = feat_data->call_info.callref;
|
|
ui_update_callref(dcb->line, dcb->call_id, feat_data->call_info.callref);
|
|
}
|
|
/* update gcid */
|
|
if (feat_data->call_info.global_call_id[0] != '\0') {
|
|
ui_update_gcid(dcb->line, dcb->call_id, feat_data->call_info.global_call_id);
|
|
/*
|
|
* store the gcid lcb, this is used to prevent short ringing
|
|
* in scenarios where a call is routed to the calling phone.
|
|
*/
|
|
lsm_update_gcid(dcb->call_id, feat_data->call_info.global_call_id);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function: fsmdef_set_feature_timer
|
|
*
|
|
* Description: This function is called to set (start) timer for a
|
|
* given timer. The context of the timer is set so that upon
|
|
* expiration the corresponding context (dcb) can be obtained.
|
|
*
|
|
* Parameters:
|
|
* dcb - pointer to the fsmdef_dcb_t. The caller must ensure
|
|
* dcb is not NULL.
|
|
* timer - pointer to cprTimer_t. The caller must ensure that
|
|
* timer is not NULL.
|
|
* duration - the time duration for the timer to be set.
|
|
*
|
|
* Returns:
|
|
* N/A.
|
|
*/
|
|
static void
|
|
fsmdef_set_feature_timer (fsmdef_dcb_t *dcb, cprTimer_t *timer,
|
|
uint32_t duration)
|
|
{
|
|
static const char fname[] = "fsmdef_set_feature_timer";
|
|
|
|
if (cprCancelTimer(*timer) != CPR_SUCCESS) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CANCEL_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Feature", cpr_errno);
|
|
|
|
return;
|
|
}
|
|
|
|
if (cprStartTimer(*timer, duration, (void *)(long)dcb->call_id) == CPR_FAILURE) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Feature", cpr_errno);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fsmdef_set_req_pending_timer (fsmdef_dcb_t *dcb)
|
|
{
|
|
static const char fname[] = "fsmdef_set_req_pending_timer";
|
|
uint32_t msec;
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
|
|
return;
|
|
}
|
|
|
|
if (!dcb->req_pending_tmr) {
|
|
dcb->req_pending_tmr = cprCreateTimer("Request Pending",
|
|
GSM_REQ_PENDING_TIMER,
|
|
TIMER_EXPIRATION,
|
|
gsm_msg_queue);
|
|
|
|
if (dcb->req_pending_tmr == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Request Pending");
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (dcb->inbound) {
|
|
// We did not initiate this call, so set timer between 0 and 2000ms
|
|
msec = abs(cpr_rand()) % 2000;
|
|
} else {
|
|
// We initiated this call, so set the timer between 2100 and 4000ms
|
|
msec = abs(cpr_rand()) % 1900 + 2100;
|
|
}
|
|
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Starting req pending timer for %d ms.\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname), msec);
|
|
|
|
fsmdef_set_feature_timer(dcb, &dcb->req_pending_tmr, msec);
|
|
}
|
|
|
|
static void
|
|
fsmdef_set_ringback_delay_timer (fsmdef_dcb_t *dcb)
|
|
{
|
|
static const char fname[] = "fsmdef_set_ringback_delay_timer";
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
|
|
return;
|
|
}
|
|
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Starting Ringback Delay timer"
|
|
" for %d ms.\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname), RINGBACK_DELAY);
|
|
|
|
fsmdef_set_feature_timer(dcb, &dcb->ringback_delay_tmr, RINGBACK_DELAY);
|
|
}
|
|
|
|
/*******************************************************************
|
|
* event functions
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_default (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
|
|
if (fcb->dcb) {
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
}
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* Default event handler for feature_ack event
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_default_feature_ack (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_default_feature_ack";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_ack_t *msg = (cc_feature_ack_t *) event->msg;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_ev_default_feature_ack"));
|
|
|
|
if (ftr_id == CC_FEATURE_SELECT) {
|
|
/* Reeceived the response for select, so turn the flag off */
|
|
dcb->select_pending = FALSE;
|
|
if (dcb->selected) {
|
|
dcb->selected = FALSE;
|
|
g_numofselected_calls--;
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"call is unselected and number of selected \
|
|
calls on the phone is %d\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, msg->call_id, fname),
|
|
g_numofselected_calls);
|
|
|
|
} else {
|
|
dcb->selected = TRUE;
|
|
if ((g_b2bjoin_pending == FALSE) &&
|
|
(dcb->active_feature == CC_FEATURE_B2B_JOIN)) {
|
|
g_b2bjoin_pending = TRUE;
|
|
g_b2bjoin_callid = dcb->call_id;
|
|
}
|
|
g_numofselected_calls++;
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"call is selected and number of selected \
|
|
calls on the phone is %d\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname),
|
|
g_numofselected_calls);
|
|
}
|
|
ui_call_selected(dcb->line, lsm_get_ui_id(dcb->call_id), (dcb->selected)?CC_DIALOG_LOCKED:CC_DIALOG_UNLOCKED);
|
|
|
|
} else if (dcb->active_feature != ftr_id) {
|
|
// check if we are getting feature_ack for the active feature
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"feature_ack rcvd for %s but %s is active\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname),
|
|
cc_feature_name(ftr_id), cc_feature_name(dcb->active_feature));
|
|
|
|
}
|
|
|
|
// reset active feature
|
|
dcb->active_feature = CC_FEATURE_NONE;
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static void
|
|
fsmdef_sm_ignore_ftr (fsm_fcb_t *fcb, int fname, cc_features_t ftr_id)
|
|
{
|
|
fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
if (fcb->dcb) {
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
static void
|
|
fsmdef_sm_ignore_src (fsm_fcb_t *fcb, int fname, cc_srcs_t src_id)
|
|
{
|
|
fsm_sm_ignore_src(fcb, __LINE__, src_id);
|
|
|
|
if (fcb->dcb) {
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* fsmdef_error_onhook_timeout
|
|
*
|
|
* Timer is started immediately after the call error. This function is called
|
|
* when there is a timeout event generated by the timer .
|
|
*
|
|
* @param[in] data The gsm ID (callid_t) of the call onhook timeout
|
|
* has occured.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void
|
|
fsmdef_error_onhook_timeout (void *data)
|
|
{
|
|
static const char fname[] = "fsmdef_error_onhook_timeout";
|
|
fsmdef_dcb_t *dcb;
|
|
callid_t call_id;
|
|
|
|
call_id = (callid_t)(long)data;
|
|
if (call_id == CC_NO_CALL_ID) {
|
|
/* Invalid call id */
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "invalid data");
|
|
return;
|
|
}
|
|
|
|
/* Retrieve dcb from call id */
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
if (dcb == NULL) {
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
|
|
|
|
return;
|
|
}
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, fname, "timeout");
|
|
|
|
cc_int_onhook(CC_SRC_GSM, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE,
|
|
dcb->call_id, dcb->line, FALSE, FALSE);
|
|
}
|
|
|
|
/**
|
|
* Function: fsmdef_feature_timer_timeout
|
|
*
|
|
* Description: This function is called when receives time out
|
|
* notification. The function then converts the timer event
|
|
* into the CCAPI event suitable for GSM's call state machine.
|
|
*
|
|
* Parameters:
|
|
* feature_id - corresponding feature ID of the timer event.
|
|
* data - the opaque data for the caller which is actually
|
|
* is the GSM's call id.
|
|
*
|
|
* Returns:
|
|
* NULL or
|
|
* pointer to the cc_feature_t.
|
|
*/
|
|
void *
|
|
fsmdef_feature_timer_timeout (cc_features_t feature_id, void *data)
|
|
{
|
|
static const char fname[] = "fsmdef_feature_timer_timeout";
|
|
cc_feature_t *pmsg;
|
|
callid_t call_id;
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "timeout");
|
|
|
|
call_id = (callid_t)(long)data;
|
|
if (call_id == CC_NO_CALL_ID) {
|
|
/* Invalid call id */
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "invalid data");
|
|
return NULL;
|
|
}
|
|
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
if (dcb == NULL) {
|
|
/* The corresponding dcb for the call ID is not found */
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB), fname);
|
|
return (NULL);
|
|
}
|
|
|
|
if (dcb->inband_received && feature_id == CC_FEATURE_RINGBACK_DELAY_TIMER_EXP) {
|
|
/* Double check if inbound ringback indication is received*/
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "inband received!");
|
|
return (NULL);
|
|
}
|
|
|
|
pmsg = (cc_feature_t *) gsm_get_buffer(sizeof(*pmsg));
|
|
if (!pmsg) {
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1),
|
|
call_id, dcb->line, fname,
|
|
"failed to allocate feature timer message");
|
|
return NULL;
|
|
}
|
|
|
|
memset(pmsg, 0, sizeof(*pmsg));
|
|
|
|
pmsg->msg_id = CC_MSG_FEATURE;
|
|
pmsg->src_id = CC_SRC_GSM;
|
|
pmsg->call_id = call_id;
|
|
pmsg->line = dcb->line;
|
|
pmsg->feature_id = feature_id;
|
|
pmsg->data_valid = FALSE;
|
|
|
|
return (void *) pmsg;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Function handles idle setup request received from the network
|
|
*
|
|
* @sm_eent_t event
|
|
*
|
|
* @return SM_RC_END
|
|
*
|
|
* @pre (called_number and called_number not NULL)
|
|
* @pre (event->data not_eq NULL)
|
|
* @pre (event->msg not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_idle_setup (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_idle_setup";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_setup_t *msg = (cc_setup_t *) event->msg;
|
|
callid_t call_id = msg->call_id;
|
|
int temp;
|
|
string_t called_number = msg->caller_id.called_number;
|
|
string_t calling_number = msg->caller_id.calling_number;
|
|
fsmdef_dcb_t *dcb;
|
|
cc_causes_t cause;
|
|
fsmxfr_xcb_t *xcb;
|
|
fsm_fcb_t *other_fcb;
|
|
callid_t other_call_id;
|
|
boolean alerting = TRUE;
|
|
boolean replaces = msg->replaces;
|
|
int other_active_calls;
|
|
boolean transfer_target = FALSE;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Make sure we have a valid called_number.
|
|
*/
|
|
if ((called_number == NULL) || (called_number[0] == '\0')) {
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
/*
|
|
* Check the called/calling number for the E.164 escaped "+"
|
|
* and convert it to a real "+" if present.
|
|
*/
|
|
if (cpr_strncasecmp(called_number, "%2B", 3) == 0) {
|
|
fsmdef_convert_esc_plus(called_number);
|
|
}
|
|
if (cpr_strncasecmp(calling_number, "%2B", 3) == 0) {
|
|
fsmdef_convert_esc_plus(calling_number);
|
|
}
|
|
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"called_number= %s calling_number= %s\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, msg->call_id, fname),
|
|
msg->caller_id.called_number, msg->caller_id.calling_number);
|
|
|
|
//idle = lsm_is_phone_idle();
|
|
|
|
xcb = fsmxfr_get_xcb_by_call_id(call_id);
|
|
if (xcb && replaces) {
|
|
transfer_target = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Get a new incoming call context.
|
|
* if we the target of a transfer, request that the line
|
|
* availability be increased by one to account for the third
|
|
* instance of a line to become available to allow completion
|
|
* of transfer.
|
|
*/
|
|
cause = fsm_get_new_incoming_call_context(call_id, fcb, called_number,
|
|
transfer_target);
|
|
dcb = fcb->dcb;
|
|
if ((msg->call_info.type != CC_FEAT_MONITOR) &&
|
|
(replaces != TRUE)) {
|
|
if (lsm_is_line_available(dcb->line, TRUE) == FALSE) {
|
|
/* increment it to compensate for decrementing while ending the call. */
|
|
lsm_increment_call_chn_cnt(dcb->line);
|
|
fsmdef_end_call(dcb, CC_CAUSE_BUSY);
|
|
return (SM_RC_END);
|
|
}
|
|
lsm_increment_call_chn_cnt(dcb->line);
|
|
}
|
|
else {
|
|
/*
|
|
* join calls (barged, M & R) are not counted by CUCM. so we should not count either
|
|
*/
|
|
dcb->call_not_counted_in_mnc_bt = TRUE;
|
|
dcb->join_call_id = msg->call_info.data.join.join_call_id;
|
|
}
|
|
/*
|
|
* Set default orientation for the incoming setup as "from" to
|
|
* avoid updating UI when call info is received in "ACK" which
|
|
* shoule be "from" for typicall incoming call.
|
|
*/
|
|
dcb->orientation = CC_ORIENTATION_FROM;
|
|
|
|
switch (cause) {
|
|
case CC_CAUSE_OK:
|
|
break;
|
|
|
|
case CC_CAUSE_NO_RESOURCE:
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (SM_RC_CLEANUP);
|
|
|
|
default:
|
|
fsmdef_end_call(dcb, cause);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* Check for Anonymous call blocking. If low bit is set,
|
|
* then do not allow call. Note that we must allow both upper and lowercase
|
|
* ANON strings, hence the use of strcasestr
|
|
*/
|
|
config_get_value(CFGID_ANONYMOUS_CALL_BLOCK, &temp, sizeof(temp));
|
|
if (temp & 1) {
|
|
/*
|
|
* We compare the calling name to the hardcoded Anonymous string we use in
|
|
* our SIP headers. This handles the case where calling name was pulled from
|
|
* the From header and was set to Anonymous. We also compare the calling name
|
|
* to the localized string index for Private which is what the calling name will
|
|
* be set to if an RPID header was received.
|
|
*/
|
|
char tmp_str[STATUS_LINE_MAX_LEN];
|
|
sstrncpy(tmp_str, platform_get_phrase_index_str(UI_PRIVATE), sizeof(tmp_str));
|
|
if (strcasestr(msg->caller_id.calling_name, SIP_HEADER_ANONYMOUS_STR) ||
|
|
strcasestr(msg->caller_id.calling_name, tmp_str)) {
|
|
fsmdef_end_call(dcb, CC_CAUSE_ANONYMOUS);
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Check if Call Waiting is disabled.
|
|
* If call-waiting is disabled and there is another call active
|
|
* then the GSM will return busy.
|
|
* unless this is a barge/monitor target call
|
|
*
|
|
*/
|
|
config_get_line_value(CFGID_LINE_CALL_WAITING, &temp, sizeof(temp),
|
|
dcb->line);
|
|
other_active_calls = fsmdef_get_active_call_cnt(call_id);
|
|
|
|
if ((msg->call_info.type != CC_FEAT_MONITOR)) {
|
|
if ((!(temp & 1)) && (other_active_calls > 0) &&
|
|
(!((xcb != NULL) && (xcb->mode == FSMXFR_MODE_TARGET)))) {
|
|
|
|
fsmdef_end_call(dcb, CC_CAUSE_BUSY);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If this is a call to replace another call,
|
|
* check that we have a call to replace
|
|
*/
|
|
other_call_id = fsmxfr_get_other_call_id(xcb, call_id);
|
|
if (replaces) {
|
|
if ((xcb == NULL) || (other_call_id == CC_NO_CALL_ID)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, "",
|
|
"No call to replace");
|
|
|
|
fsmdef_end_call(dcb, CC_CAUSE_NO_REPLACE_CALL);
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* The called name and number will not be obtained from
|
|
* the setup. Remove it before getting from the setup message.
|
|
*/
|
|
if (msg->caller_id.called_name != NULL) {
|
|
strlib_free(msg->caller_id.called_name);
|
|
msg->caller_id.called_name = NULL;
|
|
}
|
|
if (msg->caller_id.called_number != NULL) {
|
|
strlib_free(msg->caller_id.called_number);
|
|
msg->caller_id.called_number = NULL;
|
|
}
|
|
/* Get the caller ID from the setup message */
|
|
fsmdef_mv_caller_id(dcb, &msg->caller_id);
|
|
|
|
if (msg->caller_id.call_type == CC_CALL_FORWARDED) {
|
|
|
|
dcb->call_type = FSMDEF_CALL_TYPE_FORWARD;
|
|
}
|
|
|
|
/*
|
|
* If a call instance id is provided, use it in the dcb. We will
|
|
* check to see that the call instance id provided differs from
|
|
* what is currently stored in the dcb before assigning it.
|
|
*/
|
|
if (msg->call_info.type == CC_FEAT_CALLINFO) {
|
|
cc_feature_data_call_info_t *data;
|
|
|
|
data = &msg->call_info.data.call_info_feat_data;
|
|
if (data->feature_flag & CC_CALL_INSTANCE) {
|
|
if (data->caller_id.call_instance_id != 0 &&
|
|
data->caller_id.call_instance_id !=
|
|
dcb->caller_id.call_instance_id) {
|
|
if (dcb->caller_id.call_instance_id != 0) {
|
|
fsmutil_free_ci_id(dcb->caller_id.call_instance_id,
|
|
dcb->line);
|
|
}
|
|
dcb->caller_id.call_instance_id =
|
|
data->caller_id.call_instance_id;
|
|
fsmutil_set_ci_id(dcb->caller_id.call_instance_id, dcb->line);
|
|
}
|
|
}
|
|
|
|
if (data->feature_flag & CC_SECURITY) {
|
|
FSM_SET_SECURITY_STATUS(dcb, data->security);
|
|
}
|
|
|
|
if (data->feature_flag & CC_POLICY) {
|
|
FSM_SET_POLICY(dcb, data->policy);
|
|
}
|
|
}
|
|
dcb->alert_info = msg->alert_info;
|
|
dcb->alerting_ring = msg->alerting_ring;
|
|
dcb->alerting_tone = msg->alerting_tone;
|
|
|
|
/*
|
|
* This is an incoming call, so we know we will need to send a
|
|
* RELEASE when we clear the call.
|
|
*/
|
|
dcb->send_release = TRUE;
|
|
|
|
cause = gsmsdp_negotiate_offer_sdp(fcb, &msg->msg_body, TRUE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
if (transfer_target) {
|
|
/*
|
|
* Send a proceeding event to the transfer call.
|
|
*
|
|
* The proceeding event will end the other call
|
|
* and answer the current call.
|
|
*
|
|
*/
|
|
cc_int_proceeding(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, &(dcb->caller_id));
|
|
}
|
|
|
|
cc_int_setup_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), NULL);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_SETUP_ACK);
|
|
|
|
|
|
|
|
cc_int_proceeding(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id));
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_PROCEEDING);
|
|
|
|
|
|
alerting = fsmdef_extract_join_target(event);
|
|
|
|
/*
|
|
* This might be the transfer call from the transferee to the target.
|
|
* In such a case we want this new call to match the state of the call
|
|
* that it is replacing.
|
|
*/
|
|
if (xcb != NULL) {
|
|
other_fcb = fsm_get_fcb_by_call_id_and_type(other_call_id,
|
|
FSM_TYPE_DEF);
|
|
if (other_fcb && (other_fcb->old_state == FSMDEF_S_CONNECTED ||
|
|
other_fcb->old_state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
|
|
other_fcb->old_state == FSMDEF_S_RESUME_PENDING ||
|
|
other_fcb->state == FSMDEF_S_CONNECTED ||
|
|
other_fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND ||
|
|
other_fcb->state == FSMDEF_S_RESUME_PENDING)) {
|
|
alerting = FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
if (alerting == TRUE) {
|
|
/*
|
|
* set the call priority in lcb. This is used by ringer logic.
|
|
*/
|
|
if ((msg->call_info.type == CC_FEAT_CALLINFO) &&
|
|
(msg->call_info.data.call_info_feat_data.priority == CC_CALL_PRIORITY_URGENT)) {
|
|
lsm_set_lcb_call_priority(call_id);
|
|
}
|
|
if ((msg->call_info.type == CC_FEAT_CALLINFO) &&
|
|
(msg->call_info.data.call_info_feat_data.dusting == TRUE)) {
|
|
lsm_set_lcb_dusting_call(call_id);
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ALERTING,
|
|
FSMDEF_CC_CALLER_ID);
|
|
/*
|
|
* Currently we do not send SDP in the 180 response.
|
|
*/
|
|
cc_int_alerting(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), NULL, FALSE);
|
|
|
|
/* update callref */
|
|
if ( dcb->callref == 0 ) {
|
|
dcb->callref = msg->call_info.data.call_info_feat_data.callref;
|
|
ui_update_callref(dcb->line, dcb->call_id, msg->call_info.data.call_info_feat_data.callref);
|
|
}
|
|
/* update gcid */
|
|
ui_update_gcid(dcb->line, dcb->call_id, msg->call_info.data.call_info_feat_data.global_call_id);
|
|
/*
|
|
* store the gcid lcb, this is used to prevent short ringing
|
|
* in scenarios where a call is routed to the calling phone.
|
|
*/
|
|
lsm_update_gcid(dcb->call_id, msg->call_info.data.call_info_feat_data.global_call_id);
|
|
}
|
|
|
|
ui_cc_capability(dcb->line, lsm_get_ui_id(dcb->call_id), msg->recv_info_list);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_ALERTING);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_INCOMING_ALERTING);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
sm_rcs_t
|
|
fsmdef_dialstring (fsm_fcb_t *fcb, const char *dialstring,
|
|
cc_redirect_t *redirect, boolean replace,
|
|
cc_call_info_t *call_info)
|
|
{
|
|
static const char fname[] = "fsmdef_dialstring";
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_causes_t cause;
|
|
cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (dialstring) {
|
|
if (strlen(dialstring) > MAX_SIP_URL_LENGTH) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Dial string too long\n", DEB_F_PREFIX_ARGS(FSM, fname));
|
|
/* Force clean up call without sending release */
|
|
return (fsmdef_release(fcb, CC_CAUSE_INVALID_NUMBER, FALSE));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If there is active feature which is waiting for digit collection
|
|
* then use service URI preceded with dialed number.
|
|
*/
|
|
switch (dcb->active_feature) {
|
|
|
|
case CC_FEATURE_CFWD_ALL:
|
|
fsmdef_append_dialstring_to_feature_uri(dcb, dialstring);
|
|
break;
|
|
|
|
default:
|
|
if (dialstring) {
|
|
dcb->caller_id.called_number =
|
|
strlib_update(dcb->caller_id.called_number, dialstring);
|
|
}
|
|
break;
|
|
}
|
|
|
|
cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
/* Force clean up call without sending release */
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
/* Build SDP for sending out */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
/* Force clean up call without sending release */
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
/*
|
|
* Since we are sending setup to UI we will also have to send
|
|
* release to it to for sip stack to clean up the call
|
|
*/
|
|
dcb->send_release = TRUE;
|
|
|
|
/*
|
|
* lsm_parse_displaystr will free present called number and return
|
|
* pointer to parsed called number
|
|
*/
|
|
dcb->caller_id.called_number =
|
|
lsm_parse_displaystr(dcb->caller_id.called_number);
|
|
|
|
/* set default orientation for outgoing call */
|
|
dcb->orientation = CC_ORIENTATION_TO;
|
|
dcb->inbound = FALSE;
|
|
|
|
/*
|
|
* Invoke cc_call_state with modified caller_id for features such as
|
|
* pickups
|
|
*/
|
|
fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_DIALING_COMPLETED, CC_CAUSE_MIN);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_SETUP);
|
|
|
|
fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_SENT, CC_CAUSE_MIN);
|
|
|
|
/*
|
|
* Send setup or INVITE out after finishing all UI activities and media
|
|
* preparation. This is done as the final step in order to minimize
|
|
* UI activities/media activities to run concurrently while processing
|
|
* the response from the network. On the platform that uses Java VM to
|
|
* support UI and media, the time to process the UI and media activities
|
|
* may take longer than the network response time to the INVITE (such as
|
|
* voice mail case) and JNI calls may be blocked while invoking the
|
|
* UI or media calls in the LSM.
|
|
*/
|
|
cc_int_setup(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), dcb->alert_info, VCM_INSIDE_RING,
|
|
VCM_INSIDE_DIAL_TONE, redirect, call_info, replace, NULL, &msg_body);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CALL_SENT);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_dialstring (sm_event_t *event)
|
|
{
|
|
sm_rcs_t sm_rc;
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
// handle dialstring event if from callfwdall
|
|
if (fsmdef_process_dialstring_for_callfwd(event) == SM_RC_END) {
|
|
// release the call started to collect callfwd info
|
|
dcb->send_release = FALSE;
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
|
|
}
|
|
|
|
sm_rc = fsmdef_dialstring(fcb, ((cc_dialstring_t *)event->msg)->dialstring,
|
|
NULL, FALSE, NULL);
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
|
|
/**
|
|
* fsmdef_ev_createoffer
|
|
*
|
|
* Generates Offer SDP
|
|
*
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_createoffer (sm_event_t *event) {
|
|
// sm_rcs_t sm_rc;
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_msgbody_info_t msg_body;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
line_t line = msg->line;
|
|
callid_t call_id = msg->call_id;
|
|
// cc_causes_t lsm_rc;
|
|
int sdpmode = 0;
|
|
char *ufrag = NULL;
|
|
char *ice_pwd = NULL;
|
|
short vcm_res;
|
|
session_data_t *sess_data_p = NULL;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
/* Force clean up call without sending release */
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
if (msg->data.session.has_constraints) {
|
|
sess_data_p = (session_data_t *)findhash(msg->data.session.sessionid);
|
|
if (sess_data_p) {
|
|
gsmsdp_process_cap_constraints(dcb, sess_data_p->cc_constraints);
|
|
|
|
if (0 > delhash(msg->data.session.sessionid)) {
|
|
FSM_DEBUG_SM (DEB_F_PREFIX"failed to delete hash sessid=0x%08x\n",
|
|
DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__), msg->data.session.sessionid);
|
|
}
|
|
cpr_free(sess_data_p);
|
|
}
|
|
}
|
|
|
|
vcmGetIceParams(dcb->peerconnection, &ufrag, &ice_pwd);
|
|
if (!ufrag || !ice_pwd) {
|
|
ui_create_offer(evCreateOfferError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
dcb->ice_ufrag = (char *)cpr_malloc(strlen(ufrag) + 1);
|
|
if (!dcb->ice_ufrag)
|
|
return SM_RC_END;
|
|
|
|
sstrncpy(dcb->ice_ufrag, ufrag, strlen(ufrag) + 1);
|
|
free(ufrag);
|
|
|
|
|
|
dcb->ice_pwd = (char *)cpr_malloc(strlen(ice_pwd) + 1);
|
|
if (!dcb->ice_pwd)
|
|
return SM_RC_END;
|
|
|
|
sstrncpy(dcb->ice_pwd, ice_pwd, strlen(ice_pwd) + 1);
|
|
free(ice_pwd);
|
|
|
|
vcm_res = vcmGetDtlsIdentity(dcb->peerconnection,
|
|
dcb->digest_alg, FSMDEF_MAX_DIGEST_ALG_LEN,
|
|
dcb->digest, FSMDEF_MAX_DIGEST_LEN);
|
|
|
|
if (vcm_res) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetDtlsIdentity returned an error\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_END;
|
|
}
|
|
|
|
cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_create_offer(evCreateOfferError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_create_offer(evCreateOfferError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
/* Pass offer SDP back to UI */
|
|
ui_create_offer(evCreateOffer, line, call_id, dcb->caller_id.call_instance_id, msg_body.parts[0].body);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
/**
|
|
* fsmdef_ev_createanswer
|
|
*
|
|
* Generates Answer SDP
|
|
*
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_createanswer (sm_event_t *event) {
|
|
// sm_rcs_t sm_rc;
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_msgbody_info_t msg_body;
|
|
line_t line = msg->line;
|
|
callid_t call_id = msg->call_id;
|
|
// line_t free_line;
|
|
int sdpmode = 0;
|
|
// const char *called_number = "1234";
|
|
// cc_causes_t lsm_rc;
|
|
// cc_msgbody_t *part;
|
|
// uint32_t body_length;
|
|
char *ufrag = NULL;
|
|
char *ice_pwd = NULL;
|
|
short vcm_res;
|
|
session_data_t *sess_data_p;
|
|
boolean has_audio;
|
|
boolean has_video;
|
|
boolean has_data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
if (msg->data.session.has_constraints) {
|
|
sess_data_p = (session_data_t *)findhash(msg->data.session.sessionid);
|
|
if (sess_data_p) {
|
|
gsmsdp_process_cap_constraints(dcb, sess_data_p->cc_constraints);
|
|
|
|
if (0 > delhash(msg->data.session.sessionid)) {
|
|
FSM_DEBUG_SM (DEB_F_PREFIX"failed to delete hash sessid=0x%08x\n",
|
|
DEB_F_PREFIX_ARGS(SIP_CC_PROV, __FUNCTION__), msg->data.session.sessionid);
|
|
}
|
|
cpr_free(sess_data_p);
|
|
}
|
|
}
|
|
|
|
vcmGetIceParams(dcb->peerconnection, &ufrag, &ice_pwd);
|
|
if (!ufrag || !ice_pwd) {
|
|
ui_create_offer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
dcb->ice_ufrag = (char *)cpr_malloc(strlen(ufrag) + 1);
|
|
if (!dcb->ice_ufrag)
|
|
return SM_RC_END;
|
|
|
|
sstrncpy(dcb->ice_ufrag, ufrag, strlen(ufrag) + 1);
|
|
free(ufrag);
|
|
|
|
|
|
dcb->ice_pwd = (char *)cpr_malloc(strlen(ice_pwd) + 1);
|
|
if (!dcb->ice_pwd)
|
|
return SM_RC_END;
|
|
|
|
sstrncpy(dcb->ice_pwd, ice_pwd, strlen(ice_pwd) + 1);
|
|
free(ice_pwd);
|
|
|
|
vcm_res = vcmGetDtlsIdentity(dcb->peerconnection,
|
|
dcb->digest_alg, FSMDEF_MAX_DIGEST_ALG_LEN,
|
|
dcb->digest, FSMDEF_MAX_DIGEST_LEN);
|
|
|
|
if (vcm_res) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"vcmGetDtlsIdentity returned an error\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_END;
|
|
}
|
|
|
|
/*
|
|
* Determine what media types are offered, used to create matching local SDP
|
|
* for negotiation.
|
|
*/
|
|
gsmsdp_get_offered_media_types(fcb, dcb->sdp, &has_audio, &has_video, &has_data);
|
|
|
|
/*
|
|
* The sdp member of the dcb has local and remote sdp
|
|
* this next function fills in the local part
|
|
*/
|
|
cause = gsmsdp_create_local_sdp(dcb, FALSE, has_audio, has_video, has_data, FALSE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_create_answer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
// Force clean up call without sending release
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
/* TODO(ekr@rtfm.com): The second true is because we are acting as if we are
|
|
processing an offer. The first, however, is for an initial offer and we may
|
|
want to set that conditionally. */
|
|
cause = gsmsdp_negotiate_media_lines(fcb, dcb->sdp, TRUE, TRUE, FALSE, TRUE);
|
|
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_create_answer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_create_answer(evCreateAnswerError, line, call_id, dcb->caller_id.call_instance_id, NULL);
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
/* Pass SDP back to UI */
|
|
ui_create_answer(evCreateAnswer, line, call_id, dcb->caller_id.call_instance_id, msg_body.parts[0].body);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
/**
|
|
* SetLocalDescription
|
|
*
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_setlocaldesc(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_msgbody_info_t msg_body;
|
|
int action = msg->action;
|
|
// string_t sdp = msg->sdp;
|
|
int sdpmode = 0;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
// cc_causes_t lsm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SETLOCALDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
if (JSEP_OFFER == action) {
|
|
cause = gsmsdp_encode_sdp(dcb->sdp, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SETLOCALDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/* compare and fail if different:
|
|
* anant: Why? The JS should be able to modify the SDP. Commenting out for now (same for answer)
|
|
if (strcmp(msg_body.parts[0].body, msg->sdp) != 0) {
|
|
ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SDPCHANGED);
|
|
return (SM_RC_END);
|
|
}
|
|
*/
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CALL_SENT);
|
|
|
|
} else if (JSEP_ANSWER == action) {
|
|
|
|
/* compare SDP generated from CreateAnswer */
|
|
cause = gsmsdp_encode_sdp(dcb->sdp, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SETLOCALDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/* compare and fail if different
|
|
if (strcmp(msg_body.parts[0].body, msg->sdp) != 0) {
|
|
ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SDPCHANGED);
|
|
return (SM_RC_END);
|
|
}*/
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
|
|
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ANSWERED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTING);
|
|
|
|
/*
|
|
* Now that we have negotiated the media, time to set up ICE.
|
|
* There also needs to be an ICE check in negotiate_media_lines.
|
|
*/
|
|
cause = gsmsdp_install_peer_ice_attributes(fcb);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_set_local_description(evSetLocalDescError, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_SDPCHANGED);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/* taken from fsmdef_ev_connected_ack start rx and tx */
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
/*
|
|
* If DSP is not able to start rx/tx channels, release the call
|
|
*/
|
|
if (dcb->dsp_out_of_resources == TRUE) {
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN, NULL);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/* we may want to use the functionality in the following method
|
|
* to handle media capability changes, needs discussion
|
|
* fsmdef_transition_to_connected(fcb);
|
|
*/
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
|
|
|
|
}
|
|
|
|
ui_set_local_description(evSetLocalDesc, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_OK);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
/**
|
|
* SetRemoteDescription
|
|
*
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_setremotedesc(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
int action = msg->action;
|
|
int sdpmode = 0;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
// cc_causes_t lsm_rc;
|
|
cc_msgbody_t *part;
|
|
uint32_t body_length;
|
|
cc_msgbody_info_t msg_body;
|
|
boolean has_audio;
|
|
boolean has_video;
|
|
boolean has_data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
ui_set_remote_description(evSetRemoteDescError, line, call_id,
|
|
dcb->caller_id.call_instance_id, NULL, PC_SETREMOTEDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
cc_initialize_msg_body_parts_info(&msg_body);
|
|
|
|
msg_body.num_parts = 1;
|
|
msg_body.content_type = cc_content_type_SDP;
|
|
part = &msg_body.parts[0];
|
|
body_length = strlen(msg->sdp);
|
|
part->body = msg->sdp;
|
|
part->body_length = body_length;
|
|
part->content_type = cc_content_type_SDP;
|
|
part->content_disposition.required_handling = FALSE;
|
|
part->content_disposition.disposition = cc_disposition_session;
|
|
part->content_id = NULL;
|
|
|
|
if (JSEP_OFFER == action) {
|
|
|
|
cause = gsmsdp_process_offer_sdp(fcb, &msg_body, TRUE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_set_remote_description(evSetRemoteDescError, line, call_id,
|
|
dcb->caller_id.call_instance_id, NULL, PC_SETREMOTEDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* Determine what media types are offered, used to create matching local SDP
|
|
* for negotiation.
|
|
*/
|
|
gsmsdp_get_offered_media_types(fcb, dcb->sdp, &has_audio, &has_video, &has_data);
|
|
|
|
/*
|
|
* The sdp member of the dcb has local and remote sdp
|
|
* this next function fills in the local part
|
|
*/
|
|
cause = gsmsdp_create_local_sdp(dcb, TRUE, has_audio, has_video, has_data, FALSE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
|
|
NULL, PC_SETREMOTEDESCERROR);
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
// Force clean up call without sending release
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
cause = gsmsdp_negotiate_media_lines(fcb, dcb->sdp, TRUE, TRUE, TRUE, FALSE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
|
|
NULL, PC_SETREMOTEDESCERROR);
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
gsmsdp_clean_media_list(dcb);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_INCOMING_ALERTING);
|
|
|
|
} else if (JSEP_ANSWER == action) {
|
|
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
|
|
NULL, PC_SETREMOTEDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* Now that we have negotiated the media, time to set up ICE.
|
|
* There also needs to be an ICE check in negotiate_media_lines.
|
|
*/
|
|
cause = gsmsdp_install_peer_ice_attributes(fcb);
|
|
if (cause != CC_CAUSE_OK) {
|
|
ui_set_remote_description(evSetRemoteDescError, line, call_id, dcb->caller_id.call_instance_id,
|
|
NULL, PC_SETREMOTEDESCERROR);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED, FSMDEF_CC_CALLER_ID);
|
|
|
|
/* we may want to use the functionality in the following method
|
|
* to handle media capability changes, needs discussion
|
|
* fsmdef_transition_to_connected(fcb);
|
|
* fsmdef_transition_to_connected(fcb);
|
|
*/
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
|
|
}
|
|
|
|
ui_set_remote_description(evSetRemoteDesc, line, call_id, dcb->caller_id.call_instance_id, NULL, PC_OK);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_localdesc(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
// cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
int sdpmode = 0;
|
|
// cc_causes_t lsm_rc;
|
|
// cc_msgbody_t *part;
|
|
// uint32_t body_length;
|
|
// cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_remotedesc(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
// cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
int sdpmode = 0;
|
|
// cc_causes_t lsm_rc;
|
|
// cc_msgbody_t *part;
|
|
// uint32_t body_length;
|
|
// cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_setpeerconnection(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
// cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
callid_t call_id = msg->call_id;
|
|
int sdpmode = 0;
|
|
line_t line = msg->line;
|
|
cc_causes_t lsm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (!sdpmode) {
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (!msg)
|
|
return SM_RC_END;
|
|
|
|
if (!msg->data_valid)
|
|
return SM_RC_END;
|
|
|
|
if (dcb == NULL) {
|
|
dcb = fsmdef_get_new_dcb(call_id);
|
|
if (dcb == NULL) {
|
|
return SM_RC_ERROR;
|
|
}
|
|
|
|
lsm_rc = lsm_get_facility_by_line(call_id, line, FALSE, dcb);
|
|
if (lsm_rc != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"lsm_get_facility_by_line failed.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_END;
|
|
}
|
|
|
|
fsmdef_init_dcb(dcb, call_id, FSMDEF_CALL_TYPE_NONE, NULL, line, fcb);
|
|
|
|
fsm_set_fcb_dcbs(dcb);
|
|
}
|
|
|
|
PR_ASSERT(strlen(msg->data.pc.pc_handle) < PC_HANDLE_SIZE);
|
|
sstrncpy(dcb->peerconnection, msg->data.pc.pc_handle, sizeof(dcb->peerconnection));
|
|
dcb->peerconnection_set = TRUE;
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_addstream(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
// cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
int sdpmode = 0;
|
|
// cc_causes_t lsm_rc;
|
|
// cc_msgbody_t *part;
|
|
// uint32_t body_length;
|
|
// cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (sdpmode == FALSE) {
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* This is temporary code to allow configuration of the two
|
|
* default streams. When multiple streams > 2 are supported this
|
|
* will be re-implemented.
|
|
*/
|
|
if (msg->data.track.media_type == VIDEO) {
|
|
dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
|
|
dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_SENDRECV;
|
|
dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_stream = msg->data.track.stream_id;
|
|
dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_track = msg->data.track.track_id;
|
|
} else if (msg->data.track.media_type == AUDIO) {
|
|
dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
|
|
dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_SENDRECV;
|
|
dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_stream = msg->data.track.stream_id;
|
|
dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_track = msg->data.track.track_id;
|
|
} else {
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_removestream(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
// cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
int sdpmode = 0;
|
|
// cc_causes_t lsm_rc;
|
|
// cc_msgbody_t *part;
|
|
// uint32_t body_length;
|
|
// cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (sdpmode == FALSE) {
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
/*
|
|
* This is temporary code to allow configuration of the two
|
|
* default streams. When multiple streams > 2 are supported this
|
|
* will be re-implemented.
|
|
*/
|
|
if (msg->data.track.media_type == AUDIO) {
|
|
dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
|
|
dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_RECVONLY;
|
|
dcb->video_pref = SDP_DIRECTION_SENDRECV;
|
|
} else if (msg->data.track.media_type == VIDEO) {
|
|
dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
|
|
dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_RECVONLY;
|
|
} else {
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_addcandidate(sm_event_t *event) {
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
// cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
int sdpmode = 0;
|
|
short vcm_res;
|
|
// uint16_t level;
|
|
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
if (sdpmode == FALSE) {
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"dcb is NULL.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
return SM_RC_CLEANUP;
|
|
}
|
|
|
|
|
|
/* Perform level lookup based on mid value */
|
|
/* comment until mid is properly updated
|
|
cause = gsmsdp_find_level_from_mid(dcb, (const char *)msg->data.candidate.mid, &level);
|
|
*/
|
|
|
|
vcm_res = vcmSetIceCandidate(dcb->peerconnection, (char *)msg->data.candidate.candidate, msg->data.candidate.level);
|
|
if(vcm_res) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"failure setting ice candidate.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
}
|
|
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static void
|
|
fsmdef_check_active_feature (fsmdef_dcb_t *dcb, cc_features_t ftr_id)
|
|
{
|
|
if ((dcb) && (dcb->active_feature != ftr_id)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_FTR_REQ_ACT),
|
|
dcb->call_id, dcb->line,
|
|
cc_feature_name(ftr_id),
|
|
cc_feature_name(dcb->active_feature));
|
|
lsm_ui_display_notify(INDEX_STR_KEY_NOT_ACTIVE, NO_FREE_LINES_TIMEOUT);
|
|
}
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_idle_feature (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_idle_feature";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
line_t line = msg->line;
|
|
cc_causes_t cause = CC_CAUSE_NORMAL;
|
|
callid_t call_id = fcb->call_id;
|
|
boolean expline;
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
fsmcnf_ccb_t *ccb;
|
|
fsmxfr_xcb_t *xcb;
|
|
char *global_call_id = NULL;
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
switch (src_id) {
|
|
case CC_SRC_UI:
|
|
case CC_SRC_GSM:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
if (dcb) {
|
|
dcb->video_pref = data->caps.support_direction;
|
|
}
|
|
break;
|
|
case CC_FEATURE_CFWD_ALL:
|
|
if (fsmdef_is_feature_uri_configured(ftr_id) == FALSE) {
|
|
fsm_display_feature_unavailable();
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
|
|
// handle cfwd event for ccm and non-ccm cases
|
|
// process feature event only if no other active feature
|
|
if ((dcb->active_feature == CC_FEATURE_NONE) &&
|
|
(fsmdef_get_connected_call() == NULL)) {
|
|
dcb->active_feature = ftr_id;
|
|
(void) fsmdef_process_cfwd_softkey_event(event);
|
|
} else {
|
|
fsmdef_check_active_feature(dcb, ftr_id);
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
}
|
|
break;
|
|
case CC_FEATURE_NEW_CALL:
|
|
|
|
/* fetch the global_call_id from feature data */
|
|
global_call_id = data->newcall.global_call_id;
|
|
|
|
/*
|
|
* Set the expanded parameter. This parameter is used when
|
|
* requesting a free line. A transfer can request that the line
|
|
* availability be increased by one to account for the third
|
|
* instance of a line to become available for transfers.
|
|
*/
|
|
if (data != NULL) {
|
|
ccb = fsmcnf_get_ccb_by_call_id(call_id);
|
|
xcb = fsmxfr_get_xcb_by_call_id(call_id);
|
|
if ((ccb != NULL) || (xcb != NULL)) {
|
|
expline = TRUE;
|
|
} else {
|
|
expline = FALSE;
|
|
}
|
|
} else {
|
|
expline = FALSE;
|
|
}
|
|
|
|
/*
|
|
* Get a new outgoing call context if we have not already
|
|
* grabbed one.
|
|
*/
|
|
if (fcb->dcb == NULL) {
|
|
cause = fsm_get_new_outgoing_call_context(call_id, line, fcb,
|
|
expline);
|
|
switch (cause) {
|
|
case CC_CAUSE_OK:
|
|
break;
|
|
|
|
case CC_CAUSE_NO_RESOURCE:
|
|
GSM_ERR_MSG("%s No Resource! Return SM_RC_CLEANUP.", fname);
|
|
return (SM_RC_CLEANUP);
|
|
|
|
default:
|
|
/*
|
|
* No free lines.
|
|
*/
|
|
fsm_display_no_free_lines();
|
|
|
|
/*
|
|
* Send an endcall message. This behaviour is different
|
|
* than an offhook or line event because the new_call
|
|
* feature may have been generated by a transfer (as the
|
|
* consultation call) and will need this end_call message
|
|
* so that it can cleanup the transfer. The offhook can
|
|
* only come from the UI so it can just be cleaned up here
|
|
* without regard for a transfer.
|
|
*/
|
|
fsmdef_end_call(fcb->dcb, cause);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
dcb = fcb->dcb;
|
|
/*
|
|
* Let Dialog Manager know that there is OFFHOOK event
|
|
*/
|
|
fsmdef_notify_hook_event(fcb, CC_MSG_OFFHOOK, global_call_id,
|
|
data->newcall.prim_call_id,
|
|
data->newcall.hold_resume_reason,
|
|
CC_MONITOR_NONE,CFWDALL_NONE);
|
|
}
|
|
|
|
|
|
/*
|
|
* The user is attempting to start a new call on a specific line:
|
|
* 1. need to place the connected call (if there is one) on hold,
|
|
* 2. clear any outgoing ringing calls,
|
|
* 3. initiate this call.
|
|
*/
|
|
if (fsmdef_wait_to_start_new_call(TRUE, CC_SRC_GSM, call_id, line,
|
|
ftr_id, data)) {
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
//lsm_set_active_call_id(call_id);
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
if ( data->newcall.cause == CC_CAUSE_CONF ||
|
|
data->newcall.cause == CC_CAUSE_XFER_LOCAL ) {
|
|
/* suppress stutter dial tone for conf and transfer features */
|
|
fsmdef_call_cc_state_dialing(dcb, TRUE);
|
|
} else {
|
|
fsmdef_call_cc_state_dialing(dcb, FALSE);
|
|
}
|
|
|
|
switch (data->newcall.cause) {
|
|
case CC_CAUSE_XFER_REMOTE:
|
|
/*
|
|
* This newcall feature is really the consultation part of
|
|
* a local transfer that has been transferred by the
|
|
* consultation call, so proceed as though this is a
|
|
* dialstring call since we already have the called_number.
|
|
*/
|
|
if (data->newcall.redirect.redirects[0].number[0] != '\0') {
|
|
sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
|
|
&(data->newcall.redirect), FALSE,
|
|
NULL);
|
|
|
|
} else if (data->newcall.redirect.redirects[0].redirect_reason
|
|
== CC_REDIRECT_REASON_DEFLECTION) {
|
|
/*
|
|
* CC_REDIRECT_REASON_DEFLECTION shows that transferee is
|
|
* going to initiate a new call for replacing the call leg
|
|
* between transferor and target.
|
|
*/
|
|
|
|
memset(data->newcall.redirect.redirects[0].number, 0,
|
|
sizeof(CC_MAX_DIALSTRING_LEN));
|
|
sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
|
|
&(data->newcall.redirect), FALSE,
|
|
NULL);
|
|
|
|
} else {
|
|
sm_rc =
|
|
fsmdef_dialstring(fcb, data->newcall.dialstring, NULL,
|
|
FALSE, NULL);
|
|
}
|
|
|
|
return (sm_rc);
|
|
|
|
case CC_CAUSE_REDIRECT:
|
|
sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
|
|
&(data->newcall.redirect), FALSE,
|
|
NULL);
|
|
return (sm_rc);
|
|
|
|
case CC_CAUSE_XFER_BY_REMOTE:
|
|
|
|
/* CC_REDIRECT_REASON_DEFLECTION shows that transferee is
|
|
* going to initiate a new call for replacing the call leg
|
|
* between transferor and target.
|
|
*/
|
|
|
|
memset(data->newcall.redirect.redirects[0].number, 0,
|
|
sizeof(CC_MAX_DIALSTRING_LEN));
|
|
sm_rc = fsmdef_dialstring(fcb, data->newcall.dialstring,
|
|
&(data->newcall.redirect), FALSE,
|
|
NULL);
|
|
return (sm_rc);
|
|
|
|
default:
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_COLLECT_INFO);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
cause = fsmdef_get_cause(msg->data_valid, data);
|
|
|
|
/*
|
|
* There has to be a dcb to process this event.
|
|
*/
|
|
if (fcb->dcb == NULL) {
|
|
// commented out following line due to klocwork error
|
|
// call is dereferencing fcb->dcb when it is NULL (sorry, can't do that!)
|
|
// cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN, NULL);
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
if (dcb->call_type == FSMDEF_CALL_TYPE_INCOMING ||
|
|
dcb->call_type == FSMDEF_CALL_TYPE_FORWARD) {
|
|
dcb->send_release = TRUE;
|
|
}
|
|
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
|
|
} /* switch (src_id) { */
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
sm_rcs_t
|
|
fsmdef_offhook (fsm_fcb_t *fcb, cc_msgs_t msg_id, callid_t call_id,
|
|
line_t line, const char *dial_string,
|
|
sm_event_t *event, char *global_call_id,
|
|
callid_t prim_call_id, cc_hold_resume_reason_e consult_reason,
|
|
monitor_mode_t monitor_mode)
|
|
{
|
|
boolean wait = FALSE;
|
|
boolean wait2 = FALSE;
|
|
boolean wait3 = FALSE;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Get a new outgoing call context if we have not already
|
|
* grabbed one.
|
|
*/
|
|
if (fcb->dcb == NULL) {
|
|
cause = fsm_get_new_outgoing_call_context(call_id, line, fcb, FALSE);
|
|
switch (cause) {
|
|
case CC_CAUSE_OK:
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* No free lines
|
|
*/
|
|
fsm_display_no_free_lines();
|
|
|
|
if (fsmdef_get_connected_call() != NULL) {
|
|
lsm_speaker_mode(ON);
|
|
} else {
|
|
lsm_speaker_mode(OFF);
|
|
}
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
/*
|
|
* Let Dialog Manager know that there is OFFHOOK event
|
|
*/
|
|
fsmdef_notify_hook_event(fcb, CC_MSG_OFFHOOK, global_call_id,
|
|
prim_call_id, consult_reason, monitor_mode,CFWDALL_NONE);
|
|
}
|
|
|
|
|
|
/*
|
|
* The user is attempting to start a new call on a specific line:
|
|
* 1. need to place the connected call (if there is one) on hold,
|
|
* 2. clear any outgoing ringing calls, or calls are in reorder/busy state
|
|
* 3. initiate this call.
|
|
*/
|
|
fsmdef_find_and_hold_connected_call(call_id, &wait, CC_SRC_GSM);
|
|
|
|
fsmdef_find_and_handle_ring_connecting_releasing_calls(call_id, &wait2);
|
|
|
|
fsmdef_clear_preserved_calls(&wait3);
|
|
|
|
/*
|
|
* Requeue the message if we need to wait for the connected line to
|
|
* hold
|
|
*/
|
|
if ((wait == TRUE) || (wait2 == TRUE) || (wait3 == TRUE)) {
|
|
switch (msg_id) {
|
|
case CC_MSG_OFFHOOK:
|
|
cc_int_offhook(CC_SRC_GSM, CC_SRC_GSM, prim_call_id, consult_reason,
|
|
call_id, line, global_call_id, monitor_mode,CFWDALL_NONE);
|
|
break;
|
|
|
|
case CC_MSG_LINE:
|
|
cc_int_line(CC_SRC_GSM, CC_SRC_GSM, call_id, line);
|
|
break;
|
|
|
|
case CC_MSG_DIALSTRING:
|
|
cc_int_dialstring(CC_SRC_GSM, CC_SRC_GSM, call_id, line,
|
|
dial_string, global_call_id, monitor_mode);
|
|
break;
|
|
|
|
case CC_MSG_FEATURE:
|
|
if (dial_string != NULL) {
|
|
cc_int_dialstring(CC_SRC_GSM, CC_SRC_GSM, call_id, line,
|
|
dial_string, global_call_id, monitor_mode);
|
|
break;
|
|
}
|
|
|
|
/*FALLTHROUGH*/
|
|
default:
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
//lsm_set_active_call_id(call_id);
|
|
|
|
return (SM_RC_SUCCESS);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_idle_offhook (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_offhook_t *msg = (cc_offhook_t *) event->msg;
|
|
fsmdef_dcb_t *dcb;
|
|
sm_rcs_t sm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
sm_rc = fsmdef_offhook(fcb, msg->msg_id, msg->call_id, msg->line, NULL,
|
|
event, msg->global_call_id, msg->prim_call_id,
|
|
msg->hold_resume_reason, msg->monitor_mode);
|
|
|
|
if (sm_rc != SM_RC_SUCCESS) {
|
|
return (sm_rc);
|
|
}
|
|
|
|
dcb = fcb->dcb;
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
fsmdef_call_cc_state_dialing(dcb, FALSE);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_COLLECT_INFO);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_idle_dialstring (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_dialstring_t *msg = (cc_dialstring_t *) event->msg;
|
|
fsmdef_dcb_t *dcb;
|
|
cc_action_data_t data;
|
|
sm_rcs_t sm_rc;
|
|
cc_call_info_t call_info;
|
|
cc_call_info_t *call_info_p = NULL;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
sm_rc = fsmdef_offhook(fcb, msg->msg_id, msg->call_id, msg->line,
|
|
msg->dialstring, event, msg->g_call_id,
|
|
CC_NO_CALL_ID, CC_REASON_NONE, msg->monitor_mode);
|
|
|
|
if (sm_rc != SM_RC_SUCCESS) {
|
|
return (sm_rc);
|
|
}
|
|
|
|
dcb = fcb->dcb;
|
|
|
|
if (msg->dialstring) {
|
|
lsm_set_lcb_dialed_str_flag(dcb->call_id);
|
|
}
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
data.tone.tone = VCM_INSIDE_DIAL_TONE;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE, &data);
|
|
|
|
dcb->send_release = TRUE;
|
|
|
|
/* Set call_info with global call_id, sent in the Initcallreq,
|
|
* If this call is not because Initcallreq, then don't set call_info
|
|
* Also, if this is a monitor call, add the mode to the call_info
|
|
*/
|
|
if (msg->g_call_id != NULL) {
|
|
|
|
call_info.type = CC_FEAT_INIT_CALL;
|
|
call_info.data.initcall.monitor_mode = msg->monitor_mode;
|
|
sstrncpy(call_info.data.initcall.gcid, msg->g_call_id, CC_GCID_LEN);
|
|
|
|
call_info_p = &call_info;
|
|
}
|
|
|
|
if ( strncmp(CISCO_BLFPICKUP_STRING, msg->dialstring, strlen(CISCO_BLFPICKUP_STRING)) == 0 ) {
|
|
dcb->log_disp = CC_CALL_LOG_DISP_RCVD;
|
|
}
|
|
|
|
sm_rc = fsmdef_dialstring(fcb, msg->dialstring, NULL, FALSE, call_info_p);
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_session_audit (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_session_audit";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_audit_sdp_req_t *audit_msg = (cc_audit_sdp_req_t *) event->msg;
|
|
cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (gsmsdp_encode_sdp_and_update_version(dcb, &msg_body) != CC_CAUSE_OK) {
|
|
/*
|
|
* Failed to encode our local sdp. Send ack to SIP stack with
|
|
* no message body. SIP stack will include previously send
|
|
* SDP in response to session audit request.
|
|
*/
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
|
|
cc_int_audit_sdp_ack(CC_SRC_GSM, CC_SRC_SIP, audit_msg->call_id,
|
|
audit_msg->line, NULL);
|
|
} else {
|
|
cc_int_audit_sdp_ack(CC_SRC_GSM, CC_SRC_SIP, audit_msg->call_id,
|
|
audit_msg->line, &msg_body);
|
|
}
|
|
|
|
/*
|
|
* If we are currently performing a spoofed ringout and the current session audit
|
|
* does not indicate that we should continue to do so, go back to connected state.
|
|
* But only change to connected state if not locally held.
|
|
*/
|
|
if (dcb->spoof_ringout_applied &&
|
|
!dcb->spoof_ringout_requested) {
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
if ((fcb->state != FSMDEF_S_HOLDING) &&
|
|
(fcb->state != FSMDEF_S_HOLD_PENDING)) {
|
|
/*
|
|
* If is at least one media entry that is not in loally held
|
|
* then go to connected state.
|
|
*/
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
}
|
|
}
|
|
|
|
return (SM_RC_SUCCESS);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_collectinginfo_release (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_FAILED, CC_CAUSE_INVALID_NUMBER);
|
|
|
|
// Start onhook timer
|
|
if ( dcb->err_onhook_tmr) {
|
|
(void) cprDestroyTimer(dcb->err_onhook_tmr);
|
|
}
|
|
dcb->err_onhook_tmr = cprCreateTimer("Error Onhook",
|
|
GSM_ERROR_ONHOOK_TIMER,
|
|
TIMER_EXPIRATION,
|
|
gsm_msg_queue);
|
|
if (dcb->err_onhook_tmr == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
|
|
dcb->call_id, dcb->line, "", "Error Onhook");
|
|
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
if (cprStartTimer(dcb->err_onhook_tmr,
|
|
FSMDEF_ERR_ONHOOK_TMR_SECS * 1000,
|
|
(void *)(long)dcb->call_id) == CPR_FAILURE) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
|
|
dcb->call_id, dcb->line, "",
|
|
"Error Onhook", cpr_errno);
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_collectinginfo_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_action_data_t data;
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
cc_causes_t cause;
|
|
cc_feature_data_t *feature_data = &(msg->data);
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (msg->feature_id) {
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = feature_data->caps.support_direction;
|
|
break;
|
|
case CC_FEATURE_END_CALL:
|
|
cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
|
|
if (fcb->state == FSMDEF_S_KPML_COLLECT_INFO) {
|
|
/* Clean up and send release */
|
|
return (fsmdef_release(fcb, cause, TRUE));
|
|
}
|
|
else {
|
|
/* Clean up without sending release */
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
}
|
|
|
|
case CC_FEATURE_NUMBER:
|
|
case CC_FEATURE_URL:
|
|
dcb->dial_mode = ((msg->feature_id == CC_FEATURE_NUMBER) ?
|
|
(DIAL_MODE_NUMERIC) : (DIAL_MODE_URL));
|
|
|
|
data.dial_mode.mode = dcb->dial_mode;
|
|
data.dial_mode.digit_cnt = dcb->digit_cnt;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_DIAL_MODE,
|
|
&data);
|
|
|
|
break;
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
/*
|
|
* lsm_set_lcb_prevent_ringing() will check if there is a RINGIN call
|
|
* with the same GCID. If so, it will set a flag to prevent ringing.
|
|
*/
|
|
lsm_set_lcb_prevent_ringing(dcb->call_id);
|
|
break;
|
|
|
|
case CC_FEATURE_SELECT:
|
|
fsmdef_select_invoke(dcb, feature_data);
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_CFWD_ALL:
|
|
if (fsmdef_is_feature_uri_configured(msg->feature_id) == FALSE) {
|
|
fsm_set_call_status_feature_unavailable(dcb->call_id, dcb->line);
|
|
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
|
|
// handle cfwd event for ccm and non-ccm cases
|
|
// process feature event only if no other active feature
|
|
if (dcb->active_feature == CC_FEATURE_NONE) {
|
|
dcb->active_feature = ftr_id;
|
|
(void) fsmdef_process_cfwd_softkey_event(event);
|
|
} else {
|
|
fsmdef_check_active_feature(dcb, ftr_id);
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
dcb->active_feature = CC_FEATURE_NONE;
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
}
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
/*
|
|
* Function: fsmdef_ev_digit_begin
|
|
*
|
|
* Parameters: event
|
|
*
|
|
* Description: This function is called each time a digit is
|
|
* received from the platform code. Currently, the platform code
|
|
* parses the dialplan. This function simply turns the
|
|
* dialtone off every time a digit is received and
|
|
* displays the appropriate keyset. (Eventually, this
|
|
* interface should be changed and GSM should have
|
|
* better knowledge of the dialplan.)
|
|
*
|
|
* Returns: SM_RC_END
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_digit_begin (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_digit_begin";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_digit_begin_t *msg = (cc_digit_begin_t *) event->msg;
|
|
char digit;
|
|
cc_action_data_t data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
digit = lsm_digit2ch(msg->digit);
|
|
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Digit Received= %c: stopping dial tone..\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, msg->line, msg->call_id, fname), digit);
|
|
data.tone.tone = VCM_INSIDE_DIAL_TONE;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE, &data);
|
|
|
|
/*
|
|
* Increment digit_cnt so that the proper keyset will be
|
|
* displayed.
|
|
*/
|
|
if (dcb->digit_cnt < CC_MAX_DIALSTRING_LEN) {
|
|
dcb->digit_cnt++;
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_proceeding (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
fcb->dcb->send_release = TRUE;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
#ifdef SAPP_SAPP_GSM
|
|
if ((event->msg != NULL) &&
|
|
(((cc_proceeding_t *)(event->msg))->caller_id.called_name != NULL)) {
|
|
dcb->caller_id.called_name =
|
|
strlib_update(dcb->caller_id.called_name,
|
|
((cc_proceeding_t *) (event->msg))->caller_id.
|
|
called_name);
|
|
}
|
|
#endif
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_FAR_END_PROCEEDING,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_OUTGOING_PROCEEDING);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_out_alerting (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_out_alerting";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_alerting_t *msg = (cc_alerting_t *) event->msg;
|
|
cc_causes_t cause = CC_CAUSE_ERROR;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
dcb->send_release = TRUE;
|
|
|
|
dcb->inband = FALSE;
|
|
if (msg->inband) {
|
|
dcb->inband = TRUE;
|
|
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/*
|
|
* Record fact that we have successfully negotiated media that may be
|
|
* used for inband ringback.
|
|
*/
|
|
dcb->inband_received = TRUE;
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"inband_received, cancel timer.\n", DEB_F_PREFIX_ARGS(FSM, fname));
|
|
|
|
/*
|
|
* If ringback delay timer has been started, cancel it now.
|
|
*/
|
|
if (cprCancelTimer(dcb->ringback_delay_tmr) != CPR_SUCCESS) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CANCEL_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Ringback Delay",
|
|
cpr_errno);
|
|
}
|
|
} else {
|
|
/*
|
|
* Not inband alerting case. Set ringback delay timer so that local
|
|
* ringback will eventually be played. We delay the ringback for
|
|
* a short time to handle the case where the messages key was pressed.
|
|
* This is because VM server can respond very quickly with RTP, 183,
|
|
* and 200 and we do not want local ringback tone to interfere with
|
|
* the playing of the VM prompt.
|
|
*/
|
|
if (!cprIsTimerRunning(dcb->ringback_delay_tmr)) {
|
|
fsmdef_set_ringback_delay_timer(dcb);
|
|
}
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_FAR_END_ALERTING,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
/*
|
|
* If DSP is not able to start rx/tx channels, release the call
|
|
*/
|
|
if (dcb->dsp_out_of_resources == TRUE) {
|
|
(void)fsmdef_release(fcb, CC_CAUSE_NO_MEDIA, dcb->send_release);
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (SM_RC_END);
|
|
}
|
|
// fsmdef_update_pd(dcb, FSMDEF_CALL_TYPE_OUTGOING);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_OUTGOING_ALERTING);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_callsent_release (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_release_t *msg = (cc_release_t *) event->msg;
|
|
cc_causes_t cause = msg->cause;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
char tmp_str[STATUS_LINE_MAX_LEN];
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/* if UI_STATE of BUSY in 183 Call-Info is causing the release,
|
|
do not modify dcb->send_release */
|
|
if (cause != CC_CAUSE_UI_STATE_BUSY) {
|
|
dcb->send_release = FALSE;
|
|
} else {
|
|
// CSCti63677
|
|
if ((fcb->state == FSMDEF_S_OUTGOING_ALERTING) &&
|
|
(dcb->inband_received == TRUE) &&
|
|
(dcb->placed_call_update_required)) {
|
|
|
|
lsm_update_placed_callinfo(dcb);
|
|
dcb->placed_call_update_required = FALSE;
|
|
}
|
|
}
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
|
|
|
|
/* For 500 response from the CCM, disconnect the call and clear the UI.
|
|
* There are several cases in which CCM sends down 500 response code to
|
|
* clear the call and UI. Some of the cases are CFWDALL, early conference
|
|
* and CTI transfer of ringing call
|
|
*
|
|
* Non-auto pickups do receive 480 response, it is OK release the call.
|
|
*/
|
|
if ((cause == CC_CAUSE_REMOTE_SERVER_ERROR) ||
|
|
(((strncmp(dcb->caller_id.called_number, CISCO_BLFPICKUP_STRING,
|
|
(sizeof(CISCO_BLFPICKUP_STRING) - 1)) == 0)) &&
|
|
((cause == CC_TEMP_NOT_AVAILABLE) || (cause == CC_CAUSE_CONGESTION) ))) {
|
|
if (cause == CC_CAUSE_CONGESTION) {
|
|
if (platGetPhraseText(STR_INDEX_NO_CALL_FOR_PICKUP, (char *)tmp_str, STATUS_LINE_MAX_LEN - 1) == CPR_SUCCESS)
|
|
{
|
|
ui_set_notification(CC_NO_LINE, CC_NO_CALL_ID, tmp_str, 2, FALSE, DEF_NOTIFY_PRI);
|
|
}
|
|
}
|
|
cause = CC_CAUSE_OK;
|
|
}
|
|
|
|
switch (cause) {
|
|
case CC_CAUSE_ERROR:
|
|
case CC_CAUSE_NOT_FOUND:
|
|
case CC_CAUSE_BUSY:
|
|
case CC_CAUSE_CONGESTION:
|
|
case CC_CAUSE_INVALID_NUMBER:
|
|
case CC_CAUSE_PAYLOAD_MISMATCH:
|
|
case CC_CAUSE_REMOTE_SERVER_ERROR:
|
|
case CC_TEMP_NOT_AVAILABLE:
|
|
case CC_CAUSE_UI_STATE_BUSY:
|
|
case CC_CAUSE_NO_USER_ANS:
|
|
|
|
fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_FAILED, cause);
|
|
|
|
if (cause != CC_CAUSE_UI_STATE_BUSY) {
|
|
cc_int_release_complete(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, cause, NULL);
|
|
}
|
|
/* see if the SIP stack has aborted this call early for some reason
|
|
* If SIP brought this down, we are still offhook on the UI, so
|
|
* when we get the release_complete from the 200 for the BYE, we
|
|
* need to ignore it, so that reorder can be played AND when the user
|
|
* hangs up, then the UI will be driven to a clean state.
|
|
*/
|
|
if (src_id == CC_SRC_SIP) {
|
|
dcb->early_error_release = TRUE;
|
|
}
|
|
|
|
if ( dcb->err_onhook_tmr) {
|
|
(void) cprDestroyTimer(dcb->err_onhook_tmr);
|
|
}
|
|
dcb->err_onhook_tmr = cprCreateTimer("Error Onhook",
|
|
GSM_ERROR_ONHOOK_TIMER,
|
|
TIMER_EXPIRATION,
|
|
gsm_msg_queue);
|
|
if (dcb->err_onhook_tmr == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
|
|
dcb->call_id, dcb->line, "", "Error Onhook");
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
if (cprStartTimer(dcb->err_onhook_tmr,
|
|
FSMDEF_ERR_ONHOOK_TMR_SECS * 1000,
|
|
(void *)(long)dcb->call_id) == CPR_FAILURE) {
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
|
|
dcb->call_id, dcb->line, "",
|
|
"Error Onhook", cpr_errno);
|
|
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
sm_rc = fsmdef_release(fcb, cause, dcb->send_release);
|
|
if (sm_rc == SM_RC_CLEANUP) {
|
|
/*
|
|
* FSM release indicates clean up, do not continue
|
|
* on since fcb and dcb have been freed or re-initialized.
|
|
*/
|
|
return (sm_rc);
|
|
}
|
|
} /* switch (cause) */
|
|
|
|
/*UI_STATE of BUSY in 183 is causing the release, so
|
|
*don't change state. This is needed to support
|
|
*callback feature. Since the callee is busy, we need
|
|
*update call UI status to "Busy" from "Ringout" to
|
|
*reflect this change.
|
|
*/
|
|
if (cause != CC_CAUSE_UI_STATE_BUSY) {
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_RELEASING);
|
|
} else {
|
|
cc_action_data_t action_data;
|
|
action_data.update_ui.action = CC_UPDATE_SET_CALL_STATUS;
|
|
action_data.update_ui.data.set_call_status_parms.phrase_str_p = platform_get_phrase_index_str(LINE_BUSY);
|
|
action_data.update_ui.data.set_call_status_parms.timeout = 0;
|
|
action_data.update_ui.data.set_call_status_parms.call_id = dcb->call_id;
|
|
action_data.update_ui.data.set_call_status_parms.line = dcb->line;
|
|
/*Update UI status to "Busy".*/
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
|
|
&action_data);
|
|
}
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_callsent_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
cc_causes_t cause;
|
|
cc_feature_data_redirect_t *data = &(msg->data.redirect);
|
|
cc_action_data_t action_data;
|
|
cc_feature_data_t *select_data = &(msg->data);
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = select_data->caps.support_direction;
|
|
break;
|
|
case CC_FEATURE_NOTIFY:
|
|
if (src_id == CC_SRC_SIP) {
|
|
fsmdef_ev_notify_feature(msg, dcb);
|
|
} else {
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
}
|
|
|
|
break;
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
/**
|
|
* In case of earlier attandence, there might a waiting call.
|
|
*/
|
|
lsm_remove_lcb_prevent_ringing(dcb->call_id);
|
|
/*
|
|
* Since user press the end call, no need to wait to play the busy tone.
|
|
* So, clear the early_error_release and clean the fcb/dcb.
|
|
*/
|
|
dcb->early_error_release = FALSE;
|
|
cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
|
|
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
|
|
case CC_FEATURE_REDIRECT:
|
|
/*
|
|
* The outgoing call has been redirected, so we need to:
|
|
* 1. ACK the redirect request,
|
|
* 2. release the current call,
|
|
* 3. start a new call to the redirect number.
|
|
*/
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
|
|
CC_FEATURE_REDIRECT, NULL, CC_CAUSE_REDIRECT);
|
|
/*
|
|
* May need to update an xcb if this call is involved in a transfer.
|
|
*/
|
|
//xcb = fsmxfr_get_xcb_by_call_id(call_id);
|
|
// fsmxfr_update_xfr_context(xcb, call_id, redirect_call_id);
|
|
dcb->caller_id.called_number =
|
|
strlib_update(dcb->caller_id.called_number, data->redirect_number);
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_DIALING_COMPLETED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
break;
|
|
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_calltype(fcb, msg);
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
/*
|
|
* lsm_set_lcb_prevent_ringing() will check if there is a RINGIN call
|
|
* with the same GCID. If so, it will set a flag to prevent ringing.
|
|
*/
|
|
lsm_set_lcb_prevent_ringing(dcb->call_id);
|
|
break;
|
|
|
|
case CC_FEATURE_UPDATE:
|
|
/* Simply reply with a 200OK to a received UPDATE */
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
|
|
CC_FEATURE_UPDATE, NULL, CC_CAUSE_OK);
|
|
break;
|
|
|
|
case CC_FEATURE_RINGBACK_DELAY_TIMER_EXP:
|
|
if (!dcb->inband_received) {
|
|
/*
|
|
* Ringback delay timer expired and we have not received
|
|
* a response from the far end indicating that they are
|
|
* playing inband ringback. Start local ringback tone now.
|
|
*/
|
|
action_data.tone.tone = VCM_ALERTING_TONE;
|
|
(void)cc_call_action(call_id, line, CC_ACTION_PLAY_TONE,
|
|
&action_data);
|
|
}
|
|
break;
|
|
|
|
|
|
case CC_FEATURE_SELECT:
|
|
fsmdef_select_invoke(dcb, select_data);
|
|
return (SM_RC_END);
|
|
|
|
|
|
case CC_FEATURE_SUBSCRIBE:
|
|
/* KPML subscription received so collect digits for KPML */
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_KPML_COLLECT_INFO);
|
|
break;
|
|
|
|
case CC_FEATURE_CFWD_ALL:
|
|
fsm_set_call_status_feature_unavailable(call_id, line);
|
|
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
} /* switch (ftr_id) { */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_release_call (fsm_fcb_t *fcb, cc_feature_t *msg)
|
|
{
|
|
cc_feature_data_t *data = &(msg->data);
|
|
cc_state_data_t state_data;
|
|
cc_causes_t cause;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
cause = fsmdef_get_cause(msg->data_valid, data);
|
|
|
|
/*
|
|
* Do things a little different depending on the value of the
|
|
* release cause.
|
|
*/
|
|
switch (cause) {
|
|
case CC_CAUSE_XFER_LOCAL:
|
|
/*
|
|
* Send release and then wait for the release_complete.
|
|
*/
|
|
cc_int_release(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, data->endcall.cause,
|
|
data->endcall.dialstring, NULL);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_RELEASING);
|
|
|
|
state_data.onhook.caller_id = dcb->caller_id;
|
|
state_data.onhook.local = TRUE;
|
|
state_data.onhook.cause = CC_CAUSE_NORMAL;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK, &state_data);
|
|
|
|
break;
|
|
|
|
case CC_CAUSE_XFER_REMOTE:
|
|
/*
|
|
* No need to send release because the remote end initiated
|
|
* the transfer.
|
|
*/
|
|
dcb->send_release = FALSE;
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
|
|
case CC_CAUSE_XFER_CNF:
|
|
case CC_CAUSE_REPLACE:
|
|
/*
|
|
* We are the target of a transfer and this is the consultation
|
|
* call that is being replaced, so we just need to onhook this call
|
|
* but leave the signaling up until the stack notifies the FSM that
|
|
* the transfer is accepted - and then we will release the call.
|
|
* Same has to happen when bridge of conference ends the call. We are
|
|
* initiating transfer in this case so we want signaling to remain
|
|
* up while UI should be cleared up.
|
|
*/
|
|
state_data.onhook.caller_id = dcb->caller_id;
|
|
state_data.onhook.local = TRUE;
|
|
state_data.onhook.cause = CC_CAUSE_NORMAL;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ONHOOK, &state_data);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLDING);
|
|
|
|
break;
|
|
|
|
default:
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_inalerting_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case CC_SRC_UI:
|
|
case CC_SRC_GSM:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
/* force an update to media cap */
|
|
dcb->media_cap_tbl->id--;
|
|
gsmsdp_update_local_sdp_media_capability(dcb, FALSE, FALSE);
|
|
break;
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
return (fsmdef_release_call(fcb, msg));
|
|
|
|
case CC_FEATURE_ANSWER:
|
|
/*
|
|
* The user wants to answer this call, so...
|
|
* 1. need to place the connected call (if there is one) on hold,
|
|
* 2. clear all the outgoing ringing lines,
|
|
* 3. answer this call.
|
|
*/
|
|
if (fsmdef_wait_to_start_new_call(TRUE, CC_SRC_GSM, dcb->call_id, dcb->line,
|
|
CC_FEATURE_ANSWER, NULL)) {
|
|
|
|
/*
|
|
* Inform the LSM that the answering of this call has
|
|
* been delayed while waiting for other calls to clear.
|
|
*/
|
|
(void)cc_call_action(dcb->call_id, dcb->line,
|
|
CC_ACTION_ANSWER_PENDING, NULL);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
return (fsmdef_handle_inalerting_offhook_answer(event));
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (ftr_id) { */
|
|
|
|
break;
|
|
|
|
case CC_SRC_SIP:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
case CC_FEATURE_UPDATE:
|
|
/* Simply reply with a 200 OK to a received UPDATE */
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, call_id, line,
|
|
CC_FEATURE_UPDATE, NULL, CC_CAUSE_OK);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (ftr_id) { */
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (src_id) { */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
/*
|
|
* This function contains the common code for fsmdef_ev_inalerting_offhook()
|
|
* and the ANSWER event handling in the fsmdef_ev_inalerting_feature().
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_handle_inalerting_offhook_answer (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_causes_t cause;
|
|
cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/* Build our response SDP to include in the connected */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/* For CCM, call_type indicate if the call is forwarded or not
|
|
* for forwarded call display will be shown as "Forward", only
|
|
* during ringing state. Once the call is connected then the call
|
|
* is shown as normal incoming call "From". so change call type now
|
|
* Do this only if Retain Forward Information is disabled or not configured.
|
|
* If configured/enabled then leave the call type as Forward.
|
|
*/
|
|
|
|
if (dcb->call_type == FSMDEF_CALL_TYPE_FORWARD) {
|
|
if (!fsmdef_check_retain_fwd_info_state()) {
|
|
dcb->call_type = FSMDEF_CALL_TYPE_INCOMING;
|
|
/*
|
|
* Force us to update the UI so that any possible callinfo received
|
|
* prior to the call is answered takes effect.
|
|
*/
|
|
dcb->ui_update_required = TRUE;
|
|
}
|
|
}
|
|
|
|
/* Cancel any existing autoanswer timer */
|
|
(void)cprCancelTimer(dcb->autoAnswerTimer);
|
|
|
|
cc_int_connected(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), NULL, &msg_body);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_ANSWERED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTING);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_inalerting_offhook (sm_event_t *event)
|
|
{
|
|
return (fsmdef_handle_inalerting_offhook_answer(event));
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_connecting_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_causes_t cause;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case CC_SRC_UI:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
break;
|
|
case CC_FEATURE_END_CALL:
|
|
cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
|
|
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case CC_SRC_SIP:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
case CC_FEATURE_CALL_PRESERVATION:
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
|
|
|
|
case CC_FEATURE_NOTIFY:
|
|
fsmdef_ev_notify_feature(msg, dcb);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case CC_SRC_GSM:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_END_CALL:
|
|
cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
|
|
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
|
|
break;
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Function to handle transition to FSMDEF_S_CONNECTED. It checks
|
|
* whether there is any media capability that needs to be updated
|
|
* or not. If there is not then it transition to FSMDEF_S_CONNECTED
|
|
* otherwise it transitions to the FSMDEF_S_CONNECTED_MEDIA_PEND state
|
|
* and sends out the media update request.
|
|
*
|
|
* @param[in] fcb - The pointer to the fsm_fcb_t structure of this
|
|
* call.
|
|
*
|
|
* @return SM_RC_END or SM_RC_CLEANUP
|
|
*
|
|
* @pre (fcb not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_transition_to_connected (fsm_fcb_t *fcb)
|
|
{
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_data_t feature_data;
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (dcb->req_pending_tmr) {
|
|
/* cancel any request pending timer, just in case */
|
|
(void) cprCancelTimer(dcb->req_pending_tmr);
|
|
}
|
|
|
|
/*
|
|
* Update the media capability without effecting the existing media line.
|
|
*/
|
|
if (!gsmsdp_update_local_sdp_media_capability(dcb, FALSE, FALSE)) {
|
|
/* not thing is changed, transition to connected state */
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
|
|
return (sm_rc);
|
|
}
|
|
|
|
|
|
feature_data.resume.call_info.type = CC_FEAT_NONE;
|
|
feature_data.resume.call_info.data.hold_resume_reason = CC_REASON_NONE;
|
|
feature_data.resume.msg_body.num_parts = 0;
|
|
feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
|
|
/* Encode SDP */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb,
|
|
&feature_data.resume.msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return(fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
fsmdef_get_rtp_stat(dcb, &(feature_data.resume.kfactor));
|
|
|
|
/* Send feature request to SIP */
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
CC_FEATURE_MEDIA, &feature_data);
|
|
|
|
|
|
if (g_dock_undock_event == MEDIA_INTERFACE_UPDATE_STARTED) {
|
|
g_dock_undock_event = MEDIA_INTERFACE_UPDATE_IN_PROCESS;
|
|
ui_update_media_interface_change(dcb->line, dcb->call_id, MEDIA_INTERFACE_UPDATE_BEGIN);
|
|
} else if (g_dock_undock_event == MEDIA_INTERFACE_UPDATE_IN_PROCESS) {
|
|
DEF_DEBUG(DEB_F_PREFIX" MEDIA_INTERFACE_UPDATE is already in process. "
|
|
" Ignore another update event.\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_transition_to_connected"));
|
|
}
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED_MEDIA_PEND);
|
|
return (sm_rc);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_connected (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_connected";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_connected_t *msg = (cc_connected_t *) event->msg;
|
|
cc_causes_t cause;
|
|
sm_rcs_t sm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
dcb->send_release = TRUE;
|
|
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
// Reset dcb->active_feature flag
|
|
dcb->active_feature = CC_FEATURE_NONE;
|
|
|
|
/* Reset spoof ring out in case t was set before going to connected state. */
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
|
|
/*
|
|
* Cancel ringback delay timer
|
|
*/
|
|
if (cprCancelTimer(dcb->ringback_delay_tmr) != CPR_SUCCESS) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CANCEL_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Ringback Delay",
|
|
cpr_errno);
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
if ( dcb->log_disp != CC_CALL_LOG_DISP_UNKNWN ) {
|
|
ui_log_disposition(dcb->call_id, dcb->log_disp );
|
|
}
|
|
|
|
|
|
ui_cc_capability(dcb->line, lsm_get_ui_id(dcb->call_id), msg->recv_info_list);
|
|
|
|
/*
|
|
* If DSP is not able to start rx/tx channels, release the call
|
|
*/
|
|
if (dcb->dsp_out_of_resources == TRUE) {
|
|
(void)fsmdef_release(fcb, CC_CAUSE_NO_MEDIA, dcb->send_release);
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (SM_RC_END);
|
|
}
|
|
cc_int_connected_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), NULL);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED_ACK);
|
|
|
|
// fsmdef_update_pd(dcb, FSMDEF_CALL_TYPE_OUTGOING);
|
|
|
|
/*
|
|
* Handle media capability changes if there is before transition to
|
|
* connected state.
|
|
*/
|
|
sm_rc = fsmdef_transition_to_connected(fcb);
|
|
fsmutil_set_shown_calls_ci_element(dcb->caller_id.call_instance_id, dcb->line);
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_connected_ack (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_connected_ack_t *msg = (cc_connected_ack_t *) event->msg;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Check the remote SDP. The far end may not have included the SDP in an
|
|
* earlier message, which means that the SDP must be in this message.
|
|
*/
|
|
if (dcb->remote_sdp_in_ack == TRUE) {
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
/*
|
|
* If DSP is not able to start rx/tx channels, release the call
|
|
*/
|
|
if (dcb->dsp_out_of_resources == TRUE) {
|
|
(void)fsmdef_release(fcb, CC_CAUSE_NO_MEDIA, dcb->send_release);
|
|
cc_call_state(fcb->dcb->call_id, fcb->dcb->line, CC_STATE_UNKNOWN,
|
|
NULL);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
// fsmdef_update_pd(dcb, FSMDEF_CALL_TYPE_INCOMING);
|
|
|
|
/*
|
|
* Handle media capability changes if there is before transition to
|
|
* connected state.
|
|
*/
|
|
return (fsmdef_transition_to_connected(fcb));
|
|
}
|
|
|
|
/**
|
|
* The function handles local hold event but not sending any hold request
|
|
* out to the remote end. The local SDP is updated by the way.
|
|
*
|
|
* @param[in]fcb - pointer to fsm_fcb_t
|
|
*
|
|
* @return SM_RC_END or failrue.
|
|
*
|
|
* @pre (fcb not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsm_hold_local_only (fsm_fcb_t *fcb)
|
|
{
|
|
static const char fname[] = "fsm_hold_local_only";
|
|
cc_state_data_t state_data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Check local hold status, and allow request if the media is not
|
|
* locally held.
|
|
*/
|
|
if (fsmdef_all_media_are_local_hold(dcb)) {
|
|
/*
|
|
* a new hold request is not allowed. Ignore the request
|
|
* but we should still ack the request.
|
|
*/
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_HOLD, NULL, CC_CAUSE_NORMAL);
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
|
|
fname, "already hold");
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
state_data.hold.caller_id = dcb->caller_id;
|
|
state_data.hold.local = TRUE;
|
|
|
|
/*
|
|
* Update the SDP so that offer indicates hold. Reinitialize the local
|
|
* sdp media to include all available codecs. We do this because our local
|
|
* list has been shortened to the one negotiated codec.
|
|
*/
|
|
(void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, TRUE);
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_HOLD, &state_data);
|
|
|
|
/* set all the media to local hold */
|
|
fsmdef_update_media_hold_status(dcb, NULL, TRUE);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLDING);
|
|
|
|
sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT | CCSIP_SRC_SDP_BIT,
|
|
&dcb->sdp);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
* The function handles local hold event. The function also supports
|
|
* re-sending hold request out such as during a glare condition.
|
|
*
|
|
* @param[in]fcb - pointer to fsm_fcb_t
|
|
* @param[in]data_p - pointer to the cc_feature_data_t of the
|
|
* hold feature.
|
|
* @param[in]resend - TRUE indicates to resend hold request.
|
|
*
|
|
* @return SM_RC_END or failrue.
|
|
*
|
|
* @pre (fcb not_eq NULL)
|
|
* @pre (data_p not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsm_hold_local (fsm_fcb_t *fcb, cc_feature_data_t *data_p,
|
|
boolean resend)
|
|
{
|
|
static const char fname[] = "fsm_hold_local";
|
|
cc_state_data_t state_data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Check local hold status, and allow request if the media is not
|
|
* locally held or the caller indicates that to resend the hold
|
|
* request (such as in glare resolution).
|
|
*/
|
|
if (!resend && fsmdef_all_media_are_local_hold(dcb)) {
|
|
/*
|
|
* a new hold request is not allowed. Ignore the request
|
|
* but we should still ack the request.
|
|
*/
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_HOLD, NULL,
|
|
CC_CAUSE_NORMAL);
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
|
|
fname, "already hold");
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
state_data.hold.caller_id = dcb->caller_id;
|
|
state_data.hold.local = TRUE;
|
|
state_data.hold.reason = data_p->hold.call_info.data.hold_resume_reason;
|
|
|
|
/* Store hold reason in case we need to resend the hold request due to
|
|
* request pending response.
|
|
*/
|
|
dcb->hold_reason = data_p->hold.call_info.data.hold_resume_reason;
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
|
|
fsmdef_get_rtp_stat(dcb, &(data_p->hold.kfactor));
|
|
|
|
/* put the call on hold before building the SDP as DSP
|
|
* will then be able to give us a full set of codecs
|
|
* CUCM doesn't like to see a change in codecs on the fly
|
|
* ( i.e. without going to inactive state ) */
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_HOLD, &state_data);
|
|
|
|
/*
|
|
* Update the SDP so that offer indicates hold. Reinitialize the local
|
|
* sdp to include all available codecs. We do this because our
|
|
* local list has been shortened to the one negotiated codec.
|
|
*/
|
|
(void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, TRUE);
|
|
|
|
/*
|
|
* Do not expect any msg. body from local hold but free them
|
|
* just in case before build new SDP body to send out.
|
|
*/
|
|
cc_free_msg_body_parts(&data_p->hold.msg_body);
|
|
|
|
/* Build SDP for sending out */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &data_p->hold.msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/* set all the media to local hold */
|
|
fsmdef_update_media_hold_status(dcb, NULL, TRUE);
|
|
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
CC_FEATURE_HOLD, data_p);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLDING);
|
|
|
|
sipsdp_src_dest_free(CCSIP_DEST_SDP_BIT | CCSIP_SRC_SDP_BIT,
|
|
&dcb->sdp);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
* Handles hold feature in connected media update pending state.
|
|
*
|
|
* @param[in] fcb The pointer to the fsm_fcb_t structure of this
|
|
* call chain.
|
|
* @param[in] data_p pointer to the cc_feature_data_t.
|
|
*
|
|
* @return sm_rsc_t indicates whether the execution of
|
|
* next statmachine to end or to clean up.
|
|
*/
|
|
static sm_rcs_t
|
|
fsm_connected_media_pend_local_hold (fsm_fcb_t *fcb, cc_feature_data_t *data_p)
|
|
{
|
|
static const char fname[] = "fsm_hold_local_connected_media_pend";
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Check local hold status, and allow request if the media is not
|
|
* locally held.
|
|
*/
|
|
if (fsmdef_all_media_are_local_hold(dcb)) {
|
|
/*
|
|
* a new hold request is not allowed. Ignore the request
|
|
* but we should still ack the request.
|
|
*/
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_HOLD, NULL,
|
|
CC_CAUSE_NORMAL);
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
|
|
fname, "already hold");
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (dcb->req_pending_tmr &&
|
|
cprIsTimerRunning(dcb->req_pending_tmr)) {
|
|
/*
|
|
* Request timer is running, that means we are waiting
|
|
* to re-send media update again due to previously glare.
|
|
* Since the previous offer has not been accepted, we can
|
|
* simply just send hold instead when glare resolution timer
|
|
* expires.
|
|
*/
|
|
|
|
/* store the reason to resend when glare timer expires */
|
|
dcb->hold_reason = data_p->hold.call_info.data.hold_resume_reason;
|
|
/*
|
|
* reset feature hold pending flag in case that there are
|
|
* multiple hold feature received while waiting for
|
|
* glare resolution to resolve.
|
|
*/
|
|
FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* We have sent media capability update out but have not received
|
|
* any response yet. The glare condition may occur but we can only
|
|
* assume that the media update was sent out at this point.
|
|
* We can not send out any more request until the result is
|
|
* known. We can not do any thing now but simply remember
|
|
* to re-send media with the hold feature pending when
|
|
* the result is known.
|
|
*/
|
|
FSM_SET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
* common function to handles media feature from remote end.
|
|
*
|
|
* @param[in] fcb The pointer to the fsm_fcb_t structure of this
|
|
* call chain.
|
|
* @param[in] msg The pointer to cc_feature_t.
|
|
*
|
|
* @return sm_rsc_t indicates whether the execution of
|
|
* next statmachine to end or to clean up.
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_remote_media (fsm_fcb_t *fcb, cc_feature_t *msg)
|
|
{
|
|
static const char fname[] = "fsmdef_remote_media";
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
cc_feature_data_t feature_data;
|
|
cc_causes_t cause;
|
|
boolean send_ack = TRUE;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
memset(&feature_data, 0 , sizeof(cc_feature_data_t));
|
|
/*
|
|
* Determine what type of RESUME/MEDIA this is:
|
|
* 1. third-party control is just trying to change the media -
|
|
* midcall-invite with no SDP, so we need to wait for the SDP
|
|
* in the SIP ACK before we can truly resume the media.
|
|
* 2. remote end wants to resume a held call or just a media
|
|
* changes.
|
|
*
|
|
* We can distinguish between the two because case 1 will not
|
|
* have any data and case 2 will have data.
|
|
*/
|
|
if (msg->data_valid == FALSE) {
|
|
/*
|
|
* Case 1.
|
|
*
|
|
* negotiate offer without SDP will reset all local media entries
|
|
* to have all codecs included. This is to re-advertise the
|
|
* capabilities again.
|
|
*/
|
|
(void) gsmsdp_negotiate_offer_sdp(fcb, NULL, FALSE);
|
|
|
|
/*
|
|
* Update the media direction based on whether each media
|
|
* stream is locally held or not before sending out the
|
|
* offer SDP.
|
|
*/
|
|
fsmdef_set_per_media_local_hold_sdp(dcb);
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_MEDIA,
|
|
NULL);
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_START_RCV,
|
|
NULL);
|
|
} else {
|
|
/*
|
|
* SIP may send MEDIA feature when answer SDP is received in
|
|
* ACK. The secnario is found when remote resumes and the
|
|
* resume INVITE is a delayed media INVITE. We sent an offer in
|
|
* the 200 OK and gets the answer back in the ACK. In this
|
|
* case, SIP will send MEDIA feature to GSM. We need to check
|
|
* whether we are waiting for an answer in ACK or not and
|
|
* use the corresponding offer/answer SDP negotiation function.
|
|
*/
|
|
if (dcb->remote_sdp_in_ack) {
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb,
|
|
&data->resume.msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
/*
|
|
* There is some thing wrong the answer SDP for some
|
|
* reason, can not go on.
|
|
*/
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/*
|
|
* This is the answer to our previous offer, no need to
|
|
* to ack to SIP this one.
|
|
*/
|
|
send_ack = FALSE;
|
|
} else {
|
|
/* This is a new offer */
|
|
|
|
/*
|
|
* get k factor to be included in the feature ack. Getting
|
|
* the k factor needs to be done before maniputate media
|
|
* stream by the LSM.
|
|
*/
|
|
fsmdef_media_t *media = gsmsdp_find_audio_media(dcb);
|
|
if ((media) && (media->direction != SDP_DIRECTION_INACTIVE)) {
|
|
fsmdef_get_rtp_stat(dcb, &(feature_data.resume.kfactor));
|
|
}
|
|
|
|
cause = gsmsdp_negotiate_offer_sdp(fcb,
|
|
&data->resume.msg_body, FALSE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
/*
|
|
* Received a sdp that cannot be accepted.
|
|
* It should just reject the new sdp offer rather than
|
|
* tearing down the call.
|
|
*/
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, msg->feature_id, NULL, cause);
|
|
return (SM_RC_END);
|
|
}
|
|
/*
|
|
* Update the media based on local hold.
|
|
*/
|
|
fsmdef_set_per_media_local_hold_sdp(dcb);
|
|
}
|
|
|
|
/*
|
|
* If spoof ringout is not being requested and we are currently
|
|
* playing spoof ringout, transition the LSM from the far end alerting
|
|
* to the connected state.
|
|
*/
|
|
if ((!dcb->spoof_ringout_requested) && (dcb->spoof_ringout_applied)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
} else {
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_MEDIA,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
if (send_ack) {
|
|
/* Build SDP from our current SDP */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &feature_data.resume.msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, msg->feature_id, &feature_data,
|
|
CC_CAUSE_NORMAL);
|
|
}
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Function to handles connected state feature events. Function handles
|
|
* feature events generated by GSM, UI and SIP stack.
|
|
*
|
|
* @param sm_event_t event
|
|
*
|
|
* @return SM_RC_END or SM_RC_CLEANUP
|
|
*
|
|
* @pre (fcb->dcb not_eq NULL)
|
|
* @pre (event->data not_eq NULL)
|
|
* @pre (event->msg not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_connected_feature (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_connected_feature";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
sm_rcs_t sm_rc;
|
|
cc_feature_data_t feature_data;
|
|
cc_action_data_t action_data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case CC_SRC_UI:
|
|
case CC_SRC_GSM:
|
|
switch (msg->feature_id) {
|
|
case CC_FEATURE_HOLD:
|
|
/* If the line number is 0xFF, then this request
|
|
* came from GSM during a Transfer. We want to
|
|
* put the call on local hold only. We do not want
|
|
* to send a cc_feature to the SIP stack because
|
|
* that will cause an Invite Hold to go via SIP.
|
|
* We don't want to put the other end on hold, just
|
|
* ourselves.
|
|
*/
|
|
if (msg->line == 0xFF) {
|
|
sm_rc = fsm_hold_local_only(fcb);
|
|
} else {
|
|
if (msg->data_valid) {
|
|
sm_rc = fsm_hold_local(fcb, data, FALSE);
|
|
} else {
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
CC_REASON_NONE;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
sm_rc = fsm_hold_local(fcb, &feature_data, FALSE);
|
|
}
|
|
|
|
}
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_JOIN:
|
|
/*
|
|
* Send offhook to the new call that triggers the
|
|
* completion of the setup of the join in call
|
|
*/
|
|
fsmdef_ev_join(data);
|
|
break;
|
|
|
|
case CC_FEATURE_SELECT:
|
|
if (msg->data_valid == FALSE) {
|
|
fsmdef_select_invoke(dcb, NULL);
|
|
} else {
|
|
fsmdef_select_invoke(dcb, data);
|
|
}
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_B2B_JOIN:
|
|
if (msg->data_valid == FALSE) {
|
|
fsmdef_b2bjoin_invoke(dcb, NULL);
|
|
} else {
|
|
fsmdef_b2bjoin_invoke(dcb, data);
|
|
}
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_DIRTRXFR:
|
|
case CC_FEATURE_UNDEFINED:
|
|
fsm_display_feature_unavailable();
|
|
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
// Force an re-INVITE by mismatching the id
|
|
dcb->media_cap_tbl->id--;
|
|
/* FALL THRU */
|
|
case CC_FEATURE_UPD_MEDIA_CAP:
|
|
/*
|
|
* Media capability update request, check to see if
|
|
* there is any change in media capability and transition
|
|
* the pending state or stay in the connected state.
|
|
*/
|
|
sm_rc = fsmdef_transition_to_connected(fcb);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_REQ_PEND_TIMER_EXP:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_handle_join_pending(dcb);
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (msg->feature_id) */
|
|
|
|
break;
|
|
|
|
case CC_SRC_SIP:
|
|
switch (msg->feature_id) {
|
|
|
|
case CC_FEATURE_MEDIA:
|
|
/*
|
|
* remote send media update which can be resume or
|
|
* or just media changes.
|
|
*/
|
|
sm_rc = fsmdef_remote_media(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
case CC_FEATURE_CALL_PRESERVATION:
|
|
action_data.update_ui.action = CC_UPDATE_CALL_PRESERVATION;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_UPDATE_UI,
|
|
&action_data);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_PRESERVED);
|
|
break;
|
|
case CC_FEATURE_NOTIFY:
|
|
fsmdef_ev_notify_feature(msg, dcb);
|
|
break;
|
|
|
|
case CC_FEATURE_UPDATE:
|
|
/*
|
|
* We only get an UPDATE feature event if we receive a medialess UPDATE.
|
|
* This type of event only conveys UI updates that are processed with
|
|
* a call info event. We do perform one check to see if we are currently
|
|
* spoofing ringout. If we are and the spoof ringout requested flag
|
|
* has been cleared, we tell the LSM to go connected.
|
|
*/
|
|
if ((!dcb->spoof_ringout_requested) && (dcb->spoof_ringout_applied)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
}
|
|
|
|
/*
|
|
* For chaperone call, we will update call state here, to update
|
|
* the related key's status.
|
|
*/
|
|
if(dcb->policy == CC_POLICY_CHAPERONE){
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
}
|
|
break;
|
|
|
|
case CC_FEATURE_FAST_PIC_UPD:
|
|
|
|
vcmMediaControl(CREATE_CALL_HANDLE(dcb->line, dcb->call_id), VCM_MEDIA_CONTROL_PICTURE_FAST_UPDATE);
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
} /* switch (msg->feature_id) */
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
break;
|
|
} /* switch (src_id) */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Function to handles connected state media update pending feature.
|
|
*
|
|
* @param sm_event_t event
|
|
*
|
|
* @return SM_RC_END or SM_RC_CLEANUP
|
|
*
|
|
* @pre (fcb->dcb not_eq NULL)
|
|
* @pre (event->data not_eq NULL)
|
|
* @pre (event->msg not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_connected_media_pend_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
cc_feature_data_t feature_data;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case CC_SRC_UI:
|
|
case CC_SRC_GSM:
|
|
switch (msg->feature_id) {
|
|
case CC_FEATURE_HOLD:
|
|
/* If the line number is 0xFF, then this request
|
|
* came from GSM during a Transfer. We want to
|
|
* put the call on local hold only. We do not want
|
|
* to send a cc_feature to the SIP stack because
|
|
* that will cause an Invite Hold to go via SIP.
|
|
* We don't want to put the other end on hold, just
|
|
* ourselves.
|
|
*/
|
|
if (msg->line == 0xFF) {
|
|
sm_rc = fsm_hold_local_only(fcb);
|
|
} else {
|
|
if (msg->data_valid) {
|
|
sm_rc = fsm_connected_media_pend_local_hold(fcb, data);
|
|
} else {
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
CC_REASON_NONE;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
sm_rc = fsm_connected_media_pend_local_hold(fcb,
|
|
&feature_data);
|
|
}
|
|
}
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
/* FALL THRU */
|
|
case CC_FEATURE_UPD_MEDIA_CAP:
|
|
/* We are already in the media update state */
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_REQ_PEND_TIMER_EXP:
|
|
/*
|
|
* Glare resolution timer expires.
|
|
*/
|
|
if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING)) {
|
|
/* There is a hold feature pending, send out hold instead */
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
dcb->hold_reason;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
|
|
return (fsm_hold_local(fcb, &feature_data, FALSE));
|
|
}
|
|
|
|
/*
|
|
* Check the possible media capbility changes.
|
|
*/
|
|
(void)gsmsdp_update_local_sdp_media_capability(dcb, FALSE, FALSE);
|
|
feature_data.resume.call_info.type = CC_FEAT_NONE;
|
|
feature_data.resume.call_info.data.hold_resume_reason =
|
|
CC_REASON_NONE;
|
|
feature_data.resume.msg_body.num_parts = 0;
|
|
feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
|
|
/* Encode SDP */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb,
|
|
&feature_data.resume.msg_body);
|
|
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return(fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/* Send feature request to SIP */
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
CC_FEATURE_MEDIA, &feature_data);
|
|
return (SM_RC_END);
|
|
|
|
default:
|
|
/*
|
|
* The rest of the feature handles the same way as the
|
|
* connected feature handling.
|
|
*/
|
|
break;
|
|
} /* switch (msg->feature_id) */
|
|
break;
|
|
|
|
default:
|
|
/*
|
|
* The rest of the feature handles the same way as the
|
|
* connected feature handling.
|
|
*/
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Unhandled features are handled by the normal connected feature
|
|
*/
|
|
return (fsmdef_ev_connected_feature(event));
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Function to handles connected media update pending state feature ack
|
|
* events.
|
|
*
|
|
* @param sm_event_t event
|
|
*
|
|
* @return SM_RC_END or SM_RC_CLEANUP
|
|
*
|
|
* @pre (fcb->dcb not_eq NULL)
|
|
* @pre (event->data not_eq NULL)
|
|
* @pre (event->msg not_eq NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_connected_media_pend_feature_ack (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_connected_media_pend_feature_ack";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_ack_t *msg = (cc_feature_ack_t *) event->msg;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_feature_data_t feature_data;
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
cc_msgbody_info_t *msg_body;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
switch (src_id) {
|
|
case CC_SRC_SIP:
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_MEDIA:
|
|
/* Media update feature ack from SIP */
|
|
|
|
if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
|
|
/*
|
|
* The glare condition occurs from the previously sent
|
|
* media update. Starts a request pending timer so that
|
|
* we retry.
|
|
*/
|
|
fsmdef_set_req_pending_timer(dcb);
|
|
if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING)) {
|
|
/*
|
|
* Feature hold is pending, abort the media capability
|
|
* update and retry with hold instead.
|
|
*/
|
|
FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
|
|
}
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/* Check for error code reported */
|
|
if ((msg->cause != CC_CAUSE_NORMAL) &&
|
|
(msg->cause != CC_CAUSE_OK)) {
|
|
/* Unable to send media request */
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG2),
|
|
dcb->call_id, dcb->line, fname,
|
|
" Media request failed, cause= ", msg->cause);
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
|
|
msg_body = &msg->data.resume.msg_body;
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/*
|
|
* Check to see if we have a feature request pending
|
|
*/
|
|
if (FSM_CHK_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING)) {
|
|
/* There is a hold feature pending, send out hold instead */
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
dcb->hold_reason;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
FSM_RESET_FLAGS(dcb->flags, FSMDEF_F_HOLD_REQ_PENDING);
|
|
sm_rc = fsm_hold_local(fcb, &feature_data, FALSE);
|
|
} else {
|
|
/*
|
|
* If spoof ringout is not being requested and we are
|
|
* currently playing spoof ringout, transition the LSM from
|
|
* the far end alerting to the connected state.
|
|
*/
|
|
if ((!dcb->spoof_ringout_requested) &&
|
|
(dcb->spoof_ringout_applied)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
} else {
|
|
(void)cc_call_action(dcb->call_id, dcb->line,
|
|
CC_ACTION_MEDIA, NULL);
|
|
}
|
|
|
|
/*
|
|
* Check any media capability changes that might occurs
|
|
* while we were in the middle of the previous transaction.
|
|
*/
|
|
sm_rc = fsmdef_transition_to_connected(fcb);
|
|
if (g_dock_undock_event != MEDIA_INTERFACE_UPDATE_NOT_REQUIRED) {
|
|
if (is_gsmsdp_media_ip_updated_to_latest(dcb) == TRUE) {
|
|
ui_update_media_interface_change(dcb->line, dcb->call_id, MEDIA_INTERFACE_UPDATE_SUCCESSFUL);
|
|
} else {
|
|
DEF_DEBUG("We must have received another MEDIA_INTERFACE_UPDATE events "
|
|
" while current MEDIA_INTERFACE_UPDATE event is in procoess. Sending re-invite again");
|
|
escalateDeescalate();
|
|
}
|
|
}
|
|
}
|
|
return (sm_rc);
|
|
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* call default feature ack handler to take common/default actions*/
|
|
(void) fsmdef_ev_default_feature_ack(event);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_offhook (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_action_data_t data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* User has gone offhook while using the speaker.
|
|
*/
|
|
data.speaker.on = FALSE;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_SPEAKER, &data);
|
|
|
|
//lsm_set_active_call_id(dcb->call_id);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_connected_line (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
|
|
/*
|
|
* Handle media capability changes if there is before transition to
|
|
* connected state.
|
|
*/
|
|
return (fsmdef_transition_to_connected(fcb));
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_onhook (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
sm_rcs_t sm_rc;
|
|
cc_action_data_t data;
|
|
int sdpmode = 0;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/* currently this flag is only set by conference case. It signals
|
|
* that onhook has been received, do not process it anymore.
|
|
*/
|
|
if (dcb->onhook_received) {
|
|
dcb->onhook_received = FALSE;
|
|
return SM_RC_END;
|
|
}
|
|
|
|
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
|
|
|
|
if (sdpmode) {
|
|
if(dcb->ice_ufrag)
|
|
cpr_free(dcb->ice_ufrag);
|
|
|
|
if(dcb->ice_pwd)
|
|
cpr_free(dcb->ice_pwd);
|
|
}
|
|
|
|
/*
|
|
* If the user presses the ENDCALL softkey for an
|
|
* incoming call set the release cause to Busy.
|
|
*/
|
|
if (fcb->state == FSMDEF_S_INCOMING_ALERTING) {
|
|
sm_rc = fsmdef_release(fcb, CC_CAUSE_BUSY, dcb->send_release);
|
|
} else {
|
|
dcb->early_error_release = FALSE;
|
|
sm_rc = fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release);
|
|
}
|
|
|
|
if (sm_rc == SM_RC_CLEANUP) {
|
|
/* This dcb has been cleaned up, do nothing more */
|
|
return (sm_rc);
|
|
} else if (fcb->state == FSMDEF_S_HOLDING ||
|
|
fcb->state == FSMDEF_S_HOLD_PENDING) {
|
|
data.ringer.on = TRUE;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_RINGER, &data);
|
|
sm_rc = SM_RC_END;
|
|
} else {
|
|
sm_rc = SM_RC_END;
|
|
}
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_release (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_release_t *msg = (cc_release_t *) event->msg;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
dcb->send_release = FALSE;
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
|
|
|
|
if (msg->cause == CC_CAUSE_REMOTE_DISCONN_REQ_PLAYTONE) {
|
|
|
|
fsmdef_set_call_info_cc_call_state(dcb, CC_STATE_CALL_FAILED, CC_CAUSE_REMOTE_DISCONN_REQ_PLAYTONE);
|
|
|
|
/* what to return for return code */
|
|
return(SM_RC_SUCCESS);
|
|
} else {
|
|
return (fsmdef_release(fcb, msg->cause, dcb->send_release));
|
|
}
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_releasing_release (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_release_t *msg = (cc_release_t *) event->msg;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/* see if the SIP stack has aborted this call early for some reason
|
|
* If SIP brought this down, we are still offhook on the UI. We
|
|
* need to ignore it, so that reorder can be played AND when the user
|
|
* hangs up, then the UI will be driven to a clean state.
|
|
*/
|
|
if (fcb->dcb->early_error_release == FALSE) {
|
|
|
|
cc_int_release_complete(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
msg->cause, NULL);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_IDLE);
|
|
|
|
fsmdef_free_dcb(dcb);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
|
|
|
|
fsm_release(fcb, __LINE__, msg->cause);
|
|
|
|
return (SM_RC_CLEANUP);
|
|
} else {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_releasing_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_causes_t cause;
|
|
sm_rcs_t sm_rc = SM_RC_END;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_END_CALL:
|
|
cause = fsmdef_get_cause(msg->data_valid, &(msg->data));
|
|
|
|
/* Clean up call chain, no release sent */
|
|
return (fsmdef_release(fcb, cause, FALSE));
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
|
|
return (sm_rc);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_releasing_onhook (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/* Clean up call chain, no release sent */
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, FALSE));
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_release_complete (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (fcb->dcb == NULL) {
|
|
return (SM_RC_CLEANUP);
|
|
}
|
|
/* see if the SIP stack has aborted this call early for some reason
|
|
* If SIP brought this down, we are still offhook on the UI. We
|
|
* need to ignore it, so that reorder can be played AND when the user
|
|
* hangs up, then the UI will be driven to a clean state.
|
|
*/
|
|
if (fcb->dcb->early_error_release == FALSE) {
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_IDLE);
|
|
|
|
fsmdef_free_dcb(fcb->dcb);
|
|
|
|
fsm_release(fcb, __LINE__,
|
|
((cc_release_complete_t *) (event->msg))->cause);
|
|
|
|
return (SM_RC_CLEANUP);
|
|
|
|
} else {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_hold_pending_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
fsmcnf_ccb_t *ccb = NULL;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
cc_feature_data_t feature_data;
|
|
sm_rcs_t sm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case (CC_SRC_UI):
|
|
case (CC_SRC_GSM):
|
|
switch (ftr_id) {
|
|
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
break;
|
|
|
|
case CC_FEATURE_RESUME:
|
|
/*
|
|
* We will not be able to resume this call since we are in the
|
|
* hold pending state but we can place any other active call
|
|
* on hold. Find the connected call (if there is one) and place
|
|
* it on hold but not call if it is involved in a conference.
|
|
*/
|
|
if (msg->data.resume.cause != CC_CAUSE_CONF) {
|
|
if (fsmdef_wait_to_start_new_call(TRUE, src_id, call_id, line,
|
|
CC_FEATURE_RESUME, NULL)) {
|
|
ccb = fsmcnf_get_ccb_by_call_id(call_id);
|
|
if (ccb != NULL) {
|
|
ccb->cnf_ftr_ack = FALSE;
|
|
}
|
|
}
|
|
}
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_REQ_PEND_TIMER_EXP:
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
dcb->hold_reason;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
sm_rc = fsm_hold_local(fcb, &feature_data, TRUE);
|
|
return sm_rc;
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (ftr_id) */
|
|
|
|
break;
|
|
|
|
case (CC_SRC_SIP):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_MEDIA:
|
|
return (fsmdef_remote_media(fcb, msg));
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
case CC_FEATURE_CALL_PRESERVATION:
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
|
|
|
|
case CC_FEATURE_NOTIFY:
|
|
fsmdef_ev_notify_feature(msg, dcb);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
} /* switch (ftr_id) */
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
|
|
break;
|
|
} /* switch (src_id) */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
* feature ack event handler in hold pending state.
|
|
*
|
|
* @param[in] event Pointer to sm_event_t structure for feature ack event.
|
|
*
|
|
* @return Value of type sm_rcs_t to state machine
|
|
*
|
|
* @pre (event not_eqs NULL) and
|
|
* (event->data not_eqs NULL) and
|
|
* ((fsm_fcb_t *)(event->data)->dcb not_eqs NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_hold_pending_feature_ack (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_ack_t *msg = (cc_feature_ack_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_causes_t cause;
|
|
cc_msgbody_info_t *msg_body;
|
|
cc_feature_data_t feature_data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case (CC_SRC_SIP):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_RESUME:
|
|
/*
|
|
* This is feature ack for resume. We received resume
|
|
* feature ack because the hold request was received while
|
|
* we are waiting to resume i.e. was in the
|
|
* resume pending state and resume request has been sent out.
|
|
*
|
|
* If the resume ack indicates glare, then ignore sending the
|
|
* resume and transition to holding (we were in hold and
|
|
* unable to send RESUME). Otherwise send hold out right away
|
|
* if there is no other error.
|
|
*/
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
|
|
/*
|
|
* The glare condition occurs from the previously sent
|
|
* resume transition to holding.
|
|
*/
|
|
(void)fsm_hold_local_only(fcb);
|
|
break;
|
|
}
|
|
|
|
/* call default feature ack handler to take common/default actions*/
|
|
(void) fsmdef_ev_default_feature_ack(event);
|
|
|
|
if ((msg->cause != CC_CAUSE_NORMAL) &&
|
|
(msg->cause != CC_CAUSE_OK)) {
|
|
cc_call_state(dcb->call_id, dcb->line,
|
|
CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
|
|
if (msg->data_valid != TRUE) {
|
|
cc_call_state(dcb->call_id, dcb->line,
|
|
CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
|
|
msg_body = &msg->data.resume.msg_body;
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
return(fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/*
|
|
* HOLD can be sent now.
|
|
*/
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
dcb->hold_reason;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
fsm_hold_local(fcb, &feature_data, FALSE);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_holding_release (sm_event_t *event)
|
|
{
|
|
cc_release_t *msg = (cc_release_t *) event->msg;
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (msg->cause != CC_CAUSE_XFER_LOCAL) {
|
|
fcb->dcb->send_release = FALSE;
|
|
}
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
|
|
|
|
return (fsmdef_release(fcb, msg->cause, fcb->dcb->send_release));
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_holding_onhook (sm_event_t *event)
|
|
{
|
|
cc_onhook_t *msg = (cc_onhook_t *) event->msg;
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (!(msg->softkey)) {
|
|
/* Meaning Hangup, ignore, a held call can't be hung up */
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SM_DEFAULT_EVENT));
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* Meaning EndCall softkey is sent, take down
|
|
* the call, this happens during failover.
|
|
*/
|
|
FSM_SET_FLAGS(dcb->msgs_rcvd, FSMDEF_MSG_RELEASE);
|
|
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
|
|
}
|
|
|
|
/**
|
|
*
|
|
* fsmdef_reversion_timeout - Triggers LSM for doing Reversion alerts.
|
|
*
|
|
* @param fsmdef_dcb_t dcb for this call
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre (dcb not_eq NULL)
|
|
*/
|
|
|
|
void fsmdef_reversion_timeout(callid_t call_id)
|
|
{
|
|
|
|
int ret = CPR_SUCCESS;
|
|
|
|
fsmdef_dcb_t *dcb = fsmdef_get_dcb_by_call_id(call_id) ;
|
|
|
|
if ( (dcb == NULL ) || (dcb->fcb == NULL)) {
|
|
return;
|
|
}
|
|
|
|
// check that we are in HOLDING state before proceeding
|
|
if ((dcb->fcb->state != FSMDEF_S_HOLDING) &&
|
|
(dcb->fcb->state != FSMDEF_S_HOLD_PENDING)) {
|
|
return;
|
|
}
|
|
|
|
if (dcb->reversionInterval > 0) {
|
|
ret = cprStartTimer(dcb->revertTimer, dcb->reversionInterval * 1000, (void*)(long)call_id);
|
|
}
|
|
|
|
if ( ret == CPR_FAILURE ) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_START_FAILED),
|
|
dcb->call_id, dcb->line, "", "Reversion", cpr_errno);
|
|
return;
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_HOLD_REVERT, NULL);
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
*
|
|
* fsmdef_resume - Performs Resume Operation.
|
|
*
|
|
* @param sm_event_t event
|
|
*
|
|
* @return sm_rcs_t SM_RC_END - indicating the event has been consumed
|
|
*
|
|
* @pre (event not_eq NULL)
|
|
*/
|
|
|
|
static void
|
|
fsmdef_resume (sm_event_t *event)
|
|
{
|
|
|
|
static const char fname[] = "fsmdef_resume";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
fsmcnf_ccb_t *ccb = NULL;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
cc_srcs_t src_id = msg->src_id;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
cc_feature_data_t feature_data;
|
|
cc_causes_t cause;
|
|
boolean req_pending_tmr_running = FALSE;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* Ignore the request if local hold is not active.
|
|
*/
|
|
if (fsmdef_num_media_in_local_hold(dcb) == 0) {
|
|
/*
|
|
* No media in local held, should not happen here.
|
|
*/
|
|
cc_int_feature_ack(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_RESUME, NULL,
|
|
CC_CAUSE_NORMAL);
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), call_id, dcb->line,
|
|
fname, "resume media not in hold state\n");
|
|
return;
|
|
}
|
|
|
|
(void) cprCancelTimer(dcb->revertTimer);
|
|
dcb->reversionInterval = -1;
|
|
/*
|
|
* Make sure the connected call (if there is one) goes on hold,
|
|
* but do not hold the connected call if it is involved in a
|
|
* conference.
|
|
*/
|
|
if (msg->data.resume.cause != CC_CAUSE_CONF) {
|
|
if (fsmdef_wait_to_start_new_call(TRUE, src_id, call_id, line,
|
|
CC_FEATURE_RESUME, (msg->data_valid ?
|
|
data : NULL))) {
|
|
|
|
ccb = fsmcnf_get_ccb_by_call_id(call_id);
|
|
if (ccb != NULL) {
|
|
ccb->cnf_ftr_ack = FALSE;
|
|
}
|
|
return ;
|
|
}
|
|
}
|
|
|
|
/* Clear all media holding status */
|
|
fsmdef_update_media_hold_status(dcb, NULL, FALSE);
|
|
|
|
if (dcb->req_pending_tmr && cprIsTimerRunning(dcb->req_pending_tmr)) {
|
|
req_pending_tmr_running = TRUE;
|
|
}
|
|
|
|
if (!req_pending_tmr_running) {
|
|
/*
|
|
* Reinitialize the local sdp to include all available codecs.
|
|
* We do this because it is possible that our local list
|
|
* may have been shortened to the one negotiated codec. This is
|
|
* the case when we receive an incoming call, we negotiate a
|
|
* single codec, copy it into the local sdp and send it back
|
|
* in the connected msg.
|
|
*/
|
|
(void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, FALSE);
|
|
|
|
if (msg->data_valid) {
|
|
feature_data.resume.call_info = data->resume.call_info;
|
|
} else {
|
|
feature_data.resume.call_info.type = CC_FEAT_RESUME;
|
|
feature_data.resume.call_info.data.hold_resume_reason =
|
|
CC_REASON_NONE;
|
|
feature_data.resume.msg_body.num_parts = 0;
|
|
feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
|
|
}
|
|
/* Encode SDP */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb,
|
|
&feature_data.resume.msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
(void)fsmdef_release(fcb, cause, dcb->send_release);
|
|
return ;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* If spoof ringout is currently requested, transition to the
|
|
* far end alerting state instead of the connected state.
|
|
*/
|
|
if (dcb->spoof_ringout_requested) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, fname,
|
|
"setting spoof_ringout_applied");
|
|
|
|
dcb->spoof_ringout_applied = TRUE;
|
|
cc_call_state(dcb->call_id, dcb->line,
|
|
CC_STATE_FAR_END_ALERTING, FSMDEF_CC_CALLER_ID);
|
|
} else {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
/* Start receiving but not transmit, before sending resume */
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_START_RCV,
|
|
NULL);
|
|
}
|
|
|
|
if (!req_pending_tmr_running) {
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
CC_FEATURE_RESUME, &feature_data);
|
|
}
|
|
|
|
/*
|
|
* We lock the UI until we learn the result of the resume request
|
|
*/
|
|
fim_lock_ui(call_id);
|
|
|
|
/* Wait for feature ack */
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_RESUME_PENDING);
|
|
|
|
return ;
|
|
|
|
}
|
|
|
|
/**
|
|
*
|
|
* fsmdef_ev_holding_offhook - Handles offhook in for holding state.
|
|
*
|
|
* @param sm_event_t event
|
|
*
|
|
* @return sm_rcs_t SM_RC_END - indicating the event has been consumed
|
|
*
|
|
* @pre (event not_eq NULL)
|
|
*/
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_holding_offhook (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (cprIsTimerRunning(dcb->revertTimer)) {
|
|
// Off hook resume calls only if HR is active else ignore this event
|
|
fsmdef_resume(event);
|
|
}
|
|
|
|
return SM_RC_END;
|
|
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_holding_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
cc_feature_data_t feature_data;
|
|
sm_rcs_t sm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case (CC_SRC_UI):
|
|
case (CC_SRC_GSM):
|
|
switch (ftr_id) {
|
|
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
break;
|
|
|
|
case CC_FEATURE_HOLD:
|
|
if (msg->data_valid) {
|
|
sm_rc = fsm_hold_local(fcb, data, FALSE);
|
|
} else {
|
|
feature_data.hold.call_info.type = CC_FEAT_HOLD;
|
|
feature_data.hold.call_info.data.hold_resume_reason =
|
|
CC_REASON_NONE;
|
|
feature_data.hold.msg_body.num_parts = 0;
|
|
feature_data.hold.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.hold.call_info.data.call_info_feat_data.protect = FALSE;
|
|
sm_rc = fsm_hold_local(fcb, &feature_data, FALSE);
|
|
}
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_HOLD_REVERSION:
|
|
// Stop the timer for alert interval
|
|
(void) cprCancelTimer(dcb->revertTimer);
|
|
dcb->reversionInterval = -1;
|
|
|
|
// Do not revert if alertInterval is negative
|
|
if ( data->hold_reversion.alertInterval < 0 )
|
|
return SM_RC_END;
|
|
// interval timer of 0 implies continue to revert
|
|
// Clamp the interval to be in MIN/MAX_HOLD_REVERSION_INTERVAL_TIMER
|
|
if ( data->hold_reversion.alertInterval > 0 &&
|
|
data->hold_reversion.alertInterval < MIN_HOLD_REVERSION_INTERVAL_TIMER )
|
|
data->hold_reversion.alertInterval = MIN_HOLD_REVERSION_INTERVAL_TIMER;
|
|
|
|
if ( data->hold_reversion.alertInterval > MAX_HOLD_REVERSION_INTERVAL_TIMER )
|
|
data->hold_reversion.alertInterval = MAX_HOLD_REVERSION_INTERVAL_TIMER;
|
|
|
|
dcb->reversionInterval = data->hold_reversion.alertInterval ;
|
|
|
|
fsmdef_reversion_timeout(fcb->dcb->call_id);
|
|
|
|
fsmdef_handle_join_pending(dcb);
|
|
return SM_RC_END;
|
|
|
|
case CC_FEATURE_RESUME:
|
|
fsmdef_resume(event);
|
|
break;
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
(void) cprCancelTimer(dcb->revertTimer);
|
|
dcb->reversionInterval = -1;
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_SELECT:
|
|
if (msg->data_valid == FALSE) {
|
|
fsmdef_select_invoke(dcb, NULL);
|
|
} else {
|
|
fsmdef_select_invoke(dcb, data);
|
|
}
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_B2B_JOIN:
|
|
if (msg->data_valid == FALSE) {
|
|
fsmdef_b2bjoin_invoke(dcb, NULL);
|
|
} else {
|
|
fsmdef_b2bjoin_invoke(dcb, data);
|
|
}
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_DIRTRXFR:
|
|
case CC_FEATURE_UNDEFINED:
|
|
fsm_display_feature_unavailable();
|
|
|
|
fsmdef_handle_join_pending(dcb);
|
|
return (SM_RC_END);
|
|
|
|
case CC_FEATURE_REQ_PEND_TIMER_EXP:
|
|
/* Ignore the request pending timer when in holding state */
|
|
default:
|
|
fsmdef_handle_join_pending(dcb);
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (ftr_id) */
|
|
|
|
break;
|
|
|
|
case (CC_SRC_SIP):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_MEDIA:
|
|
return (fsmdef_remote_media(fcb, msg));
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
case CC_FEATURE_CALL_PRESERVATION:
|
|
(void) cprCancelTimer(dcb->revertTimer);
|
|
dcb->reversionInterval = -1;
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
|
|
|
|
case CC_FEATURE_NOTIFY:
|
|
fsmdef_ev_notify_feature(msg, dcb);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
|
|
break;
|
|
} /* switch (ftr_id) */
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
|
|
break;
|
|
} /* switch (src_id) */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_holding_feature_ack (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_holding_feature_ack";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_ack_t *msg = (cc_feature_ack_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_causes_t cause = msg->cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
switch (src_id) {
|
|
case (CC_SRC_SIP):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_HOLD:
|
|
if (cause == CC_CAUSE_REQUEST_PENDING) {
|
|
/*
|
|
* If this is feature ack for hold and request is pending,
|
|
* set a request pending timer so that we retry the hold
|
|
* feature request.
|
|
*/
|
|
fsmdef_set_req_pending_timer(dcb);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/* Check for error code reported */
|
|
if ((cause != CC_CAUSE_NORMAL) &&
|
|
(cause != CC_CAUSE_OK)) {
|
|
/* Unable to send hold request */
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG2),
|
|
dcb->call_id, dcb->line, fname,
|
|
"HOLD request failed, cause= ", cause);
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
// update video_avail as we are not negotiating the answer below
|
|
dcb->cur_video_avail = SDP_DIRECTION_INACTIVE;
|
|
lsm_update_video_avail(dcb->line, dcb->call_id, dcb->cur_video_avail);
|
|
break;
|
|
|
|
default:
|
|
fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fsm_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
|
|
/* call default feature ack handler to take common/default actions */
|
|
(void) fsmdef_ev_default_feature_ack(event);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_resume_pending_feature (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_resume_pending_feature";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
callid_t call_id = msg->call_id;
|
|
cc_feature_data_t *data = &(msg->data);
|
|
cc_feature_data_t feature_data;
|
|
sm_rcs_t sm_rc;
|
|
cc_causes_t cause;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case (CC_SRC_UI):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_UPD_SESSION_MEDIA_CAP:
|
|
dcb->video_pref = data->caps.support_direction;
|
|
break;
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
fim_unlock_ui(call_id);
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_HOLD:
|
|
/*
|
|
* The UI should be locked but the hold event from UI here
|
|
* can be the result of other call chain wants to put this
|
|
* other call that may be in conected state or in resume
|
|
* pending state on hold. One example, the user want to
|
|
* resume a call that is on hold, that call chain will look
|
|
* for any other call in connected state or call in resume
|
|
* pending (about to be connected) and puts this call on hold.
|
|
*
|
|
* There are 2 choices here. If we are in this state and has
|
|
* not sent a resume out to the network, then simply
|
|
* transition to holding state. If resume has already
|
|
* been sent and we are here waiting for resume pending ack,
|
|
* then we can not send out hold immediately. In the later
|
|
* case, go to hold pending state and wait for resume
|
|
* feature ack to send hold out.
|
|
*
|
|
* In either case, UI can be unlocked since we are now
|
|
* about to hold again.
|
|
*/
|
|
fim_unlock_ui(dcb->call_id);
|
|
if (dcb->req_pending_tmr &&
|
|
cprIsTimerRunning(dcb->req_pending_tmr)) {
|
|
/*
|
|
* Request timer is running, that means we are waiting
|
|
* to send resume or we already have sent one out
|
|
* but it resulted in glare condition and that we are waiting
|
|
* to resent it again. In this, just go back to hold state.
|
|
*/
|
|
(void) cprCancelTimer(dcb->req_pending_tmr);
|
|
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, fname,
|
|
"Received Hold while waiting to send resume\n");
|
|
|
|
/* Go back to holding without sending any thing out */
|
|
(void)fsm_hold_local_only(fcb);
|
|
} else {
|
|
/*
|
|
* We have sent resume to the network and wait for
|
|
* for the feature ack. The glare condition can
|
|
* occur but we can only assume that resume was sent out
|
|
* at this point. We can not send out any more request
|
|
* until the result is known.
|
|
*/
|
|
if (msg->data_valid) {
|
|
dcb->hold_reason =
|
|
data->hold.call_info.data.hold_resume_reason;
|
|
} else {
|
|
dcb->hold_reason = CC_REASON_NONE;
|
|
}
|
|
// since we are going to hold stop the media now
|
|
// else the next new call or resume will result in 2 sets of media ports open
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_MEDIA,
|
|
NULL);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_HOLD_PENDING);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case (CC_SRC_GSM):
|
|
switch (ftr_id) {
|
|
|
|
case CC_FEATURE_HOLD:
|
|
sm_rc = fsm_hold_local_only(fcb);
|
|
fim_unlock_ui(call_id);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
fim_unlock_ui(call_id);
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_REQ_PEND_TIMER_EXP:
|
|
|
|
/*
|
|
* Reinitialize the local sdp to include all available codecs.
|
|
* We do this because it is possible that our local list
|
|
* may have been shortened to the one negotiated codec. This is
|
|
* the case when we receive an incoming call, we negotiate a
|
|
* single codec, copy it into the local sdp and send it back
|
|
* in the connected msg.
|
|
*/
|
|
(void)gsmsdp_update_local_sdp_media_capability(dcb, TRUE, FALSE);
|
|
|
|
feature_data.resume.call_info.type = CC_FEAT_RESUME;
|
|
feature_data.resume.call_info.data.hold_resume_reason =
|
|
CC_REASON_NONE;
|
|
feature_data.resume.msg_body.num_parts = 0;
|
|
feature_data.resume.call_info.data.call_info_feat_data.swap = FALSE;
|
|
feature_data.resume.call_info.data.call_info_feat_data.protect = FALSE;
|
|
/* Encode SDP */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &feature_data.resume.msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/*
|
|
* We lock the UI until we learn the result of the resume request.
|
|
*/
|
|
fim_lock_ui(call_id);
|
|
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
CC_FEATURE_RESUME, &feature_data);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
} /* switch (ftr_id) */
|
|
|
|
break;
|
|
|
|
case (CC_SRC_SIP):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_MEDIA:
|
|
return (fsmdef_remote_media(fcb, msg));
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
case CC_FEATURE_CALL_PRESERVATION:
|
|
fim_unlock_ui(call_id);
|
|
return (fsmdef_release(fcb, CC_CAUSE_NORMAL, dcb->send_release));
|
|
|
|
case CC_FEATURE_NOTIFY:
|
|
fsmdef_ev_notify_feature(msg, dcb);
|
|
break;
|
|
|
|
case CC_FEATURE_UPDATE:
|
|
/*
|
|
* We only get an UPDATE feature event if we receive a medialess UPDATE.
|
|
* This type of event only conveys UI updates that are processed with
|
|
* a call info event. We do perform one check to see if we are currently
|
|
* spoofing ringout. If we are and the spoof ringout requested flag
|
|
* has been cleared, we tell the LSM to go connected which halts the
|
|
* spoofed ringout.
|
|
*/
|
|
if ((!dcb->spoof_ringout_requested) && (dcb->spoof_ringout_applied)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_CLR_SPOOF_APPLD),
|
|
dcb->call_id, dcb->line, fname);
|
|
|
|
dcb->spoof_ringout_applied = FALSE;
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
} /* switch (ftr_id) */
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
break;
|
|
} /* switch (src_id) */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
* feature ack event handler in resume pending state.
|
|
*
|
|
* @param[in] event Pointer to sm_event_t structure for feature ack event.
|
|
*
|
|
* @return Value of type sm_rcs_t to state machine
|
|
*
|
|
* @pre (event not_eqs NULL) and
|
|
* (event->data not_eqs NULL) and
|
|
* ((fsm_fcb_t *)(event->data)->dcb not_eqs NULL)
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_resume_pending_feature_ack (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_ev_resume_pending_feature_ack";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_ack_t *msg = (cc_feature_ack_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_causes_t cause;
|
|
cc_msgbody_info_t *msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case (CC_SRC_SIP):
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_HOLD:
|
|
/*
|
|
* This can occur because SIP stack has not sent HOLD out yet
|
|
* but the user has pressed resume since we already moved
|
|
* to HOLD state as soon as the user pressed HOLD. Ignore the
|
|
* HOLD feature ACK except if it indicates error. If we get
|
|
* REQUEST_PENDING, we'll just move to connected state.
|
|
*/
|
|
if (msg->cause == CC_CAUSE_REQUEST_PENDING) {
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CONNECTED);
|
|
return (SM_RC_END);
|
|
}
|
|
if ((msg->cause != CC_CAUSE_NORMAL) &&
|
|
(msg->cause != CC_CAUSE_OK)) {
|
|
cc_call_state(dcb->call_id, dcb->line,
|
|
CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
|
|
case CC_FEATURE_RESUME:
|
|
/*
|
|
* If this is feature ack for resume and request is pending,
|
|
* set a request pending timer so that we retry the resume
|
|
* feature request. Otherwise, unlock the UI and continue
|
|
* processing.
|
|
*/
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
if ( msg->cause == CC_CAUSE_REQUEST_PENDING ) {
|
|
fsmdef_set_req_pending_timer(dcb);
|
|
return (SM_RC_END);
|
|
} else {
|
|
fim_unlock_ui(dcb->call_id);
|
|
}
|
|
/* call default feature ack handler to take common/default actions*/
|
|
(void) fsmdef_ev_default_feature_ack(event);
|
|
|
|
if ((msg->cause == CC_CAUSE_SERV_ERR_UNAVAIL) &&
|
|
(dcb->hold_reason == CC_REASON_MONITOR_UPDATE)) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1),
|
|
dcb->call_id, dcb->line, fname,
|
|
"msg->cause == CC_CAUSE_SERV_ERR_UNAVAIL, unable to monitor update\n");
|
|
return (fsmdef_transition_to_connected(fcb));
|
|
}
|
|
|
|
if ((msg->cause != CC_CAUSE_NORMAL) &&
|
|
(msg->cause != CC_CAUSE_OK)) {
|
|
cc_call_state(dcb->call_id, dcb->line,
|
|
CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
|
|
if (msg->data_valid != TRUE) {
|
|
cc_call_state(dcb->call_id, dcb->line,
|
|
CC_STATE_UNKNOWN, NULL);
|
|
return(fsmdef_release(fcb, CC_CAUSE_ERROR, dcb->send_release));
|
|
}
|
|
|
|
msg_body = &msg->data.resume.msg_body;
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
if (!dcb->spoof_ringout_applied) {
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
FSMDEF_CC_CALLER_ID);
|
|
}
|
|
/*
|
|
* Handle media capability changes if there is before transition
|
|
* to connected state to catch any changes during resume
|
|
* transaction.
|
|
*/
|
|
return (fsmdef_transition_to_connected(fcb));
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
}
|
|
|
|
/* call default feature ack handler to take common/default actions */
|
|
(void) fsmdef_ev_default_feature_ack(event);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
static sm_rcs_t
|
|
fsmdef_ev_preserved_feature (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_srcs_t src_id = msg->src_id;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
sm_rcs_t sm_rc;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fsm_sm_ftr(ftr_id, src_id);
|
|
|
|
switch (src_id) {
|
|
case CC_SRC_UI:
|
|
case CC_SRC_GSM:
|
|
switch (msg->feature_id) {
|
|
|
|
case CC_FEATURE_END_CALL:
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
break;
|
|
}
|
|
|
|
break;
|
|
|
|
case CC_SRC_SIP:
|
|
switch (msg->feature_id) {
|
|
case CC_FEATURE_HOLD:
|
|
case CC_FEATURE_RESUME:
|
|
sm_rc = fsmdef_release_call(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_MEDIA:
|
|
sm_rc = fsmdef_remote_media(fcb, msg);
|
|
return (sm_rc);
|
|
|
|
case CC_FEATURE_CALLINFO:
|
|
fsmdef_update_callinfo(fcb, msg);
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_ftr(fcb, __LINE__, ftr_id);
|
|
break;
|
|
} /* switch (msg->feature_id) */
|
|
|
|
break;
|
|
|
|
default:
|
|
fsmdef_sm_ignore_src(fcb, __LINE__, src_id);
|
|
break;
|
|
} /* switch (src_id) */
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
cc_int32_t
|
|
fsmdef_show_cmd (cc_int32_t argc, const char *argv[])
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
fsm_fcb_t *fcb;
|
|
int i = 0;
|
|
callid_t call_id;
|
|
unsigned long strtoul_result;
|
|
char *strtoul_end;
|
|
|
|
PR_ASSERT( i == 0 );
|
|
/*
|
|
* Check if need help.
|
|
*/
|
|
if ((argc == 2) && (argv[1][0] == '?')) {
|
|
debugif_printf("%s", "show fsmdef [all|rel]\n");
|
|
} else if ((argc == 1) || (strcmp(argv[1], "all") == 0)) {
|
|
debugif_printf("%s", "\n-------- FSMDEF dcbs --------");
|
|
debugif_printf("%s", "\ni call_id dcb line");
|
|
debugif_printf("%s", "\n-----------------------------\n");
|
|
|
|
/*
|
|
* Print info for all dcbs.
|
|
*/
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
debugif_printf("%-2d %-7d 0x%8p %-4d\n",
|
|
i++, dcb->call_id, dcb, dcb->line);
|
|
}
|
|
} else if (strcmp(argv[1], "rel") == 0) {
|
|
errno = 0;
|
|
strtoul_result = strtoul(argv[2], &strtoul_end, 10);
|
|
|
|
if (errno || argv[2] == strtoul_end || strtoul_result > USHRT_MAX) {
|
|
debugif_printf("%s parse error of call_id %s", __FUNCTION__, argv[2]);
|
|
return 0;
|
|
}
|
|
|
|
call_id = (callid_t) strtoul_result;
|
|
|
|
debugif_printf("\nDEF %-4d/%d: releasing\n", call_id, 0);
|
|
|
|
fcb = fsm_get_fcb_by_call_id_and_type(call_id, FSM_TYPE_DEF);
|
|
if (fcb == NULL) {
|
|
return (0);
|
|
}
|
|
|
|
(void)fsmdef_release(fcb, CC_CAUSE_NORMAL, fcb->dcb->send_release);
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* This function is called to determine if the phone
|
|
* is in a state where autoAnswer is supported. Even
|
|
* if the feature is enabled on the line being called,
|
|
* the phone may be in a state that does not allow it.
|
|
*
|
|
* @param autoAnswerAlt to indicate what states auto answer enabled.
|
|
* @param myCallId call_id
|
|
*
|
|
* @return TRUE or FALSE
|
|
*
|
|
*/
|
|
static int
|
|
fsmdef_check_auto_answer_allowed (int autoAnswerAlt, callid_t myCallId)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->call_id != myCallId && dcb->fcb != NULL) {
|
|
/*
|
|
* autoAnswer is disabled if there is an incoming or an
|
|
* outgoing call that has not reached the connected state
|
|
* regardless of the value of autoAnswerAlt.
|
|
*/
|
|
if ((dcb->fcb->state == FSMDEF_S_COLLECT_INFO) ||
|
|
(dcb->fcb->state == FSMDEF_S_CALL_SENT) ||
|
|
(dcb->fcb->state == FSMDEF_S_OUTGOING_PROCEEDING) ||
|
|
(dcb->fcb->state == FSMDEF_S_KPML_COLLECT_INFO) ||
|
|
(dcb->fcb->state == FSMDEF_S_CONNECTING) ||
|
|
(dcb->fcb->state == FSMDEF_S_JOINING)) {
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* If autoAnswerAlt is 1, then autoAnswer is also
|
|
* disabled if there is a connected call or a call
|
|
* on hold.
|
|
*/
|
|
if (autoAnswerAlt == 1) {
|
|
if ((dcb->fcb->state == FSMDEF_S_CONNECTED) ||
|
|
(dcb->fcb->state == FSMDEF_S_PRESERVED) ||
|
|
(dcb->fcb->state == FSMDEF_S_RESUME_PENDING) ||
|
|
(dcb->fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND) ||
|
|
(dcb->fcb->state == FSMDEF_S_HOLDING) ||
|
|
(dcb->fcb->state == FSMDEF_S_OUTGOING_ALERTING) ||
|
|
(dcb->fcb->state == FSMDEF_S_INCOMING_ALERTING) ||
|
|
(dcb->fcb->state == FSMDEF_S_HOLD_PENDING)) {
|
|
|
|
return (FALSE);
|
|
}
|
|
} else if (autoAnswerAlt == 0) {
|
|
if ((dcb->fcb->state == FSMDEF_S_CONNECTED) ||
|
|
(dcb->fcb->state == FSMDEF_S_PRESERVED) ||
|
|
(dcb->fcb->state == FSMDEF_S_CONNECTED_MEDIA_PEND) ||
|
|
(dcb->fcb->state == FSMDEF_S_OUTGOING_ALERTING)) {
|
|
|
|
return (FALSE);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* This function is called when a call is received on a
|
|
* line that has auto answer enabled and the autoAnswer
|
|
* timer has expired.
|
|
*
|
|
* @param data pointer to opaque data
|
|
*
|
|
* @return none
|
|
*
|
|
* @pre (fcb and fcb->dcb not_eq NULL)
|
|
* @pre (data should be fcb data)
|
|
*/
|
|
|
|
void
|
|
fsmdef_auto_answer_timeout (void *data)
|
|
{
|
|
static const char fname[] = "fsmdef_auto_answer_timeout";
|
|
int autoAnswerAlternate = 0;
|
|
int autoAnswerOverride = 0;
|
|
int headSetActive = 0;
|
|
int speakerEnabled = 1;
|
|
callid_t call_id;
|
|
char autoAnswerMode[MAX_LINE_AUTO_ANS_MODE_SIZE];
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
call_id = (callid_t)(long)data;
|
|
if (call_id == CC_NO_CALL_ID) {
|
|
/* Invalid call id */
|
|
GSM_ERR_MSG(get_debug_string(FSMDEF_DBG1), 0, 0, fname, "invalid data");
|
|
return;
|
|
}
|
|
|
|
/* Retrieve dcb from call id */
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
if (dcb == NULL) {
|
|
/*
|
|
* The only way this should happen is for the
|
|
* call to be released without cancelling the
|
|
* autoAnswer timer.
|
|
*/
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"AutoAnswer timer expired but no dcb was found.\n", DEB_F_PREFIX_ARGS(FSM, fname));
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* The device-based config parameter autoAnswerIdleAlternate
|
|
* determines what state the phone must be in for autoAnswer
|
|
* feature to be enabled.
|
|
*/
|
|
config_get_value(CFGID_AUTOANSWER_IDLE_ALTERNATE, &autoAnswerAlternate,
|
|
sizeof(autoAnswerAlternate));
|
|
|
|
if (fsmdef_check_auto_answer_allowed(autoAnswerAlternate, dcb->call_id)) {
|
|
/*
|
|
* Is headset active? No need to check if headset has been disabled
|
|
* on the phone because the headset will never be active if it has.
|
|
*/
|
|
headSetActive = platGetAudioDeviceStatus(VCM_AUDIO_DEVICE_HEADSET);
|
|
|
|
/*
|
|
* If function call fails, just assume headset is not active
|
|
* and drive on
|
|
*/
|
|
if (headSetActive == -1) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"platGetAudioDeviceStatus() for headset failed.\n", DEB_F_PREFIX_ARGS(FSM, fname));
|
|
headSetActive = 0;
|
|
}
|
|
|
|
/*
|
|
* Determine where the audio should be sent
|
|
*/
|
|
config_get_line_string(CFGID_LINE_AUTOANSWER_MODE, autoAnswerMode,
|
|
dcb->line, sizeof(autoAnswerMode));
|
|
|
|
/*
|
|
* Check to see if the speaker has been disabled
|
|
*/
|
|
config_get_value(CFGID_SPEAKER_ENABLED, &speakerEnabled,
|
|
sizeof(speakerEnabled));
|
|
|
|
/*
|
|
* autoAnswer using speaker
|
|
*/
|
|
if (strcasestr(autoAnswerMode, "speaker")) {
|
|
/*
|
|
* Speaker has not been disabled, normal processing.
|
|
*/
|
|
if (speakerEnabled) {
|
|
/*
|
|
* Enable audio path to speaker if headset is not being used.
|
|
*/
|
|
if (!headSetActive) {
|
|
platSetSpeakerMode(TRUE);
|
|
}
|
|
/* Send offhook event to GSM */
|
|
cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_ANSWER, NULL);
|
|
} else {
|
|
|
|
/*
|
|
* Speaker is disabled, but autoAnswer is enabled and says to
|
|
* use the speaker. Check the device-based override
|
|
* parameter to see what action to take. If override is
|
|
* set just return and let normal call processing happen.
|
|
* If not, terminate the call. The best SIP response we
|
|
* could come up in this situation is to send a 480
|
|
* Temporarily Unavailable.
|
|
*/
|
|
config_get_value(CFGID_AUTOANSWER_OVERRIDE, &autoAnswerOverride,
|
|
sizeof(autoAnswerOverride));
|
|
if (!autoAnswerOverride) {
|
|
fsmdef_end_call(dcb, CC_TEMP_NOT_AVAILABLE);
|
|
}
|
|
|
|
}
|
|
} else if (strcasestr(autoAnswerMode, "headset")) {
|
|
/*
|
|
* autoAnswer using headset. If headset is not enabled just let
|
|
* the phone ring normally.
|
|
*/
|
|
if (headSetActive) {
|
|
/* Send offhook event to GSM */
|
|
cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_ANSWER, NULL);
|
|
}
|
|
} else {
|
|
/*
|
|
* Unknown autoAnswer mode
|
|
*/
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Unknown autoAnswer Mode: %s AutoAnswer is disabled.\n",
|
|
DEB_F_PREFIX_ARGS(FSM, fname), autoAnswerMode);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* fsmdef_b2bjoin_invoke
|
|
*
|
|
* Description:
|
|
* This function implements the b2bjoin feature invocation.
|
|
*
|
|
* Parameters:
|
|
* fsmdef_dcb_t *dcb - dcb associated with this call
|
|
*
|
|
* Returns: None
|
|
*/
|
|
static void
|
|
fsmdef_b2bjoin_invoke (fsmdef_dcb_t *dcb, cc_feature_data_t *join_data)
|
|
{
|
|
cc_feature_data_t feature_data;
|
|
int join_across_lines;
|
|
cc_uint32_t major_ver;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
// Disable Join if the CUCM doesn't support it
|
|
platGetSISProtocolVer(&major_ver, NULL, NULL, NULL);
|
|
|
|
if ( major_ver < SIS_PROTOCOL_MAJOR_VERSION_UNISON ) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG1), dcb->call_id, dcb->line,
|
|
"fsmdef_b2bjoin_invoke", "Major sis is small than SIS_PROTOCOL_MAJOR_VERSION_UNISON, so, B2BJOIN is disabled");
|
|
fsm_display_feature_unavailable();
|
|
fsmdef_sm_ignore_ftr(dcb->fcb, __LINE__, CC_FEATURE_B2B_JOIN);
|
|
return;
|
|
}
|
|
|
|
config_get_value(CFGID_JOIN_ACROSS_LINES,
|
|
&join_across_lines, sizeof(join_across_lines));
|
|
/*
|
|
* Invoke B2BJoin feature towards SIP. It will send a REFER to CCM
|
|
*/
|
|
if (join_data) {
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, CC_FEATURE_B2B_JOIN, join_data);
|
|
} else {
|
|
|
|
if ((g_b2bjoin_pending == FALSE) && (dcb->fcb->state == FSMDEF_S_HOLDING)
|
|
&& ((fsmdef_get_connected_call() != NULL) ||
|
|
(fsmdef_get_alertingout_call() != NULL))) {
|
|
/* Single Button Join case
|
|
* If Join is pressed on a held call while there is
|
|
* an active(connected or alerting) call, that completes the Join
|
|
*/
|
|
feature_data.b2bjoin.b2bjoin_callid = dcb->call_id;
|
|
feature_data.b2bjoin.b2bjoin_joincallid = dcb->call_id;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, CC_FEATURE_B2B_JOIN, &feature_data);
|
|
return;
|
|
}
|
|
|
|
if ((g_numofselected_calls == 0) ||
|
|
((g_b2bjoin_pending == FALSE) && (join_across_lines == JOIN_ACROSS_LINES_DISABLED) &&
|
|
(fsmdef_are_there_selected_calls_onotherline(dcb->line) == TRUE))) {
|
|
dcb->active_feature = CC_FEATURE_B2B_JOIN;
|
|
feature_data.select.select = TRUE;
|
|
fsmdef_select_invoke(dcb,&feature_data);
|
|
fsm_display_use_line_or_join_to_complete();
|
|
return;
|
|
}
|
|
if (g_b2bjoin_pending) {
|
|
if (join_across_lines == JOIN_ACROSS_LINES_DISABLED) {
|
|
if (fsmdef_are_join_calls_on_same_line(dcb->line) == FALSE) {
|
|
|
|
fsm_display_use_line_or_join_to_complete();
|
|
g_b2bjoin_pending = FALSE;
|
|
g_b2bjoin_callid = CC_NO_CALL_ID;
|
|
return;
|
|
}
|
|
}
|
|
if (dcb->call_id== g_b2bjoin_callid) {
|
|
/* If join is pending, pressing Join on the same call will cancel it */
|
|
g_b2bjoin_pending = FALSE;
|
|
g_b2bjoin_callid = CC_NO_CALL_ID;
|
|
/* Remove the check mark from it*/
|
|
cc_int_feature(CC_SRC_UI, CC_SRC_GSM, dcb->call_id,
|
|
dcb->line, CC_FEATURE_SELECT, NULL);
|
|
return;
|
|
}
|
|
feature_data.b2bjoin.b2bjoin_callid = dcb->call_id;
|
|
if( g_b2bjoin_callid == CC_NO_CALL_ID ){
|
|
feature_data.b2bjoin.b2bjoin_joincallid = dcb->call_id;
|
|
}
|
|
else{
|
|
feature_data.b2bjoin.b2bjoin_joincallid = g_b2bjoin_callid;
|
|
}
|
|
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, CC_FEATURE_B2B_JOIN, &feature_data);
|
|
|
|
} else {
|
|
if ((g_numofselected_calls == 1) && (dcb->selected)) {
|
|
/*
|
|
* If this is only one selected call, pressing
|
|
* Join on it will start a new join operation
|
|
*/
|
|
g_b2bjoin_pending = TRUE;
|
|
g_b2bjoin_callid = dcb->call_id;
|
|
fsm_display_use_line_or_join_to_complete();
|
|
return;
|
|
}
|
|
feature_data.b2bjoin.b2bjoin_callid = dcb->call_id;
|
|
feature_data.b2bjoin.b2bjoin_joincallid = dcb->call_id;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, CC_FEATURE_B2B_JOIN, &feature_data);
|
|
}
|
|
}
|
|
g_b2bjoin_pending = FALSE;
|
|
g_b2bjoin_callid = CC_NO_CALL_ID;
|
|
}
|
|
|
|
/*
|
|
* fsmdef_select_invoke
|
|
*
|
|
*
|
|
* Description:
|
|
* This function implements the join feature invocation.
|
|
*
|
|
* Parameters:
|
|
* fsmdef_dcb_t *dcb - dcb associated with this call
|
|
*
|
|
* Returns: None
|
|
*/
|
|
static void
|
|
fsmdef_select_invoke (fsmdef_dcb_t *dcb, cc_feature_data_t *select_data)
|
|
{
|
|
cc_feature_data_t feature_data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (dcb->select_pending) {
|
|
/* We have sent a select but have not received a
|
|
* response yet, so diallow further selects till then
|
|
*/
|
|
return;
|
|
}
|
|
if (select_data) {
|
|
feature_data.select.select = select_data->select.select;
|
|
} else {
|
|
if (dcb->selected == TRUE) {
|
|
feature_data.select.select = FALSE;
|
|
} else {
|
|
feature_data.select.select = TRUE;
|
|
}
|
|
}
|
|
if ((g_b2bjoin_pending) && (dcb->call_id== g_b2bjoin_callid)) {
|
|
/* If join is pending, pressing Select on the same call cancels join*/
|
|
g_b2bjoin_pending = FALSE;
|
|
g_b2bjoin_callid = CC_NO_CALL_ID;
|
|
}
|
|
dcb->select_pending = TRUE;;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id,
|
|
dcb->line, CC_FEATURE_SELECT, &feature_data);
|
|
}
|
|
|
|
/*
|
|
* handle_join_pending
|
|
*
|
|
*
|
|
* Description:
|
|
* This function checks if the join_pending flag
|
|
* needs to be cleared.due to the invocation of other features
|
|
*
|
|
* Parameters:
|
|
* fsmdef_dcb_t *dcb - dcb associated with this call
|
|
*
|
|
* Returns: None
|
|
*/
|
|
static void fsmdef_handle_join_pending (fsmdef_dcb_t *dcb)
|
|
{
|
|
if ((g_b2bjoin_pending) && (dcb->call_id == g_b2bjoin_callid)) {
|
|
/* If join is pending, invoking any other feature cancels join*/
|
|
g_b2bjoin_pending = FALSE;
|
|
g_b2bjoin_callid = CC_NO_CALL_ID;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* fsmdef_process_dialstring_for_callfwd
|
|
*
|
|
* Description:
|
|
* This function processes the dialstring event for callfwd.
|
|
*
|
|
* Parameters:
|
|
* sm_event_t *event - data associated with this call/dialstring
|
|
*
|
|
* Returns: sm_rcs_t
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_process_dialstring_for_callfwd (sm_event_t *event)
|
|
{
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/*
|
|
* For ccm case just return success.
|
|
* It will continue the call by sending INVITE
|
|
*/
|
|
return (SM_RC_SUCCESS);
|
|
}
|
|
|
|
|
|
/*
|
|
* fsmdef_process_cfwd_softkey_event
|
|
*
|
|
* Description:
|
|
* This function processes the cfwd softkey press event for callfwd.
|
|
*
|
|
* Parameters:
|
|
* sm_event_t *event - data associated with this call/dialstring
|
|
*
|
|
* Returns: sm_rcs_t
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_process_cfwd_softkey_event (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_t *msg = (cc_feature_t *) event->msg;
|
|
cc_features_t ftr_id = msg->feature_id;
|
|
cc_feature_data_t *ftr_data = &(msg->data);
|
|
cc_action_data_t cc_data;
|
|
int skMask[MAX_SOFT_KEYS];
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
// check if callfwd is active (i.e. set previously)
|
|
if (lsm_check_cfwd_all_ccm(dcb->line)) {
|
|
// call fsmdef_dialstring() version of the function specific
|
|
// to cfwd feature
|
|
return (fsmdef_cfwd_clear_ccm(fcb));
|
|
}
|
|
|
|
|
|
// code from here on is common to both modes/feature_id
|
|
if (fcb->state == FSMDEF_S_IDLE) {
|
|
|
|
/*
|
|
* The user is attempting to start a cfwdall call on a specific line:
|
|
* 1. need to place the connected call (if there is one) on hold,
|
|
* 2. clear any outgoing ringing calls,
|
|
* 3. initiate this call.
|
|
*/
|
|
if (fsmdef_wait_to_start_new_call(TRUE, CC_SRC_GSM, dcb->call_id, dcb->line, ftr_id, ftr_data))
|
|
{
|
|
dcb->active_feature = CC_FEATURE_NONE;
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
|
|
// go offhook and then move to dialing state to collect digits
|
|
//only contains <cfwdall-set> in initial NOTIFY to CUCM
|
|
fsmdef_notify_hook_event(fcb,CC_MSG_OFFHOOK,
|
|
ftr_data->newcall.global_call_id,
|
|
ftr_data->newcall.prim_call_id,
|
|
ftr_data->newcall.hold_resume_reason,
|
|
CC_MONITOR_NONE,
|
|
(ftr_id == CC_FEATURE_CFWD_ALL) ? CFWDALL_SET:CFWDALL_NONE);
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_OFFHOOK,
|
|
((cc_state_data_t *) (&(dcb->caller_id))));
|
|
|
|
fsmdef_call_cc_state_dialing(dcb, FALSE);
|
|
|
|
// stop the dial tone for parity with SCCP phone behavior
|
|
cc_data.tone.tone = VCM_INSIDE_DIAL_TONE;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE,
|
|
&cc_data);
|
|
|
|
// give different (zip-zip) tone rather than dial tone. zip_zip
|
|
// is not a permanent tone so it will end on it's own without
|
|
// us telling media termination to stop playing the tone.
|
|
cc_data.tone.tone = VCM_ZIP_ZIP;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_PLAY_TONE,
|
|
&cc_data);
|
|
|
|
// move to collect digit info state
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_COLLECT_INFO);
|
|
} else { // we must be in FSMDEF_S_COLLECT_INFO state
|
|
// stop the dial tone for parity with SCCP phone behavior
|
|
cc_data.tone.tone = VCM_INSIDE_DIAL_TONE;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_STOP_TONE,
|
|
&cc_data);
|
|
|
|
// give different (zip-zip) tone rather than dial tone. zip_zip
|
|
// is not a permanent tone so it will end on it's own without
|
|
// us telling media termination to stop playing the tone.
|
|
cc_data.tone.tone = VCM_ZIP_ZIP;
|
|
(void)cc_call_action(dcb->call_id, dcb->line, CC_ACTION_PLAY_TONE,
|
|
&cc_data);
|
|
}
|
|
ui_control_feature(dcb->line, dcb->call_id, skMask, 1, FALSE);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/**
|
|
* This function is a reduced version of the fsmdef_dialstring() function.
|
|
* It is customized for sending INVITE out to CCM to clear CFA state.
|
|
*
|
|
* @param[in] fcb The pointer to the fsm_fcb_t structure of this
|
|
* call chain.
|
|
*
|
|
* @pre (fcb not_eq NULL)
|
|
*
|
|
* @return sm_rsc_t indicates whether the execution of
|
|
* next statmachine.
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_cfwd_clear_ccm (fsm_fcb_t *fcb)
|
|
{
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_causes_t cause;
|
|
cc_msgbody_info_t msg_body;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
// to clear CFA in CCM mode... only put service uri... no dialstring.
|
|
fsmdef_append_dialstring_to_feature_uri(dcb, NULL);
|
|
|
|
// From here on all we need to do is send INVITE out.
|
|
// Since, its not a real call there is no need to update UI etc.
|
|
// Response to this call will be 5xx so it will be released by the SIP stack.
|
|
cause = gsmsdp_create_local_sdp(dcb, FALSE, TRUE, TRUE, TRUE, TRUE);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
|
|
/* Build SDP for sending out */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
return (fsmdef_release(fcb, cause, dcb->send_release));
|
|
}
|
|
cc_int_setup(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), dcb->alert_info, VCM_INSIDE_RING,
|
|
VCM_INSIDE_DIAL_TONE, NULL, NULL, FALSE, NULL, &msg_body);
|
|
|
|
/*
|
|
* Since we are sending setup to UI we will also have to send
|
|
* release to it to for sip stack to clean up the call
|
|
*/
|
|
dcb->send_release = TRUE;
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_SETUP);
|
|
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_CALL_SENT);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_append_dialstring_to_feature_uri
|
|
*
|
|
* Description:
|
|
* This function appends dialstring to feature URI.
|
|
*
|
|
* Parameters:
|
|
* fsmdef_dcb_t *dcb, char *dialstring
|
|
*
|
|
* Return Value: none
|
|
*
|
|
* Note: TNP specific implementation.
|
|
*/
|
|
static void
|
|
fsmdef_append_dialstring_to_feature_uri (fsmdef_dcb_t *dcb,
|
|
const char *dialstring)
|
|
{
|
|
char service_uri[MAX_URL_LENGTH];
|
|
|
|
service_uri[0] = '\0';
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (dcb == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_INVALID_DCB),
|
|
__FUNCTION__);
|
|
return;
|
|
}
|
|
|
|
switch (dcb->active_feature) {
|
|
case CC_FEATURE_CFWD_ALL:
|
|
config_get_string(CFGID_CALL_FORWARD_URI, service_uri,
|
|
sizeof(service_uri));
|
|
break;
|
|
default:
|
|
// do nothing
|
|
break;
|
|
}
|
|
|
|
if (service_uri[0] != NUL) {
|
|
dcb->caller_id.called_number =
|
|
strlib_update(dcb->caller_id.called_number, service_uri);
|
|
if (dialstring && dialstring[0]) {
|
|
dcb->caller_id.called_number =
|
|
strlib_append(dcb->caller_id.called_number, "-");
|
|
dcb->caller_id.called_number =
|
|
strlib_append(dcb->caller_id.called_number, dialstring);
|
|
}
|
|
} else {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Configured Feature/Service URI Not Found For Feature[%d]\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_append_dialstring_to_feature_uri"), (int)dcb->active_feature);
|
|
|
|
if (dialstring && dialstring[0]) {
|
|
dcb->caller_id.called_number =
|
|
strlib_update(dcb->caller_id.called_number, dialstring);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* fsmdef_is_feature_uri_configured
|
|
*
|
|
* Description:
|
|
* This function checks is a feature URI is configured.
|
|
*
|
|
* Parameters:
|
|
* cc_features_t ftr_id
|
|
*
|
|
* Return Value: TRUE or FALSE
|
|
*
|
|
* Note: TNP specific implementation.
|
|
*/
|
|
static boolean
|
|
fsmdef_is_feature_uri_configured (cc_features_t ftr_id)
|
|
{
|
|
char service_uri[MAX_URL_LENGTH];
|
|
|
|
service_uri[0] = '\0';
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
switch (ftr_id) {
|
|
case CC_FEATURE_CFWD_ALL:
|
|
config_get_string(CFGID_CALL_FORWARD_URI, service_uri,
|
|
sizeof(service_uri));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (service_uri[0] != NUL) {
|
|
return TRUE;
|
|
}
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Configured Feature/Service URI Not Found For Feature[%d]\n", DEB_F_PREFIX_ARGS(FSM, "fsmdef_is_feature_uri_configured"), (int)ftr_id);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_if_ok_for_dial_call
|
|
*
|
|
* Description:
|
|
* This function checks if there is a call in collecting info state
|
|
* on a given line.
|
|
*
|
|
* Parameters:
|
|
* line_t line
|
|
*
|
|
* Returns: TRUE or FALSE
|
|
*/
|
|
boolean
|
|
fsmdef_check_if_ok_for_dial_call (line_t line)
|
|
{
|
|
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (((dcb->line == line) && (dcb->call_id != CC_NO_CALL_ID)) &&
|
|
(dcb->fcb != NULL) &&
|
|
((dcb->fcb->state == FSMDEF_S_COLLECT_INFO) ||
|
|
(dcb->fcb->state == FSMDEF_S_CALL_SENT) ||
|
|
(dcb->fcb->state == FSMDEF_S_OUTGOING_PROCEEDING) ||
|
|
(dcb->fcb->state == FSMDEF_S_KPML_COLLECT_INFO))) {
|
|
return (TRUE);
|
|
}
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_if_ok_to_ans_call
|
|
*
|
|
* Description:
|
|
* Checks if given call is in ringing state.
|
|
*
|
|
* Parameters:
|
|
* line
|
|
* call_id
|
|
*
|
|
* Returns: TRUE or FALSE
|
|
*/
|
|
|
|
boolean
|
|
fsmdef_check_if_ok_to_ans_call (line_t line, callid_t call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
|
|
if (dcb == NULL) {
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((dcb->line != line) || ((dcb->fcb != NULL) && (dcb->fcb->state != FSMDEF_S_INCOMING_ALERTING))) {
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_if_ok_to_hold_call
|
|
*
|
|
* Description:
|
|
* Checks if the requested call is in connected state.
|
|
*
|
|
* Parameters:
|
|
* line
|
|
* call_id
|
|
*
|
|
* Returns: TRUE - if there is a connected or resume pending call
|
|
* FALSE - if the call is not in above state
|
|
*/
|
|
boolean
|
|
fsmdef_check_if_ok_to_hold_call (line_t line, callid_t call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
|
|
if (dcb == NULL) {
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((dcb->line != line) ||
|
|
((dcb->fcb != NULL) &&
|
|
((dcb->fcb->state != FSMDEF_S_CONNECTED) &&
|
|
(dcb->fcb->state != FSMDEF_S_CONNECTED_MEDIA_PEND) &&
|
|
(dcb->fcb->state != FSMDEF_S_RESUME_PENDING)))) {
|
|
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_if_ok_to_resume_call
|
|
*
|
|
* Description:
|
|
* Checks if the requested call is in held state.
|
|
*
|
|
* Parameters:
|
|
* line
|
|
* call_id
|
|
*
|
|
* Returns: TRUE or FALSE
|
|
*/
|
|
boolean
|
|
fsmdef_check_if_ok_to_resume_call (line_t line, callid_t call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
|
|
if (dcb == NULL) {
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((dcb->line != line) || ((dcb->fcb != NULL) &&(dcb->fcb->state != FSMDEF_S_HOLDING))) {
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_if_ok_to_run_feature
|
|
*
|
|
* Description:
|
|
* Checks if it is ok to run features on the call.
|
|
*
|
|
* Parameters:
|
|
* line
|
|
* call_id
|
|
*
|
|
* Returns: TRUE or FALSE
|
|
*/
|
|
boolean
|
|
fsmdef_check_if_ok_to_run_feature (line_t line, callid_t call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
|
|
if (dcb == NULL) {
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((dcb->line != line) ||
|
|
((dcb->fcb != NULL) &&
|
|
(dcb->fcb->state != FSMDEF_S_CONNECTED) &&
|
|
(dcb->fcb->state != FSMDEF_S_CONNECTED_MEDIA_PEND))) {
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_if_ok_to_monitor_update_call
|
|
*
|
|
* Description:
|
|
* Checks if it is ok to update the monitoring call.
|
|
*
|
|
* Parameters:
|
|
* line
|
|
* call_id
|
|
*
|
|
* Returns: TRUE or FALSE
|
|
*/
|
|
boolean
|
|
fsmdef_check_if_ok_to_monitor_update_call (line_t line, callid_t call_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
dcb = fsmdef_get_dcb_by_call_id(call_id);
|
|
|
|
if (dcb == NULL) {
|
|
return (FALSE);
|
|
}
|
|
|
|
if ((dcb->line != line) ||
|
|
((dcb->fcb != NULL) &&
|
|
(dcb->fcb->state != FSMDEF_S_CONNECTED))) {
|
|
return (FALSE);
|
|
}
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_set_call_info_cc_call_state
|
|
*
|
|
* Description:
|
|
* Sets the called number to readable localized values
|
|
* if it is service URI based feature and invokes cc_call_state()
|
|
*
|
|
* Parameters:
|
|
* dcb
|
|
* state
|
|
*
|
|
* Returns: none
|
|
*/
|
|
static void
|
|
fsmdef_set_call_info_cc_call_state (fsmdef_dcb_t *dcb, cc_states_t state, cc_causes_t cause)
|
|
{
|
|
cc_state_data_t temp_data;
|
|
char tmp_str[CALL_BUBBLE_STR_MAX_LEN];
|
|
int rc = CPR_FAILURE;
|
|
|
|
tmp_str[0] = '\0';
|
|
|
|
switch (dcb->active_feature) {
|
|
case CC_FEATURE_CFWD_ALL:
|
|
rc = platGetPhraseText(STR_INDEX_CALL_FORWARD,
|
|
(char *) tmp_str,
|
|
CALL_BUBBLE_STR_MAX_LEN);
|
|
break;
|
|
default:
|
|
rc = CPR_FAILURE;
|
|
break;
|
|
}
|
|
|
|
switch (state) {
|
|
|
|
case CC_STATE_DIALING_COMPLETED:
|
|
temp_data.dialing_completed.caller_id = dcb->caller_id;
|
|
break;
|
|
|
|
case CC_STATE_CALL_SENT:
|
|
temp_data.call_sent.caller_id = dcb->caller_id;
|
|
break;
|
|
|
|
case CC_STATE_CALL_FAILED:
|
|
temp_data.call_failed.caller_id = dcb->caller_id;
|
|
temp_data.call_failed.cause = cause;
|
|
break;
|
|
|
|
default:
|
|
/* Set the call id for other states */
|
|
temp_data.offhook.caller_id = dcb->caller_id;
|
|
break;
|
|
}
|
|
|
|
if ((rc == CPR_SUCCESS) && strlen(tmp_str) > 0) {
|
|
temp_data.offhook.caller_id.called_number = tmp_str;
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, state, &temp_data);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_get_dcb_by_call_instance_id
|
|
*
|
|
* Description:
|
|
* This function returns fsmdef DCB that has a match for a given call
|
|
* instance ID of a given line.
|
|
*
|
|
* Parameters:
|
|
* line - the dn line
|
|
* call_instance_id - for call instance ID.
|
|
*
|
|
* Returns: pointer to fsmdef_dcb_t if a matching dcb is found otherwise
|
|
* returns NULL
|
|
*/
|
|
fsmdef_dcb_t *
|
|
fsmdef_get_dcb_by_call_instance_id (line_t line, uint16_t call_instance_id)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if ((dcb->caller_id.call_instance_id == call_instance_id) &&
|
|
(dcb->line == line)) {
|
|
return (dcb);
|
|
}
|
|
}
|
|
return (NULL);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_ev_join
|
|
*
|
|
* Description:
|
|
* The fsmdef_ev_join function sends an offhook event
|
|
* to the barging call and sets its state to FSMDEF_S_JOINING.
|
|
*
|
|
* Parameters:
|
|
* data - pointer to the cc_feature_data_t
|
|
*
|
|
* Returns:
|
|
* none
|
|
*/
|
|
static void
|
|
fsmdef_ev_join (cc_feature_data_t *data)
|
|
{
|
|
fsm_fcb_t *fcb = NULL;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
fcb = fsm_get_fcb_by_call_id_and_type(data->newcall.join.join_call_id,
|
|
FSM_TYPE_DEF);
|
|
if (fcb) {
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
|
|
cc_int_offhook(CC_SRC_GSM, CC_SRC_GSM, CC_NO_CALL_ID, CC_REASON_NONE,
|
|
dcb->call_id, dcb->line, NULL, CC_MONITOR_NONE,CFWDALL_NONE);
|
|
fsm_change_state(fcb, __LINE__, FSMDEF_S_JOINING);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* fsmdef_ev_joining_connected_ack
|
|
*
|
|
* Description:
|
|
* The fsmdef_ev_joining_connected_ack negotiates the sdp in the
|
|
* ack(if there is one) and sets the fsm/ccapi state to connected
|
|
*
|
|
* Parameters:
|
|
* event - pointer to the sm_event_t
|
|
*
|
|
* Returns:
|
|
* sm_rcs_t value
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_joining_connected_ack (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_connected_ack_t *msg = (cc_connected_ack_t *) event->msg;
|
|
cc_causes_t cause;
|
|
fsmcnf_ccb_t *ccb;
|
|
fsm_fcb_t *join_target_fcb;
|
|
cc_feature_data_t data;
|
|
cc_uint32_t major_sis_ver = SIS_PROTOCOL_MAJOR_VERSION_SEADRAGON;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
/* Check the remote SDP. The far end may not have included the SDP in an
|
|
* earlier message, which means that the SDP must be in this message.
|
|
*/
|
|
if (dcb->remote_sdp_in_ack == TRUE) {
|
|
cause = gsmsdp_negotiate_answer_sdp(fcb, &msg->msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
data.endcall.cause = cause;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
|
|
CC_FEATURE_END_CALL, &data);
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
platGetSISProtocolVer(&major_sis_ver, NULL, NULL,NULL);
|
|
|
|
ccb = fsmcnf_get_ccb_by_call_id(dcb->call_id);
|
|
|
|
if (ccb) {
|
|
join_target_fcb = fsm_get_fcb_by_call_id_and_type(ccb->cnf_call_id,
|
|
FSM_TYPE_CNF);
|
|
if ((gsmsdp_is_media_encrypted(dcb) == FALSE) &&
|
|
(gsmsdp_is_media_encrypted((join_target_fcb!= NULL)?join_target_fcb->dcb:NULL) == TRUE) &&
|
|
(major_sis_ver < SIS_PROTOCOL_MAJOR_VERSION_MUSTER)) {
|
|
/*
|
|
* Target Call was secure, a non-secure is trying to Barge/Monitor,
|
|
* fail it
|
|
*/
|
|
data.endcall.cause = CC_CAUSE_SECURITY_FAILURE;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
|
|
CC_FEATURE_END_CALL, &data);
|
|
return (SM_RC_END);
|
|
}
|
|
}
|
|
|
|
cc_call_state(dcb->call_id, dcb->line, CC_STATE_CONNECTED,
|
|
(cc_state_data_t *) &(dcb->caller_id));
|
|
/*
|
|
* If DSP is not able to start rx/tx channels, release the call
|
|
*/
|
|
if (dcb->dsp_out_of_resources == TRUE) {
|
|
data.endcall.cause = CC_CAUSE_NO_MEDIA;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
|
|
CC_FEATURE_END_CALL, &data);
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
if (ccb) {
|
|
/*
|
|
* Bridge the conference legs for the join call, even though
|
|
* it's done here, ccb should only be accessed from fsmcnf
|
|
*/
|
|
ccb->active = TRUE;
|
|
ccb->bridged = TRUE;
|
|
}
|
|
|
|
/*
|
|
* Handle media capability changes if there is before transition to
|
|
* connected state.
|
|
*/
|
|
return(fsmdef_transition_to_connected(fcb));
|
|
}
|
|
|
|
/*
|
|
* fsmdef_ev_joining_offhook
|
|
*
|
|
* Description:
|
|
* The fsmdef_ev_joining_offhook creates the local sdp
|
|
* for the sip stack to send 200 Ok out
|
|
*
|
|
* Parameters:
|
|
* event - pointer to the sm_event_t
|
|
*
|
|
* Returns:
|
|
* sm_rcs_t value
|
|
*/
|
|
static sm_rcs_t
|
|
fsmdef_ev_joining_offhook (sm_event_t *event)
|
|
{
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_causes_t cause;
|
|
cc_msgbody_info_t msg_body;
|
|
cc_feature_data_t data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
/* Build our response SDP to include in the connected */
|
|
cause = gsmsdp_encode_sdp_and_update_version(dcb, &msg_body);
|
|
if (cause != CC_CAUSE_OK) {
|
|
FSM_DEBUG_SM(get_debug_string(FSM_DBG_SDP_BUILD_ERR));
|
|
memset(&data, 0, sizeof(data));
|
|
data.endcall.cause = cause;
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, dcb->call_id, dcb->line,
|
|
CC_FEATURE_END_CALL, &data);
|
|
return (SM_RC_END);
|
|
}
|
|
cc_int_connected(CC_SRC_GSM, CC_SRC_SIP, dcb->call_id, dcb->line,
|
|
&(dcb->caller_id), NULL, &msg_body);
|
|
|
|
FSM_SET_FLAGS(dcb->msgs_sent, FSMDEF_MSG_CONNECTED);
|
|
|
|
return (SM_RC_END);
|
|
}
|
|
|
|
/*
|
|
* fsmdef_extract_join_target
|
|
*
|
|
* Description:
|
|
* The fsmdef_extract_join_target extract join target call id from
|
|
* the setup message and sends a JOIN event to that call
|
|
*
|
|
* Parameters:
|
|
* event - pointer to the sm_event_t
|
|
*
|
|
* Returns:
|
|
* TRUE or FALSE
|
|
*/
|
|
static boolean
|
|
fsmdef_extract_join_target (sm_event_t *event)
|
|
{
|
|
static const char fname[] = "fsmdef_extract_join_target";
|
|
fsm_fcb_t *fcb = (fsm_fcb_t *) event->data;
|
|
cc_setup_t *msg = (cc_setup_t *) event->msg;
|
|
callid_t call_id = msg->call_id;
|
|
line_t line = msg->line;
|
|
fsmdef_dcb_t *dcb;
|
|
fsmdef_dcb_t *join_dcb;
|
|
cc_feature_data_t data;
|
|
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
dcb = fcb->dcb;
|
|
|
|
if ((msg->call_info.type == CC_FEAT_MONITOR) ||
|
|
(dcb->session == WHISPER_COACHING)) {
|
|
|
|
/* For now, null out the ui id */
|
|
lsm_set_ui_id(dcb->call_id, CC_NO_CALL_ID);
|
|
join_dcb =
|
|
fsmdef_get_dcb_by_call_id(msg->call_info.data.join.join_call_id);
|
|
if (join_dcb) {
|
|
dcb->group_id = join_dcb->group_id;
|
|
memset(&data, 0, sizeof(data));
|
|
data.newcall.join.join_call_id = call_id;
|
|
|
|
if (dcb->session == WHISPER_COACHING) {
|
|
data.newcall.cause = CC_CAUSE_MONITOR;
|
|
} else if (msg->call_info.type == CC_FEAT_MONITOR) {
|
|
data.newcall.cause = CC_CAUSE_MONITOR;
|
|
// set the session leg of this dcb to monitor
|
|
dcb->session = MONITOR;
|
|
}
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX" dcb-session type is = %s \n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname),
|
|
dcb->session == WHISPER_COACHING ? "WHISPER_COACHING" :
|
|
dcb->session == MONITOR ? "MONITOR" : "PRIMARY");
|
|
|
|
cc_int_feature(CC_SRC_GSM, CC_SRC_GSM, join_dcb->call_id, line,
|
|
CC_FEATURE_JOIN, &data);
|
|
} else {
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Unable to find join target dcb\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname));
|
|
return (TRUE);
|
|
}
|
|
} else {
|
|
FSM_DEBUG_SM(DEB_L_C_F_PREFIX"Unable to find join target call information\n",
|
|
DEB_L_C_F_PREFIX_ARGS(FSM, dcb->line, dcb->call_id, fname));
|
|
return (TRUE);
|
|
}
|
|
return (FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
* fsmdef_ev_notify_feature
|
|
*
|
|
* Description:
|
|
* Handles NOTIFY event in different states.
|
|
*
|
|
* Parameters:
|
|
* msg - message pointer
|
|
* dcb - pointer to the fsmdef_dcb_t
|
|
*
|
|
* Returns:
|
|
* none
|
|
*/
|
|
static void
|
|
fsmdef_ev_notify_feature (cc_feature_t *msg, fsmdef_dcb_t *dcb)
|
|
{
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
}
|
|
|
|
/*
|
|
* fsmdef_notify_hook_event
|
|
*
|
|
* Description:
|
|
* Send offhook/onhook events to SIP (DialogManager) task.
|
|
*
|
|
* Parameters:
|
|
* dcb - pointer to the fsmdef_dcb_t
|
|
*
|
|
* Returns:
|
|
* none
|
|
*/
|
|
static void
|
|
fsmdef_notify_hook_event (fsm_fcb_t *fcb, cc_msgs_t msg, char *global_call_id,
|
|
callid_t prim_call_id,
|
|
cc_hold_resume_reason_e consult_reason,
|
|
monitor_mode_t monitor_mode,
|
|
cfwdall_mode_t cfwdall_mode)
|
|
{
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Entered.\n", DEB_F_PREFIX_ARGS(FSM, __FUNCTION__));
|
|
|
|
if (msg == CC_MSG_OFFHOOK) {
|
|
cc_int_offhook(CC_SRC_GSM, CC_SRC_SIP, prim_call_id, consult_reason,
|
|
fcb->dcb->call_id, fcb->dcb->line,
|
|
global_call_id, monitor_mode,cfwdall_mode);
|
|
} else if (msg == CC_MSG_ONHOOK) {
|
|
cc_int_onhook(CC_SRC_GSM, CC_SRC_SIP, prim_call_id,
|
|
consult_reason, fcb->dcb->call_id, fcb->dcb->line, FALSE, FALSE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* fsmdef_update_callinfo_security_status
|
|
*
|
|
* Description:
|
|
* The fsmdef_update_callinfo_security_status function updates the call
|
|
* information that applies to TNP platform.
|
|
*
|
|
* Parameters:
|
|
* dcb - pointer to the fsmdef_dcb_t
|
|
* call_info - pointer to the cc_feature_data_call_info_t
|
|
*
|
|
* Returns:
|
|
* none
|
|
*/
|
|
static void
|
|
fsmdef_update_callinfo_security_status (fsmdef_dcb_t *dcb,
|
|
cc_feature_data_call_info_t *call_info)
|
|
{
|
|
/*
|
|
* Update security information
|
|
*/
|
|
if (call_info->feature_flag & CC_SECURITY) {
|
|
if (sip_regmgr_get_sec_level(dcb->line) != AUTHENTICATED &&
|
|
sip_regmgr_get_sec_level(dcb->line) != ENCRYPTED ) {
|
|
// Signaling security can't be trusted downgrade security level to NOT_AUTHENTICATED
|
|
call_info->security = CC_SECURITY_NOT_AUTHENTICATED;
|
|
}
|
|
|
|
if (dcb->security != call_info->security) {
|
|
FSM_SET_SECURITY_STATUS(dcb, call_info->security);
|
|
if ( call_info->security == CC_SECURITY_ENCRYPTED )
|
|
ui_update_call_security(dcb->line, lsm_get_ui_id(dcb->call_id), CC_SECURITY_ENCRYPTED);
|
|
else
|
|
ui_update_call_security(dcb->line, lsm_get_ui_id(dcb->call_id), CC_SECURITY_UNKNOWN);
|
|
|
|
dcb->ui_update_required = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* fsmdef_check_retain_fwd_info_state
|
|
*
|
|
* Description: This function checks the "Retain Forward Information" config
|
|
* parameter (only for TNP). It returns TRUE if forward info is
|
|
* to be retained; FALSE otherwise. This config parameter is
|
|
* is used to change call bubble display from "Forward <DN>" to
|
|
* "From <DN>" when an incoming call is answered AND the config
|
|
* parameter value is set to TRUE. For non-TNP, this function
|
|
* will always return FALSE (i.e. not to retain fwd info).
|
|
*
|
|
* Parameters: none
|
|
*
|
|
* Return Value: TRUE or FALSE
|
|
*/
|
|
boolean
|
|
fsmdef_check_retain_fwd_info_state (void)
|
|
{
|
|
int retain_fwd_info_cfg = 0; /* default to Disabled (or 0) for no-op */
|
|
|
|
config_get_value(CFGID_RETAIN_FORWARD_INFORMATION,
|
|
&retain_fwd_info_cfg,
|
|
sizeof(retain_fwd_info_cfg));
|
|
|
|
if (!retain_fwd_info_cfg) {
|
|
return (FALSE); /* do NOT retain forward information */
|
|
} else {
|
|
return (TRUE); /* retain forward information */
|
|
}
|
|
}
|
|
|
|
void
|
|
fsmdef_init (void)
|
|
{
|
|
static const char fname[] = "fsmdef_init";
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
|
|
/*
|
|
* Initialize the dcbs.
|
|
*/
|
|
fsmdef_dcbs = (fsmdef_dcb_t *)
|
|
cpr_calloc(FSMDEF_MAX_DCBS, sizeof(fsmdef_dcb_t));
|
|
if (fsmdef_dcbs == NULL) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"cpr_calloc returned NULL\n",
|
|
DEB_F_PREFIX_ARGS(FSM, fname));
|
|
return;
|
|
}
|
|
|
|
/* Create free media structure list */
|
|
if (!gsmsdp_create_free_media_list()) {
|
|
FSM_DEBUG_SM(DEB_F_PREFIX"Unable to create free media list\n",
|
|
DEB_F_PREFIX_ARGS(FSM, fname));
|
|
return;
|
|
}
|
|
|
|
DEF_DEBUG(DEB_F_PREFIX"Disabling mass registration print", DEB_F_PREFIX_ARGS(SIP_REG, fname));
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
fsmdef_init_dcb(dcb, CC_NO_CALL_ID, FSMDEF_CALL_TYPE_NONE,
|
|
FSMDEF_NO_NUMBER, LSM_NO_LINE, NULL);
|
|
/*
|
|
* Allocate ringback delay timer for each dcb
|
|
*/
|
|
dcb->ringback_delay_tmr = cprCreateTimer("Ringback Delay",
|
|
GSM_RINGBACK_DELAY_TIMER,
|
|
TIMER_EXPIRATION,
|
|
gsm_msg_queue);
|
|
if (dcb->ringback_delay_tmr == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Ringback Delay");
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Allocate auto answer timer for each dcb
|
|
*/
|
|
dcb->autoAnswerTimer = cprCreateTimer("Auto Answer",
|
|
GSM_AUTOANSWER_TIMER,
|
|
TIMER_EXPIRATION,
|
|
gsm_msg_queue);
|
|
if (dcb->autoAnswerTimer == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Auto Answer");
|
|
(void)cprDestroyTimer(dcb->ringback_delay_tmr);
|
|
dcb->ringback_delay_tmr = NULL;
|
|
return;
|
|
}
|
|
dcb->revertTimer = cprCreateTimer("Call Reversion",
|
|
GSM_REVERSION_TIMER,
|
|
TIMER_EXPIRATION,
|
|
gsm_msg_queue);
|
|
dcb->reversionInterval = -1;
|
|
if (dcb->revertTimer == NULL) {
|
|
FSM_DEBUG_SM(get_debug_string(FSMDEF_DBG_TMR_CREATE_FAILED),
|
|
dcb->call_id, dcb->line, fname, "Hold Revertion");
|
|
|
|
(void)cprDestroyTimer(dcb->ringback_delay_tmr);
|
|
dcb->ringback_delay_tmr = NULL;
|
|
(void)cprDestroyTimer(dcb->autoAnswerTimer);
|
|
dcb->autoAnswerTimer = NULL;
|
|
return;
|
|
}
|
|
if (dcb == fsmdef_dcbs) {
|
|
g_disable_mass_reg_debug_print = TRUE;
|
|
}
|
|
}
|
|
g_disable_mass_reg_debug_print = FALSE;
|
|
|
|
/*
|
|
* Initialize the state/event table.
|
|
*/
|
|
fsmdef_sm_table.min_state = FSMDEF_S_MIN;
|
|
fsmdef_sm_table.max_state = FSMDEF_S_MAX;
|
|
fsmdef_sm_table.min_event = CC_MSG_MIN;
|
|
fsmdef_sm_table.max_event = CC_MSG_MAX;
|
|
fsmdef_sm_table.table = (&(fsmdef_function_table[0][0]));
|
|
}
|
|
|
|
void
|
|
fsmdef_shutdown (void)
|
|
{
|
|
fsmdef_dcb_t *dcb;
|
|
|
|
FSM_FOR_ALL_CBS(dcb, fsmdef_dcbs, FSMDEF_MAX_DCBS) {
|
|
if (dcb->req_pending_tmr) {
|
|
(void)cprDestroyTimer(dcb->req_pending_tmr);
|
|
}
|
|
if (dcb->err_onhook_tmr) {
|
|
(void)cprDestroyTimer(dcb->err_onhook_tmr);
|
|
}
|
|
if (dcb->ringback_delay_tmr) {
|
|
(void)cprDestroyTimer(dcb->ringback_delay_tmr);
|
|
}
|
|
if (dcb->autoAnswerTimer) {
|
|
(void)cprDestroyTimer(dcb->autoAnswerTimer);
|
|
}
|
|
if (dcb->revertTimer) {
|
|
(void)cprDestroyTimer(dcb->revertTimer);
|
|
}
|
|
|
|
/* clean media list */
|
|
gsmsdp_clean_media_list(dcb);
|
|
}
|
|
|
|
/* destroy free media structure list */
|
|
gsmsdp_destroy_free_media_list();
|
|
|
|
cpr_free(fsmdef_dcbs);
|
|
fsmdef_dcbs = NULL;
|
|
}
|
|
|
|
static void
|
|
fsmdef_update_calltype (fsm_fcb_t *fcb, cc_feature_t *msg) {
|
|
fsmdef_dcb_t *dcb = fcb->dcb;
|
|
cc_feature_data_t *feat_data = &(msg->data);
|
|
cc_caller_id_t *caller_id;
|
|
|
|
if (msg->data_valid == FALSE) {
|
|
/* No data to use for update. Just ignore the event. */
|
|
return;
|
|
}
|
|
|
|
if (feat_data->call_info.feature_flag & CC_CALLER_ID) {
|
|
caller_id = &feat_data->call_info.caller_id;
|
|
if (caller_id->call_type == CC_CALL_FORWARDED) {
|
|
if (fsmdef_check_retain_fwd_info_state()) {
|
|
dcb->call_type = FSMDEF_CALL_TYPE_FORWARD;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|