From 4240526ce325f90fbd350ca19b6942e23f56d738 Mon Sep 17 00:00:00 2001 From: Anthony Minessale Date: Wed, 5 Jun 2013 11:19:44 -0500 Subject: [PATCH] add some device-state mechinism to FS to allow tracking of device-specific states where they may have more than one call from the same device --- libs/esl/src/esl_event.c | 2 + libs/esl/src/include/esl_event.h | 2 + src/include/switch_channel.h | 11 +- src/include/switch_core.h | 45 ++- src/include/switch_types.h | 16 +- src/mod/applications/mod_skel/mod_skel.c | 25 ++ src/mod/endpoints/mod_sofia/mod_sofia.c | 9 +- src/switch_channel.c | 461 ++++++++++++++++++++++- src/switch_core.c | 2 + src/switch_core_state_machine.c | 2 + src/switch_event.c | 2 + src/switch_ivr_originate.c | 5 + 12 files changed, 565 insertions(+), 17 deletions(-) diff --git a/libs/esl/src/esl_event.c b/libs/esl/src/esl_event.c index 73a41b7868..00c5410000 100644 --- a/libs/esl/src/esl_event.c +++ b/libs/esl/src/esl_event.c @@ -143,6 +143,8 @@ static const char *EVENT_NAMES[] = { "CONFERENCE_DATA", "CALL_SETUP_REQ", "CALL_SETUP_RESULT", + "CALL_DETAIL", + "DEVICE_STATE", "ALL" }; diff --git a/libs/esl/src/include/esl_event.h b/libs/esl/src/include/esl_event.h index 1b6e6e29b4..2b4197be41 100644 --- a/libs/esl/src/include/esl_event.h +++ b/libs/esl/src/include/esl_event.h @@ -132,6 +132,8 @@ typedef enum { ESL_EVENT_CONFERENCE_DATA, ESL_EVENT_CALL_SETUP_REQ, ESL_EVENT_CALL_SETUP_RESULT, + ESL_EVENT_CALL_DETAIL, + ESL_EVENT_DEVICE_STATE, ESL_EVENT_ALL } esl_event_types_t; diff --git a/src/include/switch_channel.h b/src/include/switch_channel.h index 219663b0e0..c35994d8af 100644 --- a/src/include/switch_channel.h +++ b/src/include/switch_channel.h @@ -659,7 +659,16 @@ SWITCH_DECLARE(void) switch_channel_state_thread_lock(switch_channel_t *channel) SWITCH_DECLARE(void) switch_channel_state_thread_unlock(switch_channel_t *channel); SWITCH_DECLARE(switch_status_t) switch_channel_state_thread_trylock(switch_channel_t *channel); SWITCH_DECLARE(void) switch_channel_handle_cause(switch_channel_t *channel, switch_call_cause_t cause); - +SWITCH_DECLARE(void) switch_channel_global_init(switch_memory_pool_t *pool); +SWITCH_DECLARE(void) switch_channel_global_uninit(void); +SWITCH_DECLARE(const char *) switch_channel_set_device_id(switch_channel_t *channel, const char *device_id); +SWITCH_DECLARE(void) switch_channel_clear_device_record(switch_channel_t *channel); +SWITCH_DECLARE(switch_device_record_t *) switch_channel_get_device_record(switch_channel_t *channel); +SWITCH_DECLARE(void) switch_channel_release_device_record(switch_device_record_t **dcdrp); +SWITCH_DECLARE(switch_status_t) switch_channel_bind_device_state_handler(switch_device_state_function_t function, void *user_data); +SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switch_device_state_function_t function); +SWITCH_DECLARE(const char *) switch_channel_device_state2str(switch_device_state_t device_state); + SWITCH_END_EXTERN_C #endif /* For Emacs: diff --git a/src/include/switch_core.h b/src/include/switch_core.h index 55ffb96cb2..2e98224273 100644 --- a/src/include/switch_core.h +++ b/src/include/switch_core.h @@ -61,6 +61,11 @@ struct switch_app_log { struct switch_app_log *next; }; +typedef struct switch_thread_data_s { + switch_thread_start_t func; + void *obj; + int alloc; +} switch_thread_data_t; typedef struct switch_hold_record_s { switch_time_t on; @@ -69,12 +74,42 @@ typedef struct switch_hold_record_s { struct switch_hold_record_s *next; } switch_hold_record_t; +typedef struct device_uuid_node_s { + char *uuid; + switch_xml_t xml_cdr; + switch_event_t *event; + switch_channel_callstate_t callstate; + switch_hold_record_t *hold_record; + switch_caller_profile_t *hup_profile; + struct switch_device_record_s *parent; + struct device_uuid_node_s *next; +} switch_device_node_t; -typedef struct switch_thread_data_s { - switch_thread_start_t func; - void *obj; - int alloc; -} switch_thread_data_t; +typedef struct switch_device_stats_s { + uint32_t total; + uint32_t offhook; + uint32_t active; + uint32_t held; + uint32_t hup; +} switch_device_stats_t; + + +typedef struct switch_device_record_s { + char *device_id; + char *uuid; + int refs; + switch_device_stats_t stats; + switch_device_state_t state; + switch_device_state_t last_state; + switch_time_t active_start; + switch_time_t active_stop; + struct device_uuid_node_s *uuid_list; + struct device_uuid_node_s *uuid_tail; + switch_mutex_t *mutex; + switch_memory_pool_t *pool; +} switch_device_record_t; + +typedef void(*switch_device_state_function_t)(switch_core_session_t *session, switch_channel_callstate_t callstate, switch_device_record_t *drec); #define DTLS_SRTP_FNAME "dtls-srtp" diff --git a/src/include/switch_types.h b/src/include/switch_types.h index 6db25349fd..0c9d0b0335 100644 --- a/src/include/switch_types.h +++ b/src/include/switch_types.h @@ -1111,9 +1111,19 @@ typedef enum { CCS_EARLY, CCS_ACTIVE, CCS_HELD, - CCS_HANGUP + CCS_HANGUP, + CCS_UNHOLD } switch_channel_callstate_t; +typedef enum { + SDS_DOWN, + SDS_ACTIVE, + SDS_ACTIVE_MULTI, + SDS_HELD, + SDS_HANGUP +} switch_device_state_t; + + /*! \enum switch_channel_state_t \brief Channel States (these are the defaults, CS_SOFT_EXECUTE, CS_EXCHANGE_MEDIA, and CS_CONSUME_MEDIA are often overridden by specific apps) @@ -1279,6 +1289,8 @@ typedef enum { CF_ZRTP_PASSTHRU, CF_ZRTP_HASH, CF_CHANNEL_SWAP, + CF_DEVICE_LEG, + CF_FINAL_DEVICE_LEG, CF_PICKUP, CF_CONFIRM_BLIND_TRANSFER, CF_NO_PRESENCE, @@ -1751,6 +1763,8 @@ typedef enum { SWITCH_EVENT_CONFERENCE_DATA, SWITCH_EVENT_CALL_SETUP_REQ, SWITCH_EVENT_CALL_SETUP_RESULT, + SWITCH_EVENT_CALL_DETAIL, + SWITCH_EVENT_DEVICE_STATE, SWITCH_EVENT_ALL } switch_event_types_t; diff --git a/src/mod/applications/mod_skel/mod_skel.c b/src/mod/applications/mod_skel/mod_skel.c index fe3c81f3fe..0a6c0a498c 100644 --- a/src/mod/applications/mod_skel/mod_skel.c +++ b/src/mod/applications/mod_skel/mod_skel.c @@ -214,6 +214,28 @@ SWITCH_STANDARD_API(skel_function) return SWITCH_STATUS_SUCCESS; } +static void mycb(switch_core_session_t *session, switch_channel_callstate_t callstate, switch_device_record_t *drec) +{ + switch_channel_t *channel = switch_core_session_get_channel(session); + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_CRIT, + "%s device: %s\nState: %s Dev State: %s/%s Total:%u Offhook:%u Active:%u Held:%u Hungup:%u Dur: %u %s\n", + switch_channel_get_name(channel), + drec->device_id, + switch_channel_callstate2str(callstate), + switch_channel_device_state2str(drec->last_state), + switch_channel_device_state2str(drec->state), + drec->stats.total, + drec->stats.offhook, + drec->stats.active, + drec->stats.held, + drec->stats.hup, + drec->active_stop ? (uint32_t)(drec->active_stop - drec->active_start) / 1000 : 0, + switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG) ? "FINAL LEG" : ""); + +} + + /* Macro expands to: switch_status_t mod_skel_load(switch_loadable_module_interface_t **module_interface, switch_memory_pool_t *pool) */ SWITCH_MODULE_LOAD_FUNCTION(mod_skel_load) { @@ -227,6 +249,8 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skel_load) SWITCH_ADD_API(api_interface, "skel", "Skel API", skel_function, "syntax"); + switch_channel_bind_device_state_handler(mycb, NULL); + /* indicate that the module should continue to be loaded */ return SWITCH_STATUS_SUCCESS; } @@ -237,6 +261,7 @@ SWITCH_MODULE_LOAD_FUNCTION(mod_skel_load) SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_skel_shutdown) { /* Cleanup dynamically allocated config settings */ + switch_channel_unbind_device_state_handler(mycb); switch_xml_config_cleanup(instructions); return SWITCH_STATUS_SUCCESS; } diff --git a/src/mod/endpoints/mod_sofia/mod_sofia.c b/src/mod/endpoints/mod_sofia/mod_sofia.c index 2a469ffa7d..bf4fd1b7bf 100644 --- a/src/mod/endpoints/mod_sofia/mod_sofia.c +++ b/src/mod/endpoints/mod_sofia/mod_sofia.c @@ -5091,7 +5091,7 @@ SWITCH_STANDARD_APP(sofia_sla_function) { private_object_t *tech_pvt; switch_core_session_t *bargee_session; - + switch_channel_t *channel = switch_core_session_get_channel(session); if (zstr(data)) { switch_log_printf(SWITCH_CHANNEL_SESSION_LOG(session), SWITCH_LOG_ERROR, "Usage: \n"); return; @@ -5101,8 +5101,7 @@ SWITCH_STANDARD_APP(sofia_sla_function) if (bargee_session == session) { switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "BARGE: %s (cannot barge on myself)\n", (char *) data); } else { - switch_channel_t *channel; - + if (switch_core_session_check_interface(bargee_session, sofia_endpoint_interface)) { tech_pvt = switch_core_session_get_private(bargee_session); switch_channel_clear_flag(tech_pvt->channel, CF_SLA_BARGING); @@ -5115,13 +5114,13 @@ SWITCH_STANDARD_APP(sofia_sla_function) switch_channel_set_flag(tech_pvt->channel, CF_SLA_BARGING); } - channel = switch_core_session_get_channel(session); switch_channel_set_variable(channel, "sip_barging_uuid", (char *)data); - } switch_core_session_rwunlock(bargee_session); } + + switch_channel_execute_on(channel, "execute_on_sip_barge"); switch_ivr_eavesdrop_session(session, data, NULL, ED_MUX_READ | ED_MUX_WRITE | ED_COPY_DISPLAY); } diff --git a/src/switch_channel.c b/src/switch_channel.c index a5585c070f..2cd0deceea 100644 --- a/src/switch_channel.c +++ b/src/switch_channel.c @@ -40,6 +40,19 @@ struct switch_cause_table { switch_call_cause_t cause; }; +typedef struct switch_device_state_binding_s { + switch_device_state_function_t function; + void *user_data; + struct switch_device_state_binding_s *next; +} switch_device_state_binding_t; + +static struct { + switch_memory_pool_t *pool; + switch_hash_t *device_hash; + switch_mutex_t *device_mutex; + switch_device_state_binding_t *device_bindings; +} globals; + static struct switch_cause_table CAUSE_CHART[] = { {"NONE", SWITCH_CAUSE_NONE}, {"UNALLOCATED_NUMBER", SWITCH_CAUSE_UNALLOCATED_NUMBER}, @@ -128,7 +141,7 @@ struct switch_channel { switch_call_direction_t direction; switch_queue_t *dtmf_queue; switch_queue_t *dtmf_log_queue; - switch_mutex_t *dtmf_mutex; + switch_mutex_t*dtmf_mutex; switch_mutex_t *flag_mutex; switch_mutex_t *state_mutex; switch_mutex_t *thread_mutex; @@ -159,8 +172,13 @@ struct switch_channel { switch_event_t *api_list; switch_event_t *var_list; switch_hold_record_t *hold_record; + switch_device_node_t *device_node; + char *device_id; }; +static void process_device_hup(switch_channel_t *channel); +static void switch_channel_check_device_state(switch_channel_t *channel, switch_channel_callstate_t callstate); + SWITCH_DECLARE(switch_hold_record_t *) switch_channel_get_hold_record(switch_channel_t *channel) { return channel->hold_record; @@ -223,6 +241,20 @@ static struct switch_callstate_table CALLSTATE_CHART[] = { {"ACTIVE", CCS_ACTIVE}, {"HELD", CCS_HELD}, {"HANGUP", CCS_HANGUP}, + {"UNHOLD", CCS_UNHOLD}, + {NULL, 0} +}; + +struct switch_device_state_table { + const char *name; + switch_device_state_t device_state; +}; +static struct switch_device_state_table DEVICE_STATE_CHART[] = { + {"DOWN", SDS_DOWN}, + {"ACTIVE", SDS_ACTIVE}, + {"ACTIVE_MULTI", SDS_ACTIVE_MULTI}, + {"HELD", SDS_HELD}, + {"HANGUP", SDS_HANGUP}, {NULL, 0} }; @@ -236,7 +268,9 @@ SWITCH_DECLARE(void) switch_channel_perform_set_callstate(switch_channel_t *chan if (o_callstate == callstate) return; channel->callstate = callstate; - + if (channel->device_node) { + channel->device_node->callstate = callstate; + } switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_DEBUG, "(%s) Callstate Change %s -> %s\n", channel->name, switch_channel_callstate2str(o_callstate), switch_channel_callstate2str(callstate)); @@ -270,6 +304,21 @@ SWITCH_DECLARE(const char *) switch_channel_callstate2str(switch_channel_callsta return str; } +SWITCH_DECLARE(const char *) switch_channel_device_state2str(switch_device_state_t device_state) +{ + uint8_t x; + const char *str = "UNKNOWN"; + + for (x = 0; x < (sizeof(DEVICE_STATE_CHART) / sizeof(struct switch_cause_table)) - 1; x++) { + if (DEVICE_STATE_CHART[x].device_state == device_state) { + str = DEVICE_STATE_CHART[x].name; + break; + } + } + + return str; +} + SWITCH_DECLARE(switch_channel_callstate_t) switch_channel_str2callstate(const char *str) { @@ -994,6 +1043,18 @@ SWITCH_DECLARE(switch_status_t) switch_channel_set_profile_var(switch_channel_t switch_mutex_lock(channel->profile_mutex); + + if (!strcasecmp(name, "device_id") && !zstr(val)) { + const char *device_id; + if (!(device_id = switch_channel_set_device_id(channel, val))) { + /* one time setting */ + switch_mutex_unlock(channel->profile_mutex); + return status; + } + + val = device_id; + } + if (!zstr(val)) { v = switch_core_strdup(channel->caller_profile->pool, val); } else { @@ -1041,7 +1102,7 @@ SWITCH_DECLARE(switch_status_t) switch_channel_set_profile_var(switch_channel_t } else { profile_node_t *pn, *n = switch_core_alloc(channel->caller_profile->pool, sizeof(*n)); int var_found; - + n->var = switch_core_strdup(channel->caller_profile->pool, name); n->val = v; @@ -1680,6 +1741,7 @@ SWITCH_DECLARE(void) switch_channel_set_flag_value(switch_channel_t *channel, sw const char *brto = switch_channel_get_partner_uuid(channel); switch_channel_set_callstate(channel, CCS_HELD); + switch_channel_check_device_state(channel, CCS_HELD); switch_mutex_lock(channel->profile_mutex); channel->caller_profile->times->last_hold = switch_time_now(); @@ -1839,6 +1901,7 @@ SWITCH_DECLARE(void) switch_channel_clear_flag(switch_channel_t *channel, switch if (ACTIVE) { switch_channel_set_callstate(channel, CCS_ACTIVE); + switch_channel_check_device_state(channel, CCS_UNHOLD); switch_mutex_lock(channel->profile_mutex); if (channel->caller_profile->times->last_hold) { channel->caller_profile->times->hold_accum += (switch_time_now() - channel->caller_profile->times->last_hold); @@ -3034,8 +3097,8 @@ SWITCH_DECLARE(switch_channel_state_t) switch_channel_perform_hangup(switch_chan channel->state = CS_HANGUP; switch_mutex_unlock(channel->state_mutex); - - + process_device_hup(channel); + channel->hangup_cause = hangup_cause; switch_log_printf(SWITCH_CHANNEL_ID_LOG, file, func, line, switch_channel_get_uuid(channel), SWITCH_LOG_NOTICE, "Hangup %s [%s] [%s]\n", channel->name, state_names[last_state], switch_channel_cause2str(channel->hangup_cause)); @@ -4512,6 +4575,394 @@ SWITCH_DECLARE(void) switch_channel_handle_cause(switch_channel_t *channel, swit } } +SWITCH_DECLARE(void) switch_channel_global_init(switch_memory_pool_t *pool) +{ + memset(&globals, 0, sizeof(globals)); + globals.pool = pool; + + switch_mutex_init(&globals.device_mutex, SWITCH_MUTEX_NESTED, pool); + switch_core_hash_init(&globals.device_hash, globals.pool); +} + +SWITCH_DECLARE(void) switch_channel_global_uninit(void) +{ + switch_core_hash_destroy(&globals.device_hash); +} + + +static void fetch_device_stats(switch_device_record_t *drec) +{ + switch_device_node_t *np; + + memset(&drec->stats, 0, sizeof(switch_device_stats_t)); + + switch_mutex_lock(drec->mutex); + for(np = drec->uuid_list; np; np = np->next) { + drec->stats.total++; + + if (!np->hup_profile) { + drec->stats.offhook++; + + if (np->callstate == CCS_HELD) { + drec->stats.held++; + } else { + drec->stats.active++; + } + } else { + drec->stats.hup++; + } + } + switch_mutex_unlock(drec->mutex); + +} + +SWITCH_DECLARE(void) switch_channel_clear_device_record(switch_channel_t *channel) +{ + switch_memory_pool_t *pool; + int sanity = 100; + switch_device_node_t *np; + switch_event_t *event; + + if (!channel->device_node || !switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG)) { + return; + } + + while(--sanity && channel->device_node->parent->refs) { + switch_yield(100000); + } + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Destroying device cdr %s on device [%s]\n", + channel->device_node->parent->uuid, + channel->device_node->parent->device_id); + + if (switch_event_create(&event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) { + int x = 0; + char prefix[80] = ""; + + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "type", "device"); + + switch_mutex_lock(channel->device_node->parent->mutex); + for(np = channel->device_node->parent->uuid_list; np; np = np->next) { + switch_snprintf(prefix, sizeof(prefix), "Call-%d", ++x); + switch_caller_profile_event_set_data(np->hup_profile, prefix, event); + } + switch_mutex_unlock(channel->device_node->parent->mutex); + + switch_event_fire(&event); + } + + switch_mutex_lock(channel->device_node->parent->mutex); + for(np = channel->device_node->parent->uuid_list; np; np = np->next) { + if (np->xml_cdr) { + switch_xml_free(np->xml_cdr); + } + if (np->event) { + switch_event_destroy(&np->event); + } + } + switch_mutex_unlock(channel->device_node->parent->mutex); + + pool = channel->device_node->parent->pool; + + switch_mutex_lock(globals.device_mutex); + switch_core_destroy_memory_pool(&pool); + + switch_mutex_unlock(globals.device_mutex); + + +} + +static void process_device_hup(switch_channel_t *channel) +{ + switch_hold_record_t *hr, *newhr, *last = NULL; + + if (!channel->device_node) { + return; + } + + switch_mutex_lock(globals.device_mutex); + channel->device_node->hup_profile = switch_caller_profile_dup(channel->device_node->parent->pool, channel->caller_profile); + fetch_device_stats(channel->device_node->parent); + + switch_ivr_generate_xml_cdr(channel->session, &channel->device_node->xml_cdr); + if (switch_event_create(&channel->device_node->event, SWITCH_EVENT_CALL_DETAIL) == SWITCH_STATUS_SUCCESS) { + switch_channel_event_set_extended_data(channel, channel->device_node->event); + } + + for (hr = channel->hold_record; hr; hr = hr->next) { + newhr = switch_core_alloc(channel->device_node->parent->pool, sizeof(*newhr)); + newhr->on = hr->on; + newhr->off = hr->off; + + if (hr->uuid) { + newhr->uuid = switch_core_strdup(channel->device_node->parent->pool, hr->uuid); + } + + if (!channel->device_node->hold_record) { + channel->device_node->hold_record = newhr; + } else { + last->next = newhr; + } + + last = newhr; + } + + if (!channel->device_node->parent->stats.offhook) { /* this is final call */ + + switch_core_hash_delete(globals.device_hash, channel->device_node->parent->device_id); + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Processing last call from device [%s]\n", + channel->device_node->parent->device_id); + switch_channel_set_flag(channel, CF_FINAL_DEVICE_LEG); + } + + switch_channel_check_device_state(channel, CCS_HANGUP); + + channel->device_node->parent->refs--; + + switch_mutex_unlock(globals.device_mutex); + +} + +static void switch_channel_check_device_state(switch_channel_t *channel, switch_channel_callstate_t callstate) +{ + switch_device_record_t *drec = NULL; + switch_device_state_binding_t *ptr = NULL; + switch_event_t *event = NULL; + + if (!channel->device_node) { + return; + } + + drec = channel->device_node->parent; + + switch_mutex_lock(globals.device_mutex); + switch_mutex_lock(drec->mutex); + + fetch_device_stats(drec); + + if (drec->stats.offhook == 0) { + drec->state = SDS_HANGUP; + } else { + if (drec->stats.active == 0 && drec->stats.held > 0) { + drec->state = SDS_HELD; + } else if (drec->stats.active == 1) { + drec->state = SDS_ACTIVE; + } else { + drec->state = SDS_ACTIVE_MULTI; + } + } + + switch(drec->state) { + case SDS_ACTIVE: + case SDS_ACTIVE_MULTI: + if (drec->last_state != SDS_HELD && drec->active_start) { + drec->active_stop = switch_micro_time_now(); + } else if (!drec->active_start) { + drec->active_start = switch_micro_time_now(); + } + break; + default: + if (drec->last_state != SDS_HELD) { + drec->active_stop = switch_micro_time_now(); + } + break; + } + + if (switch_event_create(&event, SWITCH_EVENT_DEVICE_STATE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Last-Device-State", switch_channel_device_state2str(drec->last_state)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-State", switch_channel_device_state2str(drec->state)); + switch_event_add_header_string(event, SWITCH_STACK_BOTTOM, "Device-Call-State", switch_channel_callstate2str(callstate)); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Total-Legs", "%u", drec->stats.total); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Offhook", "%u", drec->stats.offhook); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Active", "%u", drec->stats.active); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Held", "%u", drec->stats.held); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Legs-Hup", "%u", drec->stats.hup); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Start-Uepoch", "%"SWITCH_TIME_T_FMT, drec->active_start); + if (drec->active_stop) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Stop-Uepoch", "%"SWITCH_TIME_T_FMT, drec->active_stop); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Device-Talk-Time-Milliseconds", "%u", (uint32_t)(drec->active_stop - drec->active_start) / 1000); + } + } + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG1, + "%s device: %s\nState: %s Dev State: %s/%s Total:%u Offhook:%u Active:%u Held:%u Hungup:%u Dur: %u %s\n", + switch_channel_get_name(channel), + drec->device_id, + switch_channel_callstate2str(callstate), + switch_channel_device_state2str(drec->last_state), + switch_channel_device_state2str(drec->state), + drec->stats.total, + drec->stats.offhook, + drec->stats.active, + drec->stats.held, + drec->stats.hup, + drec->active_stop ? (uint32_t)(drec->active_stop - drec->active_start) / 1000 : 0, + switch_channel_test_flag(channel, CF_FINAL_DEVICE_LEG) ? "FINAL LEG" : ""); + + for (ptr = globals.device_bindings; ptr; ptr = ptr->next) { + ptr->function(channel->session, callstate, drec); + } + + if (drec->active_stop) { + drec->active_start = drec->active_stop = 0; + if (drec->state == SDS_ACTIVE || drec->state == SDS_ACTIVE_MULTI) { + drec->active_start = switch_micro_time_now(); + } + } + + drec->last_state = drec->state; + + switch_mutex_unlock(drec->mutex); + switch_mutex_unlock(globals.device_mutex); + + + if (event) { + switch_event_fire(&event); + } + +} + +/* assumed to be called under a lock */ +static void add_uuid(switch_device_record_t *drec, switch_channel_t *channel) +{ + switch_device_node_t *node; + + switch_assert(drec); + + switch_channel_set_flag(channel, CF_DEVICE_LEG); + node = switch_core_alloc(drec->pool, sizeof(*node)); + + node->uuid = switch_core_strdup(drec->pool, switch_core_session_get_uuid(channel->session)); + node->parent = drec; + channel->device_node = node; + + if (!drec->uuid_list) { + drec->uuid_list = node; + drec->uuid = node->uuid; + } else { + drec->uuid_tail->next = node; + } + + drec->uuid_tail = node; + drec->refs++; +} + +static switch_status_t create_device_record(switch_device_record_t **drecp, const char *device_id) +{ + switch_device_record_t *drec; + switch_memory_pool_t *pool; + + switch_assert(drecp); + + switch_core_new_memory_pool(&pool); + drec = switch_core_alloc(pool, sizeof(*drec)); + drec->pool = pool; + drec->device_id = switch_core_strdup(drec->pool, device_id); + switch_mutex_init(&drec->mutex, SWITCH_MUTEX_NESTED, drec->pool); + + *drecp = drec; + + return SWITCH_STATUS_SUCCESS; +} + + +SWITCH_DECLARE(const char *) switch_channel_set_device_id(switch_channel_t *channel, const char *device_id) +{ + switch_device_record_t *drec; + + if (channel->device_node) { + return NULL; + } + + channel->device_id = switch_core_session_strdup(channel->session, device_id); + + switch_mutex_lock(globals.device_mutex); + + if (!(drec = switch_core_hash_find(globals.device_hash, channel->device_id))) { + create_device_record(&drec, channel->device_id); + switch_core_hash_insert(globals.device_hash, drec->device_id, drec); + } + + add_uuid(drec, channel); + + switch_mutex_unlock(globals.device_mutex); + + switch_log_printf(SWITCH_CHANNEL_CHANNEL_LOG(channel), SWITCH_LOG_DEBUG, "Setting DEVICE ID to [%s]\n", device_id); + + switch_channel_check_device_state(channel, CCS_ACTIVE); + + return device_id; +} + +SWITCH_DECLARE(switch_device_record_t *) switch_channel_get_device_record(switch_channel_t *channel) +{ + if (channel->device_node) { + switch_mutex_lock(channel->device_node->parent->mutex); + return channel->device_node->parent; + } + + return NULL; +} + +SWITCH_DECLARE(void) switch_channel_release_device_record(switch_device_record_t **drecp) +{ + if (drecp && *drecp) { + switch_mutex_unlock((*drecp)->mutex); + *drecp = NULL; + } +} + +SWITCH_DECLARE(switch_status_t) switch_channel_bind_device_state_handler(switch_device_state_function_t function, void *user_data) +{ + switch_device_state_binding_t *binding = NULL, *ptr = NULL; + assert(function != NULL); + + if (!(binding = (switch_device_state_binding_t *) switch_core_alloc(globals.pool, sizeof(*binding)))) { + return SWITCH_STATUS_MEMERR; + } + + binding->function = function; + binding->user_data = user_data; + + switch_mutex_lock(globals.device_mutex); + for (ptr = globals.device_bindings; ptr && ptr->next; ptr = ptr->next); + + if (ptr) { + ptr->next = binding; + } else { + globals.device_bindings = binding; + } + + switch_mutex_unlock(globals.device_mutex); + + return SWITCH_STATUS_SUCCESS; +} + +SWITCH_DECLARE(switch_status_t) switch_channel_unbind_device_state_handler(switch_device_state_function_t function) +{ + switch_device_state_binding_t *ptr, *last = NULL; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(globals.device_mutex); + for (ptr = globals.device_bindings; ptr; ptr = ptr->next) { + if (ptr->function == function) { + status = SWITCH_STATUS_SUCCESS; + + if (last) { + last->next = ptr->next; + } else { + globals.device_bindings = ptr->next; + last = NULL; + continue; + } + } + last = ptr; + } + switch_mutex_unlock(globals.device_mutex); + + return status; +} + /* For Emacs: * Local Variables: diff --git a/src/switch_core.c b/src/switch_core.c index 1cb7a55c75..62db6ca9c0 100644 --- a/src/switch_core.c +++ b/src/switch_core.c @@ -1657,6 +1657,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_init(switch_core_flag_t flags, switc switch_console_init(runtime.memory_pool); switch_event_init(runtime.memory_pool); + switch_channel_global_init(runtime.memory_pool); if (switch_xml_init(runtime.memory_pool, err) != SWITCH_STATUS_SUCCESS) { apr_terminate(); @@ -2566,6 +2567,7 @@ SWITCH_DECLARE(switch_status_t) switch_core_destroy(void) switch_xml_destroy(); switch_core_session_uninit(); switch_console_shutdown(); + switch_channel_global_uninit(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Closing Event Engine.\n"); switch_event_shutdown(); diff --git a/src/switch_core_state_machine.c b/src/switch_core_state_machine.c index 7556a060a1..44ed10ed80 100644 --- a/src/switch_core_state_machine.c +++ b/src/switch_core_state_machine.c @@ -577,6 +577,8 @@ SWITCH_DECLARE(void) switch_core_session_destroy_state(switch_core_session_t *se STATE_MACRO(destroy, "DESTROY"); + switch_channel_clear_device_record(session->channel); + return; } diff --git a/src/switch_event.c b/src/switch_event.c index 7ba93ce438..307c6a97b3 100644 --- a/src/switch_event.c +++ b/src/switch_event.c @@ -198,6 +198,8 @@ static char *EVENT_NAMES[] = { "CONFERENCE_DATA", "CALL_SETUP_REQ", "CALL_SETUP_RESULT", + "CALL_DETAIL", + "DEVICE_STATE", "ALL" }; diff --git a/src/switch_ivr_originate.c b/src/switch_ivr_originate.c index d1b5874795..9769747c3e 100644 --- a/src/switch_ivr_originate.c +++ b/src/switch_ivr_originate.c @@ -2689,6 +2689,11 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_originate(switch_core_session_t *sess } + if (local_var_event) { + const char *device_id = switch_event_get_header(local_var_event, "device_id"); + switch_channel_set_profile_var(originate_status[i].peer_channel, "device_id", device_id); + } + if ((lc = switch_event_get_header(var_event, "local_var_clobber"))) { local_clobber = switch_true(lc); }