]> git.ipfire.org Git - thirdparty/freeswitch.git/commitdiff
add some device-state mechinism to FS to allow tracking of device-specific states...
authorAnthony Minessale <anthm@freeswitch.org>
Wed, 5 Jun 2013 16:19:44 +0000 (11:19 -0500)
committerAnthony Minessale <anthm@freeswitch.org>
Wed, 5 Jun 2013 16:19:53 +0000 (11:19 -0500)
12 files changed:
libs/esl/src/esl_event.c
libs/esl/src/include/esl_event.h
src/include/switch_channel.h
src/include/switch_core.h
src/include/switch_types.h
src/mod/applications/mod_skel/mod_skel.c
src/mod/endpoints/mod_sofia/mod_sofia.c
src/switch_channel.c
src/switch_core.c
src/switch_core_state_machine.c
src/switch_event.c
src/switch_ivr_originate.c

index 73a41b7868b4c71e6ac20f070b04b20c9c015f5c..00c5410000b5619d3c8fc0715fb9e8c53483893c 100644 (file)
@@ -143,6 +143,8 @@ static const char *EVENT_NAMES[] = {
        "CONFERENCE_DATA",
        "CALL_SETUP_REQ",
        "CALL_SETUP_RESULT",
+       "CALL_DETAIL",
+       "DEVICE_STATE",
        "ALL"
 };
 
index 1b6e6e29b46565347165c522db275432198736b4..2b4197be41a2c71f7dba8802e884375f6c9f8df5 100644 (file)
@@ -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;
 
index 219663b0e08e5fc0fcd8a32b61a645f4769ffc1f..c35994d8afa0f0d8f42577868bccf2738c321d92 100644 (file)
@@ -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:
index dc1610dd8073080b2a69ee12ff9a004bf0e969ae..39fab1e7cd7e1a885647f33446f22d678714b7db 100644 (file)
@@ -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_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 struct switch_thread_data_s {
-       switch_thread_start_t func;
-       void *obj;
-       int alloc;
-} switch_thread_data_t;
+typedef void(*switch_device_state_function_t)(switch_core_session_t *session, switch_channel_callstate_t callstate, switch_device_record_t *drec);
 
 
 #define MESSAGE_STAMP_FFL(_m) _m->_file = __FILE__; _m->_func = __SWITCH_FUNC__; _m->_line = __LINE__
index 6c45fac650d782a2d470028071baddd285cd7661..e9dcbae27c5150a1e3ed464d2606888779f0e0fb 100644 (file)
@@ -1109,9 +1109,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)
@@ -1277,6 +1287,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,
@@ -1727,6 +1739,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;
 
index 374010c60548e1b2328c00ab7bb77f4cfaf18931..e6cc9a68f5c7bca2e86acce23e9c3b0749c2bbba 100644 (file)
@@ -157,6 +157,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)
 {
@@ -170,6 +192,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;
 }
@@ -180,6 +204,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;
 }
index 4a0d72fcff604d56ade5d4dc905dea3cee2266aa..4486b35e013c651c00ce2b5bb953dd849551fc81 100644 (file)
@@ -5850,7 +5850,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: <uuid>\n");
                return;
@@ -5860,8 +5860,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);
                                sofia_clear_flag(tech_pvt, TFLAG_SLA_BARGING);
@@ -5874,13 +5873,13 @@ SWITCH_STANDARD_APP(sofia_sla_function)
                                sofia_set_flag(tech_pvt, TFLAG_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);
 }
index e35d159407b66f67abf1723deb8d06ff8179627f..f2e509d5183e3e1ff9034e64a3e7208aabf99973 100644 (file)
@@ -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));
@@ -4497,6 +4560,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:
index d93aac78a7d04a2f75901af4384d8e24b6a2b198..accc30636249b8fa2cb20a6f59c55c938c1069e6 100644 (file)
@@ -1644,6 +1644,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();
@@ -2553,6 +2554,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();
index 5281d912d6334d021a80d7dd9915ea1333218740..289054613f1ec0a519466993c71806f16e91f4f0 100644 (file)
@@ -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;
 }
 
index 7ba93ce438e6e45440db339d14d7634c2e7f56f1..307c6a97b304a8f2b418703e2fdec67b3d9d3004 100644 (file)
@@ -198,6 +198,8 @@ static char *EVENT_NAMES[] = {
        "CONFERENCE_DATA",
        "CALL_SETUP_REQ",
        "CALL_SETUP_RESULT",
+       "CALL_DETAIL",
+       "DEVICE_STATE",
        "ALL"
 };
 
index d1b587479592ffcd1e4a53dd81f510e1de0df236..9769747c3ea601db18649ea904853cd8be9f6b2b 100644 (file)
@@ -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);
                                }