Alarm Framework
The maemo alarm framework provides an easy way to manage timed events in the device. It is powerful, and not restricted only to wake up alarms. The framework provides many other features, including:
- Setting multiple alarm events
- Configuring the number of recurrences and the time between each one
- Choosing whether the alarm dialog should be shown
- Setting the title and the message to be shown in the alarm dialog
- Selecting a custom icon file to be shown in the alarm dialog
- Selecting a custom sound file to be played while the alarm dialog is shown
- Choosing to boot the device if it is turned off
- Choosing to run the alarm only if the device is connected
- Choosing to run the alarm in the system start up if it is missed
- Choosing to postpone the alarm if it is missed
- Executing a given file (e.g. a program or a script)
- Sending a message to a D-Bus message bus
- Querying for existing alarm events in a given period of time
- Deleting an existing alarm event
- Configuring the snooze time for the event
- Configuring the default snooze time for all alarm events
This section shows how to use the alarm interface, describes its main functions and gives examples of use cases.
Timed event functionality is provided by the alarmd daemon. It makes it possible for applications to get D-Bus messages or exec calls at certain times.
Applications register an event happening at a certain time. The alarmd daemon will then call the application when the event is due, so the application does not need to be running during the waiting period. This is useful for applications that need to update their status at regular intervals.
A single event may be set to repeat itself over and over again, at regular intervals or based on time and date. The alarmd daemon will remember these events over system shutdowns too.
The alarmd daemon can also be asked to start the system up for an event, even when powered off. These events may also be set to run in a hidden power-on mode, where the display stays off, but the device is on. The alarm daemon also supports the use of osso-systemui-alarm to show an alarm dialog for the event, which can be followed by a power up device confirmation dialog, if the device is in the hidden power-up mode.
Communication with the alarmd daemon is performed through D-Bus. It listens to both system and session buses. The easiest way is to use the C client API library, which offers straightforward API to modify the queue.
The alarm queue used by the alarmd daemon is stored in file alarm_queue.ini, located in /var/cache/alarmd directory.
1. Alarm Events
Alarm events and the functions to manage them are defined in libalarm.h header file located in /usr/include/alarmd. It is a part of the libalarm-dev debian package.
Alarm event structures contains basic information about the alarm such as:
- Application identifier string which can be used to limit queries only to alarms set by some specific application. Must be specified for all alarms.
- Alarm message string which specifies the message body to be show in the alarm dialog. Should be specified for alarms that show dialog.
- Requested triggering time, which can be set in absolute terms (time_t) or using broken down time (struct tm). Timezone can also be specified for the latter. Must be specified for non-recurring alarms, can be set for recurring alarms too.
- Recurrence control: the number of times the alarm should be repeated and the recurrence period - either in terms of absolute seconds (say, every 2 hours) or based on clock / date (say, 10:00 every Friday the 13th).
- Actions to be taken when alarm is triggered. At least one must be specified. If one or more actions are specified to take effect after user interaction a dialog will be shown with button showing the action label for each of such action.
Additionally alarm events can have string, integer, or time attributes set by the client. These can be used to store some application data along with the alarm, or to convey extra data to system ui dialog service - the textdomain for text translation for example.
Once added to the queue, each alarm event is identified by a unique key, also known as a "cookie". The cookies can be used for deleting events, or retrieving information about a specific event.
2. Managing Alarm Events
2.1 Adding Alarm Event to Queue
To add a new alarm event in to the queue, you need to set up a client side alarm event structure and send it to alarmd using alarmd_event_add() function.
The alarmd_event_add() will perform sanity checking for the alarm event before sending it over to alarmd. If inconsistencies are noted, warnings and errors will be written to stderr. Any errors will make the alarm rejected before it is sent over to alarmd.
All alarms must have an non-empty application identifier string and at least one alarm action item.
For recurring alarms there must be either simple recurrency period or the alarm_recur_t items must be valid.
For non-recurring alarms the triggering time must evaluate to a time in the future.
Addional checks include things like having all necessary dbus parameters filled in if dbus action is requested.
Example: Adding simple two button dialog 10 seconds from now
#include <alarmd/libalarm.h>
#define APPID "example-apps"
static cookie_t add_two_button_alarm(void)
{
cookie_t cookie = 0;
alarm_event_t *eve = 0;
alarm_action_t *act = 0;
/* Create alarm event structure, set application
* identifier and dialog message */
eve = alarm_event_create();
alarm_event_set_alarm_appid(eve, APPID);
alarm_event_set_message(eve, "Example Message");
/* Use absolute time triggering, show dialog
* ten seconds from now */
eve->alarm_time = time(0) + 10;
/* Add stop button action */
act = alarm_event_add_actions(eve, 1);
alarm_action_set_label(act, "Stop");
act->flags |= ALARM_ACTION_WHEN_RESPONDED;
act->flags |= ALARM_ACTION_TYPE_NOP;
/* Add snooze button action */
act = alarm_event_add_actions(eve, 1);
alarm_action_set_label(act, "Snooze");
act->flags |= ALARM_ACTION_WHEN_RESPONDED;
act->flags |= ALARM_ACTION_TYPE_SNOOZE;
/* Send the alarm to alarmd */
cookie = alarmd_event_add(eve);
/* Free all dynamic memory associated with the
* alarm event */
alarm_event_delete(eve);
return cookie;
}
2.2 Fetching Details of Alarm Event
Having set an alarm event, it is not possible to edit its details anymore, but it is possible to fetch the alarm details using the alarmd_event_get() function, passing the event cookie as parameter.
Example: Showing some alarm event details
static void show_alarm_details(cookie_t cookie)
{
alarm_event_t *eve = 0;
alarm_action_t *act = 0;
time_t tmo = time(0);
if( (eve = alarmd_event_get(cookie)) == 0 )
{
printf("unable to get details for cookie=%ld\n", (long)cookie);
goto cleanup;
}
printf("cookie %ld:\n", (long)cookie);
tmo -= eve->trigger;
printf("appid = %s\n", alarm_event_get_alarm_appid(eve));
printf("trigger = T%+ld, %s", (long)tmo, ctime(&eve->trigger));
printf("message = %s\n", alarm_event_get_message(eve));
for( int i = 0; (act = alarm_event_get_action(eve, i)) != 0; ++i )
{
printf("action%d.label = %s\n", i, alarm_action_get_label(act));
}
cleanup:
/* Free all dynamic memory associated with the
* alarm event */
alarm_event_delete(eve);
}
2.3 Deleting Alarm Event
To delete an alarm event, the alarmd_event_del() function needs to be called, passing the event cookie as a parameter.
static void delete_alarm(cookie_t cookie)
{
if( alarmd_event_del(cookie) == -1 )
{
printf("unable to delete cookie=%ld\n", (long)cookie);
}
else
{
printf("deleted cookie=%ld\n", (long)cookie);
}
}
2.4 Querying Multiple Alarm Events
The alarmd_event_query() function can be used for querying alarms in the queue.
static void list_alarms()
{
cookie_t *list = 0;
cookie_t cookie = 0;
alarm_event_t *eve = 0;
if( (list = alarmd_event_query(0,0, 0,0, APPID)) == 0 )
{
printf("query failed\n");
goto cleanup;
}
for( int i = 0; (cookie = list[i]) != 0; ++i )
{
alarm_event_delete(eve);
if( (eve = alarmd_event_get(cookie)) == 0 )
{
printf("unable to get details for cookie=%ld\n", (long)cookie);
continue;
}
printf("cookie: %ld, %s", cookie, ctime(&eve->trigger));
}
cleanup:
free(list);
alarm_event_delete(eve);
}
2.5 Modifying an Existing Alarm Event
If details of some already queued event need to be modified, this can be done by deleting the existing alarm and then adding new modified alarm.
This process can be made faster and safer by using the alarmd_event_update() function, which does the delete old and new alarm in one D-Bus transaction.
static cookie_t update_alarm(cookie_t cookie)
{
cookie_t result = -1;
alarm_event_t *eve = 0;
if( (eve = alarmd_event_get(cookie)) == 0 )
{
printf("unable to get details for cookie=%ld\n", (long)cookie);
goto cleanup;
}
alarm_event_set_message(eve, "Updated Message");
eve->alarm_time = time(0) + 20;
result = alarmd_event_update(eve);
cleanup:
alarm_event_delete(eve);
return result;
}
3 Handling Dynamic Memory
To make client software forward compatible all alarm events and associated action, recurrence and attribute items should be allocated using functions provided by libalarm.
Dynamically allocated member data should be accessed using the set/get functions provided by libalarm.
Dynamically allocated structures should be released using the delete functions provided by libalarm.
Please note that alarm_event_delete() will assume that the above rules are followed and will try release all dynamic data associated with the alarm event structure.
Thus avoid writing client code that:
- Evaluates the size of libalarm structs at compile time
alarm_event_t eve;
alarm_event_t *eve = calloc(1, sizeof *eve);
- Direcly sets dynamic member data
eve = alarm_event_create();
self->alarm_appid = (char *)"myapp";
Might leak memory, might corrupt heap.
eve = alarm_event_create();
self->alarm_appid = strdup("myapp");
Might leak memory, might cause heap corruption in later versions of libalarm.
4 Localized Strings
By default systemui dialog service will try to translate all text strings using gettext() API in hildon libraries textdomain.
If you wish to use custom translation textdomain you should set the 'textdomain' attribute accordingly:
Example: two button alarm dialog with textdomain specified
#define TEXTDOMAIN "mydomain"
#define MESSAGE_KEY "translatable message text"
#define STOP_BUTTON_KEY "translatable stop text"
#define SNOOZE_BUTTON_KEY "translatable snooze text"
static cookie_t add_two_button_alarm_with_textdomain(void)
{
alarm_event_t *eve = 0;
alarm_action_t *act = 0;
cookie_t cookie = 0;
/* Create alarm event structure, set application
* identifier and dialog message */
eve = alarm_event_create();
alarm_event_set_alarm_appid(eve, APPID);
alarm_event_set_message(eve, MESSAGE_KEY);
/* Set the textdomain to be used while making
* translation lookups */
alarm_event_set_attr_string(eve, "textdomain", TEXTDOMAIN);
/* Use absolute time triggering, show dialog
* ten seconds from now */
eve->alarm_time = time(0) + 10;
/* Add stop button action */
act = alarm_event_add_actions(eve, 1);
alarm_action_set_label(act, STOP_BUTTON_KEY);
act->flags |= ALARM_ACTION_WHEN_RESPONDED;
act->flags |= ALARM_ACTION_TYPE_NOP;
/* Add snooze button action */
act = alarm_event_add_actions(eve, 1);
alarm_action_set_label(act, SNOOZE_BUTTON_KEY);
act->flags |= ALARM_ACTION_WHEN_RESPONDED;
act->flags |= ALARM_ACTION_TYPE_SNOOZE;
/* Send the alarm to alarmd */
cookie = alarmd_event_add(eve);
/* Free all dynamic memory associated with the
* alarm event */
alarm_event_delete(eve);
return cookie;
}
5 Obtaining more information
Please constult libalarm API documentation for more information.