Documentation/Maemo 5 Developer Guide/Using Generic Platform Components/Alarm Framework

Contents

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 topic 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.


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.


Managing Alarm Events

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;
}

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);
}


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);
}
}


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);
}


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;
}

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");


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;
}

Alarm Event Demo/examples

Documentation is great, but for many people seeing some real world examples of how to actually do things with alarmD is best. Here are some common tasks broken down and commented as best I can.

Executing a command

One of the most common uses for alarmD is to act like Cron, and trigger a script/app to run at certain times. Here's an example of how to do that:

test

/************************************************
* testSetAlarm()
* 
* Test function to try and set an alarm
************************************************/

int testSetAlarm() {
       //In C you have to declare the things you're using first...
	//This is the "alarm event" structure/object I'll use. It's empty right now.
	alarm_event_t *newEvent = 0;
	//This is the "alarm event action" structure/object that I'll use. It's empty right now.
	alarm_action_t *act = 0;
	
	//AlarmD will return a unique "alarm cookie" if successful; you can use this later to identify and
	//manipulate your alarm, or just ignore it.
	cookie_t resultCookie;
		
	//Actual code begins here...
	
	//This is step 1: create a new alarm event object.
	newEvent = alarm_event_create();	//Create the default alarm struct.
	
	//This is step 2:Set the APP ID
	alarm_event_set_alarm_appid(newEvent, "MYAPP");
	
	//This is step 3: Set the title
	alarm_event_set_title(newEvent, "testAlarm");

	//Now for the tricky part, step 4: timing.
	/* If you're dealing with re-curring alarms, there are two ways to do this. The "old way" is to simply
	set how many times you want to recurr, and how many seconds between each. I use this way because it's easier
	for simple recurrence
	*/
	//"Old" way
	//Set the # of seconds between recurrences. Let's say once an hour for example (60 seconds * 60 minutes = 1 hour)
	newEvent->recur_secs = 60 * 60;
	
	//How many times should the event occur? If you set this to 0, it will reoccur infinitely.
	newEvent->recur_count =0;
	
	//When will the event occur for the very first time? After this point  it will occur every 3600 seconds...
	//For this demo I'll make the first alarm happen 1 hour from the  current time. This "time" is in standard UNIX time format,
	//so you need # of seconds since Unix Epoch. In C you do this by calling time(NULL); I then add 3600 to make the time 1 hour
	//ahead of current time.
	newEvent->alarm_time = (time_t) time(NULL) + 3600;
		
	/* You can also use the "new way" of setting up timing on events, which allows you to use a time structure instead of just
	a fixed # of seconds. So you can make an alarm happen every monday, for example, or every monday and thursday. See the information earlier in the Wiki  entry for details on this */
	//Now that our event is setup, we need to add an action to it so that the alarm actually "does" something.
	
	//Add 1 action to our alarm event, and assign it to the "act" variable
	act = alarm_event_add_actions(newEvent, 1);
	
	//Now setup that action to do something. To do this we have to set the action "flags". You can find all of them with explanations
	// in the docs, here: http://maemo.org/api_refs/5.0/5.0-final/libalarm/libalarm_8h.html#cc8e6f439d134448001132132476683c910f7626ec85a4170659b53fa2f0abc7	
	//Setup this action to be an "Execute command" one
	act->flags = ALARM_ACTION_WHEN_TRIGGERED | ALARM_ACTION_TYPE_EXEC; 
	//Now all you have to do is tell the action which command you want to "execute"
	alarm_action_set_exec_command(act, "/usr/sbin/myAwesomeCommand.sh");
	//Finally with everything setup, try to add your event to the alarm queue
	resultCookie = alarmd_event_add(newEvent);
}


Triggering DBus actions

Triggering Dbus actions is very similar to executing a command as above; the main difference is how you setup the alarm_event_action. Here's a simple DBus example:

test

/************************************************
* testSetAlarm()
* 
* Test function to try and set an alarm
************************************************/
int testSetAlarm() {
	//In C you have to declare the things you're using first...
	
	//This is the "alarm event" structure/object I'll use. It's empty right now.
	alarm_event_t *newEvent = 0;
	//This is the "alarm event action" structure/object that I'll use. It's empty right now.
	alarm_action_t *act = 0;
	
	//AlarmD will return a unique "alarm cookie" if successful; you can use this later to identify and
	//manipulate your alarm, or just ignore it.
	cookie_t resultCookie;
	
	
	//Actual code begins here...
	
	//This is step 1: create a new alarm event object.
	newEvent = alarm_event_create();	//Create the default alarm struct.
	
	//This is step 2:Set the APP ID
	alarm_event_set_alarm_appid(newEvent, "MYAPP");
	
	//This is step 3: Set the title
	alarm_event_set_title(newEvent, "testAlarm");

	//Now for the tricky part, step 4: timing.
	/* If you're dealing with re-curring alarms, there are two ways to do this. The "old way" is to simply
	set how many times you want to recurr, and how many seconds between each. I use this way because it's easier
	for basic tasks
	*/
	//"Old" way
	//Set the # of seconds between recurrences. Let's say once an hour for example (60 seconds * 60 minutes = 1 hour)
	newEvent->recur_secs = 60 * 60;
	//How many times should the event occur? If you set this to 0, it will reoccur infinitely.
	newEvent->recur_count =0;
	//When will the event occur for the very first time? After this point it will occur every 3600 seconds...
	//For this demo I'll make the first alarm happen 1 hour from the current time. This "time" is in standard UNIX time format,
	//so you need # of seconds since Unix Epoch. In C you do this by calling time(NULL); I then add 3600 to make the time 1 hour
	//ahead of current time.
	newEvent->alarm_time = (time_t) time(NULL) + 3600;
		
	//Setup the event to boot the device if it's not powered on
	newEvent->flags = ALARM_EVENT_BOOT;
	//Now that our event is setup, we need to add an action to it so that the alarm actually "does" something.
	
	//Add 1 action to our alarm event, and assign it to the "act" variable
	act = alarm_event_add_actions(newEvent, 1);
	
	//Now setup that action to do something. To do this we have to set the action "flags". You can find all of them with explanations
	// in the docs, here: http://maemo.org/api_refs/5.0/5.0-final/libalarm/libalarm_8h.html#cc8e6f439d134448001132132476683c910f7626ec85a4170659b53fa2f0abc7
	//Setup this action to be an "DBus command" one; also set it up to use DBUS auto-activation.
	act->flags = ALARM_ACTION_WHEN_TRIGGERED | ALARM_ACTION_DBUS_USE_ACTIVATION | ALARM_ACTION_TYPE_DBUS;
	
	//Setup the DBus params for this action
	alarm_action_set_dbus_interface(act, "org.maemo.testApp");
	alarm_action_set_dbus_service(act, "org.maemo.testApp");
	alarm_action_set_dbus_path(act, "/org/maemo/testApp");
	alarm_action_set_dbus_name(act, "triggerAlarm");
		
	//Finally with everything setup, try to add your event to the alarm queue
	resultCookie = alarmd_event_add(newEvent);
	
}


Obtaining more information

For more information, see libalarm API documentation.