[asterisk-commits] mjordan: trunk r429967 - in /trunk: include/asterisk/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Dec 22 08:33:36 CST 2014


Author: mjordan
Date: Mon Dec 22 08:33:24 2014
New Revision: 429967

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=429967
Log:
presencestate: Allow channel drivers to provide presence state information

This patch adds the ability for channel drivers to supply presence information
in a similar manner to device state. The patch does not provide any channel
driver implementations, but it does provide the core infrastructure necessary
for channel drivers to provide such information.

The core handles multiple providers of presence state information. Ordering
of presence state is as follows:
 INVALID < NOT_SET < AVAILABLE < UNAVAILABLE < CHAT < AWAY < XA < DND

Each provider can trump the previous if it provides a presence state that
supercedes a previous one.

Review: https://reviewboard.asterisk.org/r/4050

ASTERISK-24363 #close
Reported by: Gareth Palmer
patches:
  chan_presencestate-428146.patch uploaded by Gareth Palmer (License 5169)

Modified:
    trunk/include/asterisk/channel.h
    trunk/main/channel.c
    trunk/main/pbx.c
    trunk/main/presencestate.c

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=429967&r1=429966&r2=429967
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Mon Dec 22 08:33:24 2014
@@ -631,6 +631,7 @@
 	struct ast_channel *(* const requester)(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
 
 	int (* const devicestate)(const char *device_number);	/*!< Devicestate call back */
+	int (* const presencestate)(const char *presence_provider, char **subtype, char **message); /*!< Presencestate callback */
 
 	/*!
 	 * \brief Start sending a literal DTMF digit

Modified: trunk/main/channel.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/channel.c?view=diff&rev=429967&r1=429966&r2=429967
==============================================================================
--- trunk/main/channel.c (original)
+++ trunk/main/channel.c Mon Dec 22 08:33:24 2014
@@ -275,7 +275,7 @@
 /*! \brief Show channel types - CLI command */
 static char *handle_cli_core_show_channeltypes(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-#define FORMAT  "%-15.15s  %-40.40s %-12.12s %-12.12s %-12.12s\n"
+#define FORMAT  "%-15.15s  %-40.40s %-13.13s %-13.13s %-13.13s %-13.13s\n"
 	struct chanlist *cl;
 	int count_chan = 0;
 
@@ -294,13 +294,14 @@
 	if (a->argc != 3)
 		return CLI_SHOWUSAGE;
 
-	ast_cli(a->fd, FORMAT, "Type", "Description",       "Devicestate", "Indications", "Transfer");
-	ast_cli(a->fd, FORMAT, "-----------", "-----------", "-----------", "-----------", "-----------");
+	ast_cli(a->fd, FORMAT, "Type", "Description", "Devicestate", "Presencestate", "Indications", "Transfer");
+	ast_cli(a->fd, FORMAT, "-------------", "-------------", "-------------", "-------------", "-------------", "-------------");
 
 	AST_RWLIST_RDLOCK(&backends);
 	AST_RWLIST_TRAVERSE(&backends, cl, list) {
 		ast_cli(a->fd, FORMAT, cl->tech->type, cl->tech->description,
 			(cl->tech->devicestate) ? "yes" : "no",
+			(cl->tech->presencestate) ? "yes" : "no",
 			(cl->tech->indicate) ? "yes" : "no",
 			(cl->tech->transfer) ? "yes" : "no");
 		count_chan++;
@@ -375,6 +376,7 @@
 	ast_cli(a->fd,
 		"-- Info about channel driver: %s --\n"
 		"  Device State: %s\n"
+		"Presence State: %s\n"
 		"    Indication: %s\n"
 		"     Transfer : %s\n"
 		"  Capabilities: %s\n"
@@ -385,6 +387,7 @@
 		"  Text Support: %s\n",
 		cl->tech->type,
 		(cl->tech->devicestate) ? "yes" : "no",
+		(cl->tech->presencestate) ? "yes" : "no",
 		(cl->tech->indicate) ? "yes" : "no",
 		(cl->tech->transfer) ? "yes" : "no",
 		ast_format_cap_get_names(cl->tech->capabilities, &codec_buf),
@@ -7415,6 +7418,7 @@
 		ast_data_add_str(data_type, "name", cl->tech->type);
 		ast_data_add_str(data_type, "description", cl->tech->description);
 		ast_data_add_bool(data_type, "devicestate", cl->tech->devicestate ? 1 : 0);
+		ast_data_add_bool(data_type, "presencestate", cl->tech->presencestate ? 1 : 0);
 		ast_data_add_bool(data_type, "indications", cl->tech->indicate ? 1 : 0);
 		ast_data_add_bool(data_type, "transfer", cl->tech->transfer ? 1 : 0);
 		ast_data_add_bool(data_type, "send_digit_begin", cl->tech->send_digit_begin ? 1 : 0);

Modified: trunk/main/pbx.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/pbx.c?view=diff&rev=429967&r1=429966&r2=429967
==============================================================================
--- trunk/main/pbx.c (original)
+++ trunk/main/pbx.c Mon Dec 22 08:33:24 2014
@@ -1157,10 +1157,14 @@
 		return -1;
 	}
 	ast_str_set(&str, 0, "%s", devicelist);
-	parse = parse_hint_device(str);
-
-	while ((cur = strsep(&parse, "&"))) {
+	parse = ast_str_buffer(str);
+
+	/* Spit on '&' and ',' to handle presence hints as well */
+	while ((cur = strsep(&parse, "&,"))) {
 		devicelength = strlen(cur);
+		if (!devicelength) {
+			continue;
+		}
 		device = ao2_t_alloc(sizeof(*device) + devicelength, hintdevice_destroy,
 			"allocating a hintdevice structure");
 		if (!device) {
@@ -11828,10 +11832,12 @@
 
 static void presence_state_cb(void *unused, struct stasis_subscription *sub, struct stasis_message *msg)
 {
-	struct ast_presence_state_message *presence_state = stasis_message_data(msg);
+	struct ast_presence_state_message *presence_state;
 	struct ast_hint *hint;
 	struct ast_str *hint_app = NULL;
-	struct ao2_iterator hint_iter;
+	struct ast_hintdevice *device;
+	struct ast_hintdevice *cmpdevice;
+	struct ao2_iterator *dev_iter;
 	struct ao2_iterator cb_iter;
 	char context_name[AST_MAX_CONTEXT];
 	char exten_name[AST_MAX_EXTENSION];
@@ -11840,38 +11846,48 @@
 		return;
 	}
 
+	presence_state = stasis_message_data(msg);
+
+	if (ao2_container_count(hintdevices) == 0) {
+		/* There are no hints monitoring devices. */
+		return;
+	}
+
 	hint_app = ast_str_create(1024);
 	if (!hint_app) {
 		return;
 	}
 
 	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
-	hint_iter = ao2_iterator_init(hints, 0);
-	for (; (hint = ao2_iterator_next(&hint_iter)); ao2_cleanup(hint)) {
+
+	cmpdevice = ast_alloca(sizeof(*cmpdevice) + strlen(presence_state->provider));
+	strcpy(cmpdevice->hintdevice, presence_state->provider);
+
+	ast_mutex_lock(&context_merge_lock);/* Hold off ast_merge_contexts_and_delete */
+	dev_iter = ao2_t_callback(hintdevices,
+		OBJ_POINTER | OBJ_MULTIPLE,
+		hintdevice_cmp_multiple,
+		cmpdevice,
+		"find devices in container");
+	if (!dev_iter) {
+		ast_mutex_unlock(&context_merge_lock);
+		ast_free(hint_app);
+		return;
+	}
+
+	for (; (device = ao2_iterator_next(dev_iter)); ao2_t_ref(device, -1, "Next device")) {
 		struct ast_state_cb *state_cb;
-		const char *app;
-		char *parse;
-		SCOPED_AO2LOCK(lock, hint);
-
+
+		if (!device->hint) {
+			/* Should never happen. */
+			continue;
+		}
+		hint = device->hint;
+
+		ao2_lock(hint);
 		if (!hint->exten) {
 			/* The extension has already been destroyed */
-			continue;
-		}
-
-		/* Does this hint monitor the device that changed state? */
-		app = ast_get_extension_app(hint->exten);
-		if (ast_strlen_zero(app)) {
-			/* The hint does not monitor presence at all. */
-			continue;
-		}
-
-		ast_str_set(&hint_app, 0, "%s", app);
-		parse = parse_hint_presence(hint_app);
-		if (ast_strlen_zero(parse)) {
-			continue;
-		}
-		if (strcasecmp(parse, presence_state->provider)) {
-			/* The hint does not monitor the presence provider. */
+			ao2_unlock(hint);
 			continue;
 		}
 
@@ -11885,6 +11901,7 @@
 		ast_copy_string(exten_name, ast_get_extension_name(hint->exten),
 			sizeof(exten_name));
 		ast_str_set(&hint_app, 0, "%s", ast_get_extension_app(hint->exten));
+		ao2_unlock(hint);
 
 		/* Check to see if update is necessary */
 		if ((hint->last_presence_state == presence_state->state) &&
@@ -11928,7 +11945,7 @@
 		}
 		ao2_iterator_destroy(&cb_iter);
 	}
-	ao2_iterator_destroy(&hint_iter);
+	ao2_iterator_destroy(dev_iter);
 	ast_mutex_unlock(&context_merge_lock);
 
 	ast_free(hint_app);

Modified: trunk/main/presencestate.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/presencestate.c?view=diff&rev=429967&r1=429966&r2=429967
==============================================================================
--- trunk/main/presencestate.c (original)
+++ trunk/main/presencestate.c Mon Dec 22 08:33:24 2014
@@ -66,6 +66,7 @@
 #include "asterisk/presencestate.h"
 #include "asterisk/pbx.h"
 #include "asterisk/app.h"
+#include "asterisk/test.h"
 
 /*! \brief Device state strings for printing */
 static const struct {
@@ -146,42 +147,74 @@
 
 static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
 {
-	struct presence_state_provider *provider;
-	char *address;
-	char *label = ast_strdupa(presence_provider);
-	int res = AST_PRESENCE_INVALID;
-
-	if (check_cache) {
-		res = presence_state_cached(presence_provider, subtype, message);
-		if (res != AST_PRESENCE_INVALID) {
-			return res;
+	char *labels = ast_strdupa(presence_provider);
+	char *label;
+	enum ast_presence_state state = AST_PRESENCE_INVALID;
+	enum ast_presence_state state_order[] = {
+		[AST_PRESENCE_INVALID]     = 0,
+		[AST_PRESENCE_NOT_SET]     = 1,
+		[AST_PRESENCE_AVAILABLE]   = 2,
+		[AST_PRESENCE_UNAVAILABLE] = 3,
+		[AST_PRESENCE_CHAT]        = 4,
+		[AST_PRESENCE_AWAY]        = 5,
+		[AST_PRESENCE_XA]          = 6,
+		[AST_PRESENCE_DND]         = 7
+	};
+
+	while ((label = strsep(&labels, "&"))) {
+		enum ast_presence_state next_state = AST_PRESENCE_INVALID;
+		char *next_subtype = NULL;
+		char *next_message = NULL;
+
+		if (check_cache) {
+			next_state = presence_state_cached(label, &next_subtype, &next_message);
 		}
-	}
-
-	if ((address = strchr(label, ':'))) {
-		*address = '\0';
-		address++;
-	} else {
-		ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", presence_provider);
-		return res;
-	}
-
-	AST_RWLIST_RDLOCK(&presence_state_providers);
-	AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
-		ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
-
-		if (!strcasecmp(provider->label, label)) {
-			res = provider->callback(address, subtype, message);
-			break;
+
+		if (next_state == AST_PRESENCE_INVALID) {
+			struct presence_state_provider *provider;
+			const struct ast_channel_tech *chan_tech;
+			char *address;
+
+			if ((address = strchr(label, '/'))) {
+				*address++ = '\0';
+
+				if ((chan_tech = ast_get_channel_tech(label)) && chan_tech->presencestate) {
+					next_state = chan_tech->presencestate(address, &next_subtype, &next_message);
+				}
+			} else if ((address = strchr(label, ':'))) {
+				*address++ = '\0';
+
+				AST_RWLIST_RDLOCK(&presence_state_providers);
+				AST_RWLIST_TRAVERSE(&presence_state_providers, provider, list) {
+					ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
+
+					if (!strcasecmp(provider->label, label)) {
+						next_state = provider->callback(address, &next_subtype, &next_message);
+						break;
+					}
+				}
+				AST_RWLIST_UNLOCK(&presence_state_providers);
+
+				if (!provider) {
+					ast_log(LOG_WARNING, "No provider found for label: %s\n", label);
+				}
+			} else {
+				ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", label);
+			}
 		}
-	}
-	AST_RWLIST_UNLOCK(&presence_state_providers);
-
-	if (!provider) {
-		ast_log(LOG_WARNING, "No provider found for label %s\n", label);
-	}
-
-	return res;
+
+		if (state_order[next_state] > state_order[state]) {
+			state = next_state;
+
+			ast_free(*subtype);
+			ast_free(*message);
+
+			*subtype = next_subtype;
+			*message = next_message;
+		}
+	}
+
+	return state;
 }
 
 enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
@@ -354,6 +387,99 @@
 	return presence_state->provider;
 }
 
+#if defined(TEST_FRAMEWORK)
+
+#define TEST_CATEGORY "/main/presence"
+
+static int presence_test_alice_state = AST_PRESENCE_UNAVAILABLE;
+static int presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
+
+static int presence_test_presencestate(const char *label, char **subtype, char **message)
+{
+	if (!strcmp(label, "Alice")) {
+		return presence_test_alice_state;
+	} else if (!strcmp(label, "Bob")) {
+		return presence_test_bob_state;
+	} else {
+		return AST_PRESENCE_UNAVAILABLE;
+	}
+}
+
+static struct ast_channel_tech presence_test_tech = {
+	.type = "PresenceTestChannel",
+	.description = "Presence test technology",
+	.presencestate = presence_test_presencestate,
+};
+
+AST_TEST_DEFINE(test_presence_chan)
+{
+	int res = AST_TEST_FAIL;
+	char provider[80];
+	enum ast_presence_state state;
+	char *subtype = NULL, *message = NULL;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "channel_presence";
+		info->category = TEST_CATEGORY;
+		info->summary = "Channel presence state tests";
+		info->description = "Creates test channel technology and then test the presence state callback";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	if (ast_channel_register(&presence_test_tech)) {
+		ast_log(LOG_WARNING, "Unable to register channel type '%s'\n", presence_test_tech.type);
+		goto presence_test_cleanup;
+	}
+
+	/* Check Alice's state */
+	snprintf(provider, sizeof(provider), "%s/Alice", presence_test_tech.type);
+
+	presence_test_alice_state = AST_PRESENCE_AVAILABLE;
+	state = ast_presence_state_nocache(provider, &subtype, &message);
+
+	if (state != presence_test_alice_state) {
+		ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
+			provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
+		goto presence_test_cleanup;
+	}
+
+	/* Check Alice's and Bob's state, Alice's should win as DND > AVAILABLE */
+	snprintf(provider, sizeof(provider), "%s/Alice&%s/Bob", presence_test_tech.type, presence_test_tech.type);
+
+	presence_test_alice_state = AST_PRESENCE_DND;
+	presence_test_bob_state = AST_PRESENCE_UNAVAILABLE;
+	state = ast_presence_state_nocache(provider, &subtype, &message);
+
+	if (state != presence_test_alice_state) {
+		ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
+			provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_alice_state));
+		goto presence_test_cleanup;
+        }
+
+	/* Check Alice's and Bob's state, Bob's should now win as AVAILABLE < UNAVAILABLE */
+	presence_test_alice_state = AST_PRESENCE_AVAILABLE;
+	state = ast_presence_state_nocache(provider, &subtype, &message);
+
+	if (state != presence_test_bob_state) {
+		ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
+			provider, ast_presence_state2str(state), ast_presence_state2str(presence_test_bob_state));
+		goto presence_test_cleanup;
+	}
+
+	res = AST_TEST_PASS;
+
+presence_test_cleanup:
+	ast_channel_unregister(&presence_test_tech);
+	ast_free(subtype);
+	ast_free(message);
+
+	return res;
+}
+#endif
+
 static void presence_state_engine_cleanup(void)
 {
 	ao2_cleanup(presence_state_topic_all);
@@ -362,6 +488,7 @@
 	presence_state_cache = NULL;
 	presence_state_topic_cached = stasis_caching_unsubscribe_and_join(presence_state_topic_cached);
 	STASIS_MESSAGE_TYPE_CLEANUP(ast_presence_state_message_type);
+	AST_TEST_UNREGISTER(test_presence_chan);
 }
 
 int ast_presence_state_engine_init(void)
@@ -386,6 +513,8 @@
 	if (!presence_state_topic_cached) {
 		return -1;
 	}
+
+	AST_TEST_REGISTER(test_presence_chan);
 
 	return 0;
 }




More information about the asterisk-commits mailing list