/**
* @file alarm.c
* Alarm interface module for the Mode Control Entity
*
* Copyright © 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2013-2019 Jolla Ltd.
*
* @author David Weinehall
* @author Jukka Turunen
* @author Simo Piiroinen
*
* mce is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
*
* mce is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with mce. If not, see .
*/
#include "../mce.h"
#include "../mce-log.h"
#include "../mce-dbus.h"
#include "../mce-wakelock.h"
#include
/* Alarm UI related D-Bus constants */
typedef enum {
VISUAL_REMINDER_ON_SCREEN,
VISUAL_REMINDER_NOT_ON_SCREEN,
VISUAL_REMINDER_ON_SCREEN_NO_SOUND
} visual_reminders_status;
#define VISUAL_REMINDERS_SERVICE "com.nokia.voland"
#define VISUAL_REMINDERS_SIGNAL_IF "com.nokia.voland.signal"
#define VISUAL_REMINDERS_SIGNAL_PATH "/com/nokia/voland/signal"
#define VISUAL_REMINDER_STATUS_SIG "visual_reminders_status"
/* Timed alarm queue related D-Bus constants */
#define TIMED_DBUS_SERVICE "com.nokia.time"
#define TIMED_DBUS_OBJECT "/com/nokia/time"
#define TIMED_DBUS_INTERFACE "com.nokia.time"
#define TIMED_QUEUE_STATUS_SIG "next_bootup_event"
/** Module name */
#define MODULE_NAME "alarm"
/** Maximum number of alarm D-Bus objects requesting alarm mode */
#define ALARM_MAX_MONITORED 5
/** Pseudo-wakelock held while expecting alarm ui to start up */
#define ALARM_IMMINENT_WAKELOCK_NAME "alarm_imminent"
/** Maximum time given for alarm ui to start up
*
* This needs to be long enough to allow timed to make at least one
* retry after timeout from alarm ui invocation D-Bus method call,
* i.e. must be longer than 25 seconds.
*/
#define ALARM_IMMINENT_TIMEOUT_MS (60*1000)
/** Functionality provided by this module */
static const gchar *const provides[] = { MODULE_NAME, NULL };
/** Alarm UI D-Bus service monitor list */
static GSList *alarm_owner_monitor_list = NULL;
/** Alarm queue D-Bus service monitor list */
static GSList *queue_owner_monitor_list = NULL;
/** Module information */
G_MODULE_EXPORT module_info_struct module_info = {
/** Name of the module */
.name = MODULE_NAME,
/** Module provides */
.provides = provides,
/** Module priority */
.priority = 250
};
/* Module functions */
static void alarm_sync_state_to_datapipe(alarm_ui_state_t state);
static gboolean alarm_owner_monitor_dbus_cb (DBusMessage *const msg);
static void setup_alarm_dbus_monitor (const gchar *sender);
static gboolean queue_owner_monitor_dbus_cb (DBusMessage *const sig);
static void queue_monitor_setup (const char *sender, bool monitor);
static gboolean alarm_dialog_status_dbus_cb (DBusMessage *const msg);
static gboolean alarm_queue_status_dbus_cb (DBusMessage *const sig);
static void mce_alarm_init_dbus (void);
static void mce_alarm_quit_dbus (void);
const gchar *g_module_check_init (GModule *module);
void g_module_unload (GModule *module);
static void alarm_sync_state_to_datapipe(alarm_ui_state_t state)
{
if( datapipe_get_gint(alarm_ui_state_pipe) == state )
goto EXIT;
mce_log(LL_DEVEL, "alarm state = %s", alarm_state_repr(state));
datapipe_exec_full(&alarm_ui_state_pipe,
GINT_TO_POINTER(state));
EXIT:
return;
}
/**
* Alarm D-Bus service monitor callback.
*
* @param msg The D-Bus message
* @return TRUE on success, FALSE on failure
*/
static gboolean alarm_owner_monitor_dbus_cb(DBusMessage *const msg)
{
gboolean status = FALSE;
const gchar *old_name;
const gchar *new_name;
const gchar *service;
gssize retval;
DBusError error = DBUS_ERROR_INIT;
/* Extract result */
if (dbus_message_get_args(msg, &error,
DBUS_TYPE_STRING, &service,
DBUS_TYPE_STRING, &old_name,
DBUS_TYPE_STRING, &new_name,
DBUS_TYPE_INVALID) == FALSE) {
mce_log(LL_ERR,
"Failed to get argument from %s.%s; %s",
"org.freedesktop.DBus", "NameOwnerChanged",
error.message);
goto EXIT;
}
retval = mce_dbus_owner_monitor_remove(service, &alarm_owner_monitor_list);
if (retval == 0) {
/* We didn't get alarm off from the same service before it
* unregistered (e.g. due crash), turn alarm state off so at
* least powerkey works again.
*/
mce_log(LL_DEBUG, "visual reminder service died, "
"turning off alarm state");
alarm_sync_state_to_datapipe(MCE_ALARM_UI_OFF_INT32);
}
status = TRUE;
EXIT:
dbus_error_free(&error);
return status;
}
/**
* Install alarm D-Bus service monitor callback.
*
* @param sender sender D-Bus address
*/
static void setup_alarm_dbus_monitor(const gchar* sender)
{
mce_log(LL_DEBUG, "adding dbus monitor for: '%s'" ,sender);
/* No need to check return value, if it does not succeed, not much
* we can do / fall back to
*/
mce_dbus_owner_monitor_add(sender,
alarm_owner_monitor_dbus_cb,
&alarm_owner_monitor_list,
ALARM_MAX_MONITORED);
}
/** Callback for handling alarm queue name owner changed signals
*
* @param sig The D-Bus message
*
* @return TRUE
*/
static gboolean queue_owner_monitor_dbus_cb(DBusMessage *const sig)
{
const char *name = 0;
const char *prev = 0;
const char *curr = 0;
DBusError error = DBUS_ERROR_INIT;
if( !dbus_message_get_args(sig, &error,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_STRING, &prev,
DBUS_TYPE_STRING, &curr,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "Failed to parse arguments: %s: %s",
error.name, error.message);
goto EXIT;
}
queue_monitor_setup(name, false);
EXIT:
dbus_error_free(&error);
return TRUE;
}
/** Install/remove alarm queue D-Bus name owner monitor
*
* @param sender Private D-Bus name to monitor
* @param monitor true/false to start/stop monitoring
*/
static void queue_monitor_setup(const char *sender, bool monitor)
{
if( monitor ) {
gssize cnt = mce_dbus_owner_monitor_add(sender,
queue_owner_monitor_dbus_cb,
&queue_owner_monitor_list,
ALARM_MAX_MONITORED);
if( cnt != -1 ) {
/* A owner monitor was added/renewed */
mce_log(LL_DEVEL, "monitoring dbus name: %s", sender);
mce_wakelock_obtain(ALARM_IMMINENT_WAKELOCK_NAME,
ALARM_IMMINENT_TIMEOUT_MS);
}
}
else {
gssize cnt = mce_dbus_owner_monitor_remove(sender,
&queue_owner_monitor_list);
if( cnt == 0 ) {
/* The last monitor was removed */
mce_log(LL_DEVEL, "all dbus name monitors removed");
mce_wakelock_release(ALARM_IMMINENT_WAKELOCK_NAME);
}
}
}
/**
* D-Bus callback for the alarm dialog status signal
*
* @param msg The D-Bus message
* @return TRUE on success, FALSE on failure
*/
static gboolean alarm_dialog_status_dbus_cb(DBusMessage *const msg)
{
alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_INVALID_INT32;
gboolean status = FALSE;
const gchar *sender = dbus_message_get_sender(msg);
DBusError error = DBUS_ERROR_INIT;
dbus_int32_t dialog_status;
mce_log(LL_DEVEL, "Received alarm dialog status signal from %s",
mce_dbus_get_name_owner_ident(sender));
if (dbus_message_get_args(msg, &error,
DBUS_TYPE_INT32, &dialog_status,
DBUS_TYPE_INVALID) == FALSE) {
// XXX: should we return an error instead?
mce_log(LL_CRIT,
"Failed to get argument from %s.%s: %s",
VISUAL_REMINDERS_SIGNAL_IF,
VISUAL_REMINDER_STATUS_SIG,
error.message);
goto EXIT;
}
/* Convert alarm dialog status to to MCE alarm ui enum */
switch (dialog_status) {
case VISUAL_REMINDER_ON_SCREEN:
setup_alarm_dbus_monitor(sender);
alarm_ui_state = MCE_ALARM_UI_RINGING_INT32;
break;
case VISUAL_REMINDER_ON_SCREEN_NO_SOUND:
setup_alarm_dbus_monitor(sender);
alarm_ui_state = MCE_ALARM_UI_VISIBLE_INT32;
break;
case VISUAL_REMINDER_NOT_ON_SCREEN:
mce_dbus_owner_monitor_remove(sender, &alarm_owner_monitor_list);
alarm_ui_state = MCE_ALARM_UI_OFF_INT32;
break;
default:
mce_log(LL_ERR,
"Received invalid alarm dialog status; "
"defaulting to OFF");
alarm_ui_state = MCE_ALARM_UI_OFF_INT32;
break;
}
alarm_sync_state_to_datapipe(alarm_ui_state);
status = TRUE;
EXIT:
dbus_error_free(&error);
return status;
}
/** D-Bus callback for the alarm queue status signal
*
* @param sig The D-Bus signal message
*
* @return TRUE
*/
static gboolean alarm_queue_status_dbus_cb(DBusMessage *const sig)
{
dbus_int32_t bootup = 0;
dbus_int32_t normal = 0;
DBusError error = DBUS_ERROR_INIT;
const gchar *sender = dbus_message_get_sender(sig);
mce_log(LL_DEVEL, "Received alarm queue status signal from %s",
mce_dbus_get_name_owner_ident(sender));
if( !dbus_message_get_args(sig, &error,
DBUS_TYPE_INT32, &bootup,
DBUS_TYPE_INT32, &normal,
DBUS_TYPE_INVALID) ) {
mce_log(LL_ERR, "Failed to parse arguments: %s: %s",
error.name, error.message);
goto EXIT;
}
/* DSME makes sure the device wakes up from suspend at
* the time when timed needs to trigger an alarm. MCE
* needs to make sure device does not get back to suspend
* before alarm ui has had sufficient time to start up
* and signal alarm dialog state.
*
* Timeds sends alarm queue status signal where the "next
* alarm time" has value of one when alarm has been triggered
* and alarm ui will be started up.
*/
queue_monitor_setup(sender, bootup == 1 || normal == 1);
EXIT:
dbus_error_free(&error);
return TRUE;
}
/** Array of dbus message handlers */
static mce_dbus_handler_t alarm_dbus_handlers[] =
{
/* signals */
{
.interface = VISUAL_REMINDERS_SIGNAL_IF,
.name = VISUAL_REMINDER_STATUS_SIG,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = alarm_dialog_status_dbus_cb,
},
{
.interface = TIMED_DBUS_INTERFACE,
.name = TIMED_QUEUE_STATUS_SIG,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = alarm_queue_status_dbus_cb,
},
/* sentinel */
{
.interface = 0
}
};
/** Add dbus handlers
*/
static void mce_alarm_init_dbus(void)
{
mce_dbus_handler_register_array(alarm_dbus_handlers);
}
/** Remove dbus handlers
*/
static void mce_alarm_quit_dbus(void)
{
mce_dbus_handler_unregister_array(alarm_dbus_handlers);
}
/**
* Init function for the alarm interface module
*
* @todo XXX status needs to be set on error!
*
* @param module Unused
* @return NULL on success, a string with an error message on failure
*/
G_MODULE_EXPORT
const gchar *g_module_check_init(GModule *module)
{
(void)module;
/* Add dbus handlers */
mce_alarm_init_dbus();
return NULL;
}
/**
* Exit function for the alarm interface module
*
* @todo D-Bus unregistration
*
* @param module Unused
*/
G_MODULE_EXPORT
void g_module_unload(GModule *module)
{
(void)module;
/* Remove name ownership monitors */
mce_dbus_owner_monitor_remove_all(&alarm_owner_monitor_list);
mce_dbus_owner_monitor_remove_all(&queue_owner_monitor_list);
/* Remove dbus handlers */
mce_alarm_quit_dbus();
return;
}