Asterisk - The Open Source Telephony Project  18.5.0
presencestate.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2011-2012, Digium, Inc.
5  *
6  * David Vossel <[email protected]>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*! \file
20  *
21  * \brief Presence state management
22  */
23 
24 /*** MODULEINFO
25  <support_level>core</support_level>
26  ***/
27 
28 /*** DOCUMENTATION
29  <managerEvent language="en_US" name="PresenceStateChange">
30  <managerEventInstance class="EVENT_FLAG_CALL">
31  <synopsis>Raised when a presence state changes</synopsis>
32  <syntax>
33  <parameter name="Presentity">
34  <para>The entity whose presence state has changed</para>
35  </parameter>
36  <parameter name="Status">
37  <para>The new status of the presentity</para>
38  </parameter>
39  <parameter name="Subtype">
40  <para>The new subtype of the presentity</para>
41  </parameter>
42  <parameter name="Message">
43  <para>The new message of the presentity</para>
44  </parameter>
45  </syntax>
46  <description>
47  <para>This differs from the <literal>PresenceStatus</literal>
48  event because this event is raised for all presence state changes,
49  not only for changes that affect dialplan hints.</para>
50  </description>
51  <see-also>
52  <ref type="managerEvent">PresenceStatus</ref>
53  </see-also>
54  </managerEventInstance>
55  </managerEvent>
56 ***/
57 
58 #include "asterisk.h"
59 
60 #include "asterisk/_private.h"
61 #include "asterisk/utils.h"
62 #include "asterisk/lock.h"
63 #include "asterisk/linkedlists.h"
64 #include "asterisk/presencestate.h"
65 #include "asterisk/pbx.h"
66 #include "asterisk/app.h"
67 #include "asterisk/test.h"
68 
69 /*! \brief Device state strings for printing */
70 static const struct {
71  const char *string;
73 
74 } state2string[] = {
75  { "not_set", AST_PRESENCE_NOT_SET},
76  { "unavailable", AST_PRESENCE_UNAVAILABLE },
77  { "available", AST_PRESENCE_AVAILABLE},
78  { "away", AST_PRESENCE_AWAY},
79  { "xa", AST_PRESENCE_XA},
80  { "chat", AST_PRESENCE_CHAT},
81  { "dnd", AST_PRESENCE_DND},
82 };
83 
85 
87  .to_ami = presence_state_to_ami,
88 );
92 
93 /*! \brief A presence state provider */
95  char label[40];
98 };
99 
100 /*! \brief A list of providers */
102 
104 {
105  int i;
106  for (i = 0; i < ARRAY_LEN(state2string); i++) {
107  if (state == state2string[i].state) {
108  return state2string[i].string;
109  }
110  }
111  return "";
112 }
113 
115 {
116  int i;
117  for (i = 0; i < ARRAY_LEN(state2string); i++) {
118  if (!strcasecmp(val, state2string[i].string)) {
119  return state2string[i].state;
120  }
121  }
122  return AST_PRESENCE_INVALID;
123 }
124 
125 static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
126 {
128  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
129  struct ast_presence_state_message *presence_state;
130 
132 
133  if (!msg) {
134  return res;
135  }
136 
137  presence_state = stasis_message_data(msg);
138  res = presence_state->state;
139 
140  *subtype = !ast_strlen_zero(presence_state->subtype) ? ast_strdup(presence_state->subtype) : NULL;
141  *message = !ast_strlen_zero(presence_state->message) ? ast_strdup(presence_state->message) : NULL;
142 
143  return res;
144 }
145 
146 static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
147 {
148  char *labels = ast_strdupa(presence_provider);
149  char *label;
151  enum ast_presence_state state_order[] = {
152  [AST_PRESENCE_INVALID] = 0,
153  [AST_PRESENCE_NOT_SET] = 1,
156  [AST_PRESENCE_CHAT] = 4,
157  [AST_PRESENCE_AWAY] = 5,
158  [AST_PRESENCE_XA] = 6,
159  [AST_PRESENCE_DND] = 7
160  };
161 
162  *subtype = NULL;
163  *message = NULL;
164 
165  while ((label = strsep(&labels, "&"))) {
166  enum ast_presence_state next_state = AST_PRESENCE_INVALID;
167  char *next_subtype = NULL;
168  char *next_message = NULL;
169 
170  if (check_cache) {
171  next_state = presence_state_cached(label, &next_subtype, &next_message);
172  }
173 
174  if (next_state == AST_PRESENCE_INVALID) {
176  const struct ast_channel_tech *chan_tech;
177  char *address;
178 
179  if ((address = strchr(label, '/'))) {
180  *address++ = '\0';
181 
182  if ((chan_tech = ast_get_channel_tech(label)) && chan_tech->presencestate) {
183  next_state = chan_tech->presencestate(address, &next_subtype, &next_message);
184  }
185  } else if ((address = strchr(label, ':'))) {
186  *address++ = '\0';
187 
190  ast_debug(5, "Checking provider %s with %s\n", provider->label, label);
191 
192  if (!strcasecmp(provider->label, label)) {
193  next_state = provider->callback(address, &next_subtype, &next_message);
194  break;
195  }
196  }
198 
199  if (!provider) {
200  ast_log(LOG_WARNING, "No provider found for label: %s\n", label);
201  }
202  } else {
203  ast_log(LOG_WARNING, "No label found for presence state provider: %s\n", label);
204  }
205  }
206 
207  if (state_order[next_state] > state_order[state]) {
208  state = next_state;
209 
210  ast_free(*subtype);
211  ast_free(*message);
212 
213  *subtype = next_subtype;
214  *message = next_message;
215  }
216  }
217 
218  return state;
219 }
220 
221 enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
222 {
223  return ast_presence_state_helper(presence_provider, subtype, message, 1);
224 }
225 
226 enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
227 {
228  return ast_presence_state_helper(presence_provider, subtype, message, 0);
229 }
230 
232 {
234 
235  if (!callback || !(provider = ast_calloc(1, sizeof(*provider)))) {
236  return -1;
237  }
238 
239  provider->callback = callback;
240  ast_copy_string(provider->label, label, sizeof(provider->label));
241 
245 
246  return 0;
247 }
249 {
251  int res = -1;
252 
255  if (!strcasecmp(provider->label, label)) {
257  ast_free(provider);
258  res = 0;
259  break;
260  }
261  }
264 
265  return res;
266 }
267 
268 static void presence_state_dtor(void *obj)
269 {
270  struct ast_presence_state_message *presence_state = obj;
271  ast_string_field_free_memory(presence_state);
272 }
273 
276  const char *subtype,
277  const char *message)
278 {
279  RAII_VAR(struct ast_presence_state_message *, presence_state, ao2_alloc(sizeof(*presence_state), presence_state_dtor), ao2_cleanup);
280 
281  if (!presence_state || ast_string_field_init(presence_state, 256)) {
282  return NULL;
283  }
284 
285  presence_state->state = state;
286  ast_string_field_set(presence_state, provider, provider);
287  ast_string_field_set(presence_state, subtype, S_OR(subtype, ""));
288  ast_string_field_set(presence_state, message, S_OR(message, ""));
289 
290  ao2_ref(presence_state, +1);
291  return presence_state;
292 }
293 
294 static void presence_state_event(const char *provider,
296  const char *subtype,
297  const char *message)
298 {
299  RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
300  RAII_VAR(struct ast_presence_state_message *, presence_state, NULL, ao2_cleanup);
301 
303  return;
304  }
305 
306  presence_state = presence_state_alloc(provider, state, subtype, message);
307  if (!presence_state) {
308  return;
309  }
310 
312  if (!msg) {
313  return;
314  }
315 
317 }
318 
319 static void do_presence_state_change(const char *provider)
320 {
321  char *subtype = NULL;
322  char *message = NULL;
324 
325  state = ast_presence_state_helper(provider, &subtype, &message, 0);
326 
327  if (state == AST_PRESENCE_INVALID) {
328  return;
329  }
330 
331  presence_state_event(provider, state, subtype, message);
332  ast_free(subtype);
333  ast_free(message);
334 }
335 
337  const char *subtype,
338  const char *message,
339  const char *presence_provider)
340 {
341  if (state == AST_PRESENCE_NOT_SET) {
342  do_presence_state_change(presence_provider);
343  } else {
344  presence_state_event(presence_provider, state, subtype, message);
345  }
346 
347  return 0;
348 }
349 
351  const char *subtype,
352  const char *message,
353  const char *fmt, ...)
354 {
355  char buf[AST_MAX_EXTENSION];
356  va_list ap;
357 
358  va_start(ap, fmt);
359  vsnprintf(buf, sizeof(buf), fmt, ap);
360  va_end(ap);
361 
362  return ast_presence_state_changed_literal(state, subtype, message, buf);
363 }
364 
366 {
368 }
369 
371 {
372  return presence_state_cache;
373 }
374 
376 {
377  return stasis_caching_get_topic(presence_state_topic_cached);
378 }
379 
380 static const char *presence_state_get_id(struct stasis_message *msg)
381 {
382  struct ast_presence_state_message *presence_state = stasis_message_data(msg);
383 
385  return NULL;
386  }
387 
388  return presence_state->provider;
389 }
390 
391 #if defined(TEST_FRAMEWORK)
392 
393 #define TEST_CATEGORY "/main/presence/"
394 
397 
398 static int presence_test_presencestate(const char *label, char **subtype, char **message)
399 {
400  if (!strcmp(label, "Alice")) {
402  } else if (!strcmp(label, "Bob")) {
404  } else {
406  }
407 }
408 
410  .type = "PresenceTestChannel",
411  .description = "Presence test technology",
412  .presencestate = presence_test_presencestate,
413 };
414 
415 AST_TEST_DEFINE(test_presence_chan)
416 {
417  int res = AST_TEST_FAIL;
418  char provider[80];
420  char *subtype = NULL, *message = NULL;
421 
422  switch (cmd) {
423  case TEST_INIT:
424  info->name = "channel_presence";
425  info->category = TEST_CATEGORY;
426  info->summary = "Channel presence state tests";
427  info->description = "Creates test channel technology and then test the presence state callback";
428  return AST_TEST_NOT_RUN;
429  case TEST_EXECUTE:
430  break;
431  }
432 
433  if (ast_channel_register(&presence_test_tech)) {
434  ast_log(LOG_WARNING, "Unable to register channel type '%s'\n", presence_test_tech.type);
435  goto presence_test_cleanup;
436  }
437 
438  /* Check Alice's state */
439  snprintf(provider, sizeof(provider), "%s/Alice", presence_test_tech.type);
440 
442  state = ast_presence_state_nocache(provider, &subtype, &message);
443 
444  if (state != presence_test_alice_state) {
445  ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
447  goto presence_test_cleanup;
448  }
449 
450  /* Check Alice's and Bob's state, Alice's should win as DND > AVAILABLE */
451  snprintf(provider, sizeof(provider), "%s/Alice&%s/Bob", presence_test_tech.type, presence_test_tech.type);
452 
455  state = ast_presence_state_nocache(provider, &subtype, &message);
456 
457  if (state != presence_test_alice_state) {
458  ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
460  goto presence_test_cleanup;
461  }
462 
463  /* Check Alice's and Bob's state, Bob's should now win as AVAILABLE < UNAVAILABLE */
465  state = ast_presence_state_nocache(provider, &subtype, &message);
466 
467  if (state != presence_test_bob_state) {
468  ast_log(LOG_WARNING, "Presence state of '%s' returned '%s' instead of the expected value '%s'\n",
470  goto presence_test_cleanup;
471  }
472 
473  res = AST_TEST_PASS;
474 
475 presence_test_cleanup:
476  ast_channel_unregister(&presence_test_tech);
477  ast_free(subtype);
478  ast_free(message);
479 
480  return res;
481 }
482 #endif
483 
485 {
486  ao2_cleanup(presence_state_topic_all);
487  presence_state_topic_all = NULL;
488  ao2_cleanup(presence_state_cache);
489  presence_state_cache = NULL;
490  presence_state_topic_cached = stasis_caching_unsubscribe_and_join(presence_state_topic_cached);
492  AST_TEST_UNREGISTER(test_presence_chan);
493 }
494 
496 {
498 
500  return -1;
501  }
502 
503  presence_state_topic_all = stasis_topic_create("presence_state:all");
504  if (!presence_state_topic_all) {
505  return -1;
506  }
507 
508  presence_state_cache = stasis_cache_create(presence_state_get_id);
509  if (!presence_state_cache) {
510  return -1;
511  }
512 
513  presence_state_topic_cached = stasis_caching_topic_create(presence_state_topic_all, presence_state_cache);
514  if (!presence_state_topic_cached) {
515  return -1;
516  }
519 
520  AST_TEST_REGISTER(test_presence_chan);
521 
522  return 0;
523 }
524 
526 {
527  struct ast_presence_state_message *presence_state = stasis_message_data(msg);
528  struct ast_manager_event_blob *res;
529 
530  char *subtype = ast_escape_c_alloc(presence_state->subtype);
531  char *message = ast_escape_c_alloc(presence_state->message);
532 
533  res = ast_manager_event_blob_create(EVENT_FLAG_CALL, "PresenceStateChange",
534  "Presentity: %s\r\n"
535  "Status: %s\r\n"
536  "Subtype: %s\r\n"
537  "Message: %s\r\n",
538  presence_state->provider,
539  ast_presence_state2str(presence_state->state),
540  subtype ?: "",
541  message ?: "");
542 
543  ast_free(subtype);
544  ast_free(message);
545 
546  return res;
547 }
int(*const presencestate)(const char *presence_provider, char **subtype, char **message)
Definition: channel.h:677
Struct containing info for an AMI event to send out.
Definition: manager.h:491
const char *const type
Definition: channel.h:630
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static void do_presence_state_change(const char *provider)
struct stasis_topic * presence_state_topic_all
Definition: presencestate.c:89
int stasis_caching_accept_message_type(struct stasis_caching_topic *caching_topic, struct stasis_message_type *type)
Indicate to a caching topic that we are interested in a message type.
Definition: stasis_cache.c:90
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
static enum ast_presence_state ast_presence_state_helper(const char *presence_provider, char **subtype, char **message, int check_cache)
static struct ast_channel_tech presence_test_tech
Definition: ast_expr2.c:325
static const char * presence_state_get_id(struct stasis_message *msg)
int ast_presence_state_prov_del(const char *label)
Remove presence state provider.
const struct ast_channel_tech * ast_get_channel_tech(const char *name)
Get a channel technology structure by name.
Definition: channel.c:592
ast_presence_state_prov_cb_type callback
Definition: presencestate.c:96
char * address
Definition: f2c.h:59
void ast_channel_unregister(const struct ast_channel_tech *tech)
Unregister a channel technology.
Definition: channel.c:570
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
#define STASIS_MESSAGE_TYPE_INIT(name)
Boiler-plate messaging macro for initializing message types.
Definition: stasis.h:1501
STASIS_MESSAGE_TYPE_DEFN(ast_presence_state_message_type,.to_ami=presence_state_to_ami,)
A list of providers.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
A presence state provider.
Definition: presencestate.c:94
#define LOG_WARNING
Definition: logger.h:274
struct stasis_caching_topic * stasis_caching_topic_create(struct stasis_topic *original_topic, struct stasis_cache *cache)
Create a topic which monitors and caches messages from another topic.
Definition: stasis_cache.c:948
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
static int presence_test_presencestate(const char *label, char **subtype, char **message)
Test Framework API.
#define STASIS_MESSAGE_TYPE_CLEANUP(name)
Boiler-plate messaging macro for cleaning up message types.
Definition: stasis.h:1523
#define EVENT_FLAG_CALL
Definition: manager.h:72
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
struct stasis_message_type * stasis_message_type(const struct stasis_message *msg)
Get the message type for a stasis_message.
static void presence_state_dtor(void *obj)
Stasis message payload representing a presence state update.
const char * string
Definition: presencestate.c:71
int ast_channel_register(const struct ast_channel_tech *tech)
Register a channel technology (a new channel driver) Called by a channel module to register the kind ...
Definition: channel.c:539
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
static struct ast_presence_state_message * presence_state_alloc(const char *provider, enum ast_presence_state state, const char *subtype, const char *message)
#define NULL
Definition: resample.c:96
struct stasis_topic * ast_presence_state_topic_all(void)
Get presence state topic.
struct stasis_caching_topic * stasis_caching_unsubscribe_and_join(struct stasis_caching_topic *caching_topic)
Unsubscribes a caching topic from its upstream topic, blocking until all messages have been forwarded...
Definition: stasis_cache.c:146
struct ast_manager_event_blob * ast_manager_event_blob_create(int event_flags, const char *manager_event, const char *extra_fields_fmt,...)
Construct a ast_manager_event_blob.
Definition: manager.c:9727
Utility functions.
#define ast_strlen_zero(foo)
Definition: strings.h:52
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
#define AST_RWLIST_INSERT_HEAD
Definition: linkedlists.h:717
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
#define ast_log
Definition: astobj2.c:42
enum ast_presence_state ast_presence_state(const char *presence_provider, char **subtype, char **message)
Asks a presence state provider for the current presence state.
int ast_register_cleanup(void(*func)(void))
Register a function to be executed before Asterisk gracefully exits.
Definition: clicompat.c:19
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
ast_presence_state
Definition: presencestate.h:26
#define AST_MAX_EXTENSION
Definition: channel.h:135
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
#define ao2_ref(o, delta)
Definition: astobj2.h:464
int ast_presence_state_changed(enum ast_presence_state state, const char *subtype, const char *message, const char *fmt,...)
Notify the world that a presence provider state changed.
enum ast_presence_state ast_presence_state_nocache(const char *presence_provider, char **subtype, char **message)
Asks a presence state provider for the current presence state, bypassing the event cache...
#define AST_RWLIST_REMOVE_CURRENT
Definition: linkedlists.h:569
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
A set of macros to manage forward-linked lists.
struct stasis_topic * stasis_topic_create(const char *name)
Create a new topic.
Definition: stasis.c:618
#define TEST_CATEGORY
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Definition: channel.h:629
Core PBX routines and definitions.
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:544
Presence state management.
int stasis_caching_set_filter(struct stasis_caching_topic *caching_topic, enum stasis_subscription_message_filter filter)
Set the message type filtering level on a cache.
Definition: stasis_cache.c:109
void * stasis_message_data(const struct stasis_message *msg)
Get the data contained in a message.
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
struct stasis_message * stasis_message_create(struct stasis_message_type *type, void *data)
Create a new message.
struct stasis_cache * presence_state_cache
Definition: presencestate.c:90
int ast_presence_state_engine_init(void)
def info(msg)
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static struct ast_manager_event_blob * presence_state_to_ami(struct stasis_message *msg)
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic&#39;s subscribers.
Definition: stasis.c:1511
#define ast_free(a)
Definition: astmm.h:182
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
char * ast_escape_c_alloc(const char *s)
Escape standard &#39;C&#39; sequences in the given string.
Definition: main/utils.c:1892
static void to_ami(struct ast_sip_subscription *sub, struct ast_str **buf)
Prototypes for public functions only of internal interest,.
const char * ast_presence_state2str(enum ast_presence_state state)
Convert presence state to text string for output.
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:414
static void presence_state_event(const char *provider, enum ast_presence_state state, const char *subtype, const char *message)
static const struct @413 state2string[]
Device state strings for printing.
char * strsep(char **str, const char *delims)
struct stasis_cache * stasis_cache_create(snapshot_get_id id_fn)
Create a cache.
Definition: stasis_cache.c:360
struct stasis_cache * ast_presence_state_cache(void)
Backend cache for ast_presence_state_topic_cached()
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
static int presence_test_alice_state
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static enum ast_presence_state presence_state_cached(const char *presence_provider, char **subtype, char **message)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static int presence_test_bob_state
struct stasis_message_type * ast_presence_state_message_type(void)
Get presence state message type.
enum ast_presence_state state
struct stasis_message * stasis_cache_get(struct stasis_cache *cache, struct stasis_message_type *type, const char *id)
Retrieve an item from the cache for the ast_eid_default entity.
Definition: stasis_cache.c:686
int ast_presence_state_changed_literal(enum ast_presence_state state, const char *subtype, const char *message, const char *presence_provider)
Notify the world that a presence provider state changed.
struct stasis_topic * ast_presence_state_topic_cached(void)
Get caching presence state topic.
int ast_presence_state_prov_add(const char *label, ast_presence_state_prov_cb_type callback)
Add presence state provider.
enum ast_presence_state ast_presence_state_val(const char *val)
Convert presence state from text to integer value.
struct stasis_topic * stasis_caching_get_topic(struct stasis_caching_topic *caching_topic)
Returns the topic of cached events from a caching topics.
Definition: stasis_cache.c:85
static struct prometheus_metrics_provider provider
Definition: bridges.c:178
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct stasis_caching_topic * presence_state_topic_cached
Definition: presencestate.c:91
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:616
enum ast_presence_state(* ast_presence_state_prov_cb_type)(const char *data, char **subtype, char **message)
Presence state provider call back.
Definition: presencestate.h:43
enum ast_presence_state state
Definition: presencestate.c:72
AST_TEST_DEFINE(test_presence_chan)
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
static void presence_state_engine_cleanup(void)