/**
* @file powerkey.c
* Power key logic for the Mode Control Entity
*
* Copyright © 2004-2011 Nokia Corporation and/or its subsidiary(-ies).
* Copyright © 2013-2019 Jolla Ltd.
*
* @author David Weinehall
* @author Tapio Rantala
* @author Santtu Lakkala
* @author Irina Bezruk
* @author Simo Piiroinen
* @author Kimmo Lindholm
* @author Andrew den Exter
*
* 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 "powerkey.h"
#include "tklock.h"
#include "evdev.h"
#include "mce-log.h"
#include "mce-lib.h"
#include "mce-setting.h"
#include "mce-common.h"
#include "mce-dbus.h"
#include "mce-dsme.h"
#include "modules/doubletap.h"
#include "systemui/dbus-names.h"
#ifdef ENABLE_WAKELOCKS
# include "libwakelock.h"
#endif
#include
#include
#include
#include
#include
#include
#include
#include
/* ========================================================================= *
* OVERVIEW
*
* There is a predefined set of actions. Of these two are dbus actions
* that by default make mce broadcast dbus signals, but can be configured
* to make any dbus method call with optional string argument.
*
* Any combination of these actions can be bound to:
* - single power key press
* - double power key press
* - long power key press
*
* The selected actions are executed in a fixed order and actions that
* are common to both single and double press are executed immediately
* after powerkey is released. This allows double press configuration
* to extend what would be done with single press without causing
* delays for single press handling.
*
* Separate combinations are used depending on whether the
* display is on or off during the 1st power key press.
*
* The build-in defaults are as follows
*
* From display off:
* - single press - turns display on
* - double press - turns display on and hides lockscreen (but not device lock)
* - long press - does nothing
*
* From display on:
* - single press - turns display off and activates locksreen
* - double press - turns display off, activates locksreen and locks device
* - long press - initiates shutdown (if lockscreen is not active)
*
* Effectively this is just as before, except for the double press
* actions to apply device lock / wake up to home screen.
* ========================================================================= */
/* ========================================================================= *
* CONSTANTS
* ========================================================================= */
#define MODULE_NAME "powerkey"
/* ========================================================================= *
* PROTOTYPES
* ========================================================================= */
/* ------------------------------------------------------------------------- *
* MISC_UTIL
* ------------------------------------------------------------------------- */
/* Null tolerant string equality predicate
*
* @param s1 string
* @param s2 string
*
* @return true if both s1 and s2 are null or same string, false otherwise
*/
static inline bool eq(const char *s1, const char *s2)
{
return (s1 && s2) ? !strcmp(s1, s2) : (s1 == s2);
}
/** String is NULL or empty predicate
*/
static inline bool empty(const char *s)
{
return s == 0 || *s == 0;
}
static char *pwrkey_get_token(char **ppos);
static bool pwrkey_create_flagfile(const char *path);
static bool pwrkey_delete_flagfile(const char *path);
/* ------------------------------------------------------------------------- *
* PS_OVERRIDE
*
* Provides escape from stuck proximity sensor.
* ------------------------------------------------------------------------- */
/** [setting] Power key press count for proximity sensor override */
static gint pwrkey_ps_override_count = MCE_DEFAULT_POWERKEY_PS_OVERRIDE_COUNT;
static guint pwrkey_ps_override_count_setting_id = 0;
/** [setting] Maximum time between power key presses for proximity sensor override */
static gint pwrkey_ps_override_timeout = MCE_DEFAULT_POWERKEY_PS_OVERRIDE_TIMEOUT;
static guint pwrkey_ps_override_timeout_setting_id = 0;
static void pwrkey_ps_override_evaluate(void);
/* ------------------------------------------------------------------------- *
* ACTION_EXEC
*
* Individual actions that can be taken.
* ------------------------------------------------------------------------- */
static gint pwrkey_action_blank_mode = MCE_DEFAULT_POWERKEY_BLANKING_MODE;
static guint pwrkey_action_blank_mode_setting_id = 0;
static void pwrkey_action_vibrate (void);
static void pwrkey_action_shutdown (void);
static void pwrkey_action_tklock (void);
static void pwrkey_action_blank (void);
static void pwrkey_action_unblank (void);
static void pwrkey_action_tkunlock (void);
static void pwrkey_action_tkunlock2(void);
static void pwrkey_action_devlock (void);
static void pwrkey_action_dbus1 (void);
static void pwrkey_action_dbus2 (void);
static void pwrkey_action_dbus3 (void);
static void pwrkey_action_dbus4 (void);
static void pwrkey_action_dbus5 (void);
static void pwrkey_action_dbus6 (void);
static void pwrkey_action_dbus7 (void);
static void pwrkey_action_dbus8 (void);
static void pwrkey_action_dbus9 (void);
static void pwrkey_action_dbus10 (void);
static void pwrkey_action_nop (void);
/* ------------------------------------------------------------------------- *
* ACTION_SETS
*
* Handle sets of individual actions.
* ------------------------------------------------------------------------- */
typedef struct
{
const char *name;
void (*func)(void);
} pwrkey_bitconf_t;
static void pwrkey_mask_execute_cb (gpointer aptr);
static void pwrkey_mask_execute (uint32_t mask);
static uint32_t pwrkey_mask_from_name (const char *name);
static uint32_t pwrkey_mask_from_names (const char *names);
static gchar *pwrkey_mask_to_names (uint32_t mask);
/* ------------------------------------------------------------------------- *
* GESTURE_FILTERING
* ------------------------------------------------------------------------- */
/** Touchscreen gesture (doubletap etc) enable mode */
static gint pwrkey_gestures_enable_mode = MCE_DEFAULT_DOUBLETAP_MODE;
static guint pwrkey_gestures_enable_mode_cb_id = 0;
static bool pwrkey_gestures_allowed(bool synthesized);
static bool pwrkey_fpwakeup_allowed(void);
/* ------------------------------------------------------------------------- *
* PWRKEY_UNBLANK
* ------------------------------------------------------------------------- */
/** Enumeratio of possible unblank allowed predicates */
typedef enum
{
/** Apply powerkey press rules */
PWRKEY_UNBLANK_PREDICATE_POWERKEY,
/** Apply fingerprint wakeup rules */
PWRKEY_UNBLANK_PREDICATE_FPWAKEUP,
/** Apply rules for real gesture events */
PWRKEY_UNBLANK_PREDICATE_GESTURE_REAL,
/** Apply rules for synthetized gesture events */
PWRKEY_UNBLANK_PREDICATE_GESTURE_SYNTH,
} pwrkey_unblank_predicate_t;
/** Lookup table of unblank predicate names (for diagnostic logging) */
static const char * const pwrkey_unblank_predicate_name[] =
{
[PWRKEY_UNBLANK_PREDICATE_POWERKEY] = "powerkey",
[PWRKEY_UNBLANK_PREDICATE_FPWAKEUP] = "fpwakeup",
[PWRKEY_UNBLANK_PREDICATE_GESTURE_REAL] = "gesture_real",
[PWRKEY_UNBLANK_PREDICATE_GESTURE_SYNTH] = "gesture_synth",
};
/** Currently active unblanking allowed predicate */
static pwrkey_unblank_predicate_t pwrkey_unblank_predicate =
PWRKEY_UNBLANK_PREDICATE_POWERKEY;
static bool pwrkey_unblank_allowed (void);
static void pwrkey_unblank_set_predicate_cb(gpointer aptr);
static void pwrkey_unblank_set_predicate (pwrkey_unblank_predicate_t predicate);
/* ------------------------------------------------------------------------- *
* ACTION_TRIGGERING
* ------------------------------------------------------------------------- */
typedef struct
{
/** Actions common to single and double press */
uint32_t mask_common;
/** Actions for single press */
uint32_t mask_single;
/** Actions for double press */
uint32_t mask_double;
/** Actions for long press */
uint32_t mask_long;
} pwrkey_actions_t;
/** Actions when power key is pressed while display is on */
static pwrkey_actions_t pwrkey_actions_from_display_on = { 0, 0, 0, 0 };
/** Actions when power key is pressed while display is off */
static pwrkey_actions_t pwrkey_actions_from_display_off = { 0, 0, 0, 0 };
/** Actions on touch screen gestures */
static pwrkey_actions_t pwrkey_actions_from_gesture[POWERKEY_ACTIONS_GESTURE_COUNT] = {};
/** Currently selected power key actions; default to turning display on */
static pwrkey_actions_t *pwrkey_actions_now =
&pwrkey_actions_from_display_off;
static gchar *pwrkey_actions_single_on = 0;
static guint pwrkey_actions_single_on_setting_id = 0;
static gchar *pwrkey_actions_double_on = 0;
static guint pwrkey_actions_double_on_setting_id = 0;
static gchar *pwrkey_actions_long_on = 0;
static guint pwrkey_actions_long_on_setting_id = 0;
static gchar *pwrkey_actions_single_off = 0;
static guint pwrkey_actions_single_off_setting_id = 0;
static gchar *pwrkey_actions_double_off = 0;
static guint pwrkey_actions_double_off_setting_id = 0;
static gchar *pwrkey_actions_long_off = 0;
static guint pwrkey_actions_long_off_setting_id = 0;
/** Array of setting keys for configurable touchscreen gestures */
static const char * const pwrkey_actions_gesture_key[POWERKEY_ACTIONS_GESTURE_COUNT] =
{
MCE_SETTING_POWERKEY_ACTIONS_GESTURE0,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE1,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE2,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE3,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE4,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE5,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE6,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE7,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE8,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE9,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE10,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE11,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE12,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE13,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE14,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE15,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE16,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE17,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE18,
MCE_SETTING_POWERKEY_ACTIONS_GESTURE19,
};
/** Array of default values for configurable touchscreen gestures */
static const char * const pwrkey_actions_gesture_val[POWERKEY_ACTIONS_GESTURE_COUNT] =
{
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE0,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE1,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE2,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE3,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE4,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE5,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE6,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE7,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE8,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE9,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE10,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE11,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE12,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE13,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE14,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE15,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE16,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE17,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE18,
MCE_DEFAULT_POWERKEY_ACTIONS_GESTURE19,
};
/** Array of current values for configurable touchscreen gestures */
static gchar *pwrkey_actions_gesture [POWERKEY_ACTIONS_GESTURE_COUNT] = {};
static guint pwrkey_actions_gesture_setting_id[POWERKEY_ACTIONS_GESTURE_COUNT] = {};
static void pwrkey_actions_parse (pwrkey_actions_t *self, const char *names_single, const char *names_double, const char *names_long);
static void pwrkey_actions_do_gesture (size_t gesture);
static void pwrkey_actions_do_common (void);
static void pwrkey_actions_do_single_press (void);
static void pwrkey_actions_do_double_press (void);
static void pwrkey_actions_do_long_press (void);
static bool pwrkey_actions_update (const pwrkey_actions_t *self, gchar **names_single, gchar **names_double, gchar **names_long);
static bool pwrkey_actions_use_double_press(void);
static void pwrkey_actions_select (bool display_is_on);
/* ------------------------------------------------------------------------- *
* LONG_PRESS_TIMEOUT
*
* timer for telling apart short and long power key presses
* ------------------------------------------------------------------------- */
static gint pwrkey_long_press_delay = MCE_DEFAULT_POWERKEY_LONG_PRESS_DELAY;
static guint pwrkey_long_press_delay_setting_id = 0;
static guint pwrkey_long_press_timer_id = 0;
static gboolean pwrkey_long_press_timer_cb (gpointer aptr);
static void pwrkey_long_press_timer_start (void);
static bool pwrkey_long_press_timer_pending (void);
static bool pwrkey_long_press_timer_cancel (void);
/* ------------------------------------------------------------------------- *
* DOUBLE_PRESS_TIMEOUT
*
* timer for telling apart single and double power key presses
* ------------------------------------------------------------------------- */
static gint pwrkey_double_press_delay = MCE_DEFAULT_POWERKEY_DOUBLE_PRESS_DELAY;
static guint pwrkey_double_press_delay_setting_id = 0;
static guint pwrkey_double_press_timer_id = 0;
static gboolean pwrkey_double_press_timer_cb(gpointer aptr);
static bool pwrkey_double_press_timer_pending(void);
static bool pwrkey_double_press_timer_cancel(void);
static void pwrkey_double_press_timer_start(void);
/* ------------------------------------------------------------------------- *
* NGFD_GLUE
* ------------------------------------------------------------------------- */
static const char *xngf_state_repr (NgfEventState state);
static void xngf_status_cb (NgfClient *client, uint32_t event_id, NgfEventState state, void *userdata);
static bool xngf_create_client (void);
static void xngf_delete_client (void);
static void xngf_play_event (const char *event_name);
static void xngf_init (void);
static void xngf_quit (void);
/* ------------------------------------------------------------------------- *
* DBUS_ACTIONS
*
* emitting dbus signal from mce / making dbus method call to some service
* ------------------------------------------------------------------------- */
/** Flag file for: Possibly dangerous dbus action in progress
*
* Used for resetting dbus action config if it causes mce to crash.
*
* Using tmpfs is problematic from permissions point of view, but we do
* not want to cause flash wear by this either.
*/
static const char pwrkey_dbus_action_flag[] =
"/tmp/mce-powerkey-dbus-action.flag";
typedef struct
{
const char *setting_key;
const char *setting_def;
gchar *setting_val;
guint setting_id;
char *destination;
char *object;
char *interface;
char *member;
char *argument;
} pwrkey_dbus_action_t;
static pwrkey_dbus_action_t pwrkey_dbus_action[POWEKEY_DBUS_ACTION_COUNT] =
{
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION1,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION1,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION2,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION2,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION3,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION3,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION4,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION4,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION5,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION5,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION6,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION6,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION7,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION7,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION8,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION8,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION9,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION9,
.setting_val = 0,
.setting_id = 0,
},
{
.setting_key = MCE_SETTING_POWERKEY_DBUS_ACTION10,
.setting_def = MCE_DEFAULT_POWERKEY_DBUS_ACTION10,
.setting_val = 0,
.setting_id = 0,
},
};
static void pwrkey_dbus_action_clear(pwrkey_dbus_action_t *self);
static void pwrkey_dbus_action_reset(pwrkey_dbus_action_t *self);
static bool pwrkey_dbus_action_is_methodcall(const pwrkey_dbus_action_t *self);
static bool pwrkey_dbus_action_is_signal(const pwrkey_dbus_action_t *self);
static void pwrkey_dbus_action_parse(pwrkey_dbus_action_t *self);
static gchar *pwrkey_dbus_action_to_string(const pwrkey_dbus_action_t *self);
static void pwrkey_dbus_action_sanitize(pwrkey_dbus_action_t *self);
static void pwrkey_dbus_action_configure(size_t action_id, bool force_reset);
static void pwrkey_dbus_action_execute(size_t index);
/* ------------------------------------------------------------------------- *
* POWER_KEY_STATE_MACHINE
*
* main logic for tracking power key presses and associated timers
*
* state transition graph can be generated from "powerkey.dot" file
* ------------------------------------------------------------------------- */
/** Diplay state when power key was pressed */
static display_state_t pwrkey_stm_display_state = MCE_DISPLAY_UNDEF;
/** [setting] Power key press enable mode */
static gint pwrkey_stm_enable_mode = MCE_DEFAULT_POWERKEY_MODE;
static guint pwrkey_stm_enable_mode_setting_id = 0;
static void pwrkey_stm_long_press_timeout (void);
static void pwrkey_stm_double_press_timeout (void);
static void pwrkey_stm_powerkey_pressed (void);
static void pwrkey_stm_powerkey_released (void);
static bool pwrkey_stm_ignore_action (void);
static bool pwrkey_stm_pending_timers (void);
static void pwrkey_stm_rethink_wakelock (void);
static void pwrkey_stm_store_initial_state (void);
static void pwrkey_stm_terminate (void);
/* ------------------------------------------------------------------------- *
* HOME_KEY_STATE_MACHINE
* ------------------------------------------------------------------------- */
typedef enum
{
HOMEKEY_STM_WAIT_PRESS = 0,
HOMEKEY_STM_WAIT_UNBLANK = 1,
HOMEKEY_STM_SEND_SIGNAL = 2,
HOMEKEY_STM_WAIT_RELEASE = 3,
} homekey_stm_t;
static const char *homekey_stm_repr (homekey_stm_t state);
static void homekey_stm_set_state (homekey_stm_t state);
static bool homekey_stm_exec_step (void);
static void homekey_stm_eval_state (void);
static void homekey_stm_set_pressed (bool pressed);
/* ------------------------------------------------------------------------- *
* DBUS_IPC
*
* handling incoming and outgoing dbus messages
* ------------------------------------------------------------------------- */
static void pwrkey_dbus_send_signal(const char *sig, const char *arg);
static gboolean pwrkey_dbus_trigger_event_cb(DBusMessage *const req);
static gboolean pwrkey_dbus_ignore_incoming_call_cb(DBusMessage *const req);
static void pwrkey_dbus_init(void);
static void pwrkey_dbus_quit(void);
/* ------------------------------------------------------------------------- *
* DYNAMIC_SETTINGS
*
* tracking powerkey related runtime changeable settings
* ------------------------------------------------------------------------- */
static gint pwrkey_setting_sanitize_id = 0;
static void pwrkey_setting_sanitize_action_masks(void);
static void pwrkey_setting_sanitize_dbus_actions(void);
static gboolean pwrkey_setting_sanitize_cb (gpointer aptr);
static void pwrkey_setting_sanitize_now (void);
static void pwrkey_setting_sanitize_later (void);
static void pwrkey_setting_sanitize_cancel (void);
static bool pwrkey_setting_handle_gesture (const GConfValue *gcv, guint id);
static void pwrkey_setting_cb (GConfClient *const gcc, const guint id, GConfEntry *const entry, gpointer const data);
static void pwrkey_setting_init (void);
static void pwrkey_setting_quit (void);
/* ------------------------------------------------------------------------- *
* DATAPIPE_HANDLING
*
* reacting to state changes / input from other mce modules
* ------------------------------------------------------------------------- */
static void pwrkey_datapipe_keypress_event_cb(gconstpointer const data);
static void pwrkey_datapipe_ngfd_service_state_cb(gconstpointer data);
static void pwrkey_datapipe_system_state_cb(gconstpointer data);
static void pwrkey_datapipe_devicelock_state_cb(gconstpointer data);
static void pwrkey_datapipe_display_state_curr_cb(gconstpointer data);
static void pwrkey_datapipe_display_state_next_cb(gconstpointer data);
static void pwrkey_datapipe_lid_sensor_filtered_cb(gconstpointer data);
static void pwrkey_datapipe_proximity_sensor_actual_cb(gconstpointer data);
static void pwrkey_datapipe_call_state_cb(gconstpointer data);
static void pwrkey_datapipe_alarm_ui_state_cb(gconstpointer data);
static void pwrkey_datapipe_devicelock_service_state_cb(gconstpointer data);
static void pwrkey_datapipe_enroll_in_progress_cb(gconstpointer data);
static void pwrkey_datapipe_ngfd_event_request_cb(gconstpointer data);
static void pwrkey_datapipe_init(void);
static void pwrkey_datapipe_quit(void);
/* ------------------------------------------------------------------------- *
* MODULE_INTEFACE
* ------------------------------------------------------------------------- */
gboolean mce_powerkey_init(void);
void mce_powerkey_exit(void);
/* ========================================================================= *
* MISC_UTIL
* ========================================================================= */
/** Parse element from comma separated string list
*/
static char *
pwrkey_get_token(char **ppos)
{
char *pos = *ppos;
char *beg = pos;
if( !pos )
goto cleanup;
for( ; *pos; ++pos ) {
if( *pos != ',' )
continue;
*pos++ = 0;
break;
}
cleanup:
return *ppos = pos, beg;
}
/** Create an empty flag file
*
* @param path Path to the file to create
*
* @return true if file was created, false otherwise
*/
static bool pwrkey_create_flagfile(const char *path)
{
bool created = false;
int fd = open(path, O_WRONLY|O_CREAT|O_EXCL|O_TRUNC, 0666);
if( fd != -1 ) {
close(fd);
created = true;
}
return created;
}
/** Delete a flag file
*
* @param path Path to the file to create
*
* @return true if file was removed, false otherwise
*/
static bool pwrkey_delete_flagfile(const char *path)
{
bool deleted = false;
if( unlink(path) == 0 ) {
deleted = true;
}
return deleted;
}
/* ========================================================================= *
* DATAPIPE_HANDLING
* ========================================================================= */
/** System state; is undefined at bootup, can't assume anything */
static system_state_t system_state = MCE_SYSTEM_STATE_UNDEF;
/** Cached devicelock_state ; assume unknown */
static devicelock_state_t devicelock_state = DEVICELOCK_STATE_UNDEFINED;
/** Current display state; undefined initially, can't assume anything */
static display_state_t display_state_curr = MCE_DISPLAY_UNDEF;
/** Next Display state; undefined initially, can't assume anything */
static display_state_t display_state_next = MCE_DISPLAY_UNDEF;
/** Lid cover policy state; assume unknown */
static cover_state_t lid_sensor_filtered = COVER_UNDEF;
/** Actual proximity state; assume not covered */
static cover_state_t proximity_sensor_actual = COVER_UNDEF;
/** NGFD availability */
static service_state_t ngfd_service_state = SERVICE_STATE_UNDEF;
/** Cached alarm ui state */
static alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_OFF_INT32;
/** Cached call state */
static call_state_t call_state = CALL_STATE_NONE;
/** devicelock dbus name is reserved; assume unknown */
static service_state_t devicelock_service_state = SERVICE_STATE_UNDEF;
/** Cached ongoing fingerprint enroll; assume not */
static bool enroll_in_progress = false;
/** Use powerkey for blanking during incoming calls */
static bool pwrkey_ignore_incoming_call = false;
/* ========================================================================= *
* PS_OVERRIDE
* ========================================================================= */
/** Provide an emergency way out from stuck proximity sensor
*
* If the proximity sensor is dirty/faulty and stuck to "covered"
* state, it can leave the phone in a state where it is impossible
* to do anything about incoming call, ringing alarm.
*
* To offer somekind of remedy for the situation, this function
* allows user to force proximity sensor to "uncovered" state
* by rapidly pressing power button several times.
*/
static void
pwrkey_ps_override_evaluate(void)
{
static int64_t t_last = 0;
static gint count = 0;
/* If the feature is disabled, just reset the counter */
if( pwrkey_ps_override_count <= 0 ||
pwrkey_ps_override_timeout <= 0 ) {
t_last = 0, count = 0;
goto EXIT;
}
/* If neither sensor is not covered, just reset the counter */
if( proximity_sensor_actual == COVER_OPEN &&
lid_sensor_filtered != COVER_CLOSED ) {
t_last = 0, count = 0;
goto EXIT;
}
int64_t t_now = mce_lib_get_boot_tick();
/* If the previous power key press was too far in
* the past, start counting from zero again */
if( t_now > t_last + pwrkey_ps_override_timeout ) {
mce_log(LL_DEBUG, "ps override count restarted");
count = 0;
}
t_last = t_now;
/* If configured number of power key presses within the time
* limits has been reached, force proximity sensor state to
* "uncovered".
*
* This should allow touch input ungrabbing and turning
* display on during incoming call / alarm.
*
* If sensor gets unstuck and new proximity readings are
* received, this override will be automatically undone.
*/
if( ++count != pwrkey_ps_override_count ) {
mce_log(LL_DEBUG, "ps override count = %d", count);
goto EXIT;
}
if( proximity_sensor_actual == COVER_CLOSED ) {
mce_log(LL_CRIT, "assuming stuck proximity sensor;"
" faking uncover event");
/* Force cached proximity state to "open" */
datapipe_exec_full(&proximity_sensor_actual_pipe,
GINT_TO_POINTER(COVER_OPEN));
}
if( lid_sensor_filtered == COVER_CLOSED ) {
mce_log(LL_CRIT, "assuming stuck lid sensor;"
" resetting validation data");
/* Reset lid sensor validation data */
datapipe_exec_full(&lid_sensor_is_working_pipe,
GINT_TO_POINTER(false));
}
t_last = 0, count = 0;
EXIT:
return;
}
/* ========================================================================= *
* ACTION_EXEC
* ========================================================================= */
static void
pwrkey_action_vibrate(void)
{
mce_log(LL_DEBUG, "Requesting vibrate");
xngf_play_event("pwrkey");
}
static void
pwrkey_action_shutdown(void)
{
submode_t submode = mce_get_submode_int32();
/* Do not shutdown if the tklock is active */
if( submode & MCE_SUBMODE_TKLOCK )
goto EXIT;
mce_log(LL_DEVEL, "Requesting shutdown");
mce_dsme_request_normal_shutdown();
EXIT:
return;
}
static void
pwrkey_action_tklock(void)
{
tklock_request_t request = TKLOCK_REQUEST_ON;
mce_datapipe_request_tklock(request);
}
static void
pwrkey_action_tkunlock(void)
{
/* Only unlock if we are in/entering fully powered on display state */
switch( display_state_next ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
break;
default:
goto EXIT;
}
tklock_request_t request = TKLOCK_REQUEST_OFF;
/* Even if powerkey actions are allowed to work while proximity
* sensor is covered, we must not deactivatie the lockscreen */
if( proximity_sensor_actual != COVER_OPEN ) {
mce_log(LL_DEBUG, "Proximity sensor %s; rejecting tklock=%s",
proximity_state_repr(proximity_sensor_actual),
tklock_request_repr(request));
goto EXIT;
}
mce_datapipe_request_tklock(request);
EXIT:
return;
}
static void
pwrkey_action_tkunlock2(void)
{
if( devicelock_state != DEVICELOCK_STATE_UNLOCKED ) {
mce_log(LL_DEBUG, "devicelock_state=%s; rejecting 'tkunlock2' action",
devicelock_state_repr(devicelock_state));
}
else {
pwrkey_action_tkunlock();
}
}
static void
pwrkey_action_blank(void)
{
display_state_t request = MCE_DISPLAY_OFF;
switch( pwrkey_action_blank_mode ) {
case PWRKEY_BLANK_TO_LPM:
request = MCE_DISPLAY_LPM_ON;
break;
case PWRKEY_BLANK_TO_OFF:
default:
break;
}
mce_log(LL_DEBUG, "Requesting display=%s",
display_state_repr(request));
mce_datapipe_request_display_state(request);
}
static void
pwrkey_action_unblank(void)
{
/* Special case: Even if incoming call is beeing ignored, do not
* allow unblanking via power key while proximity sensor is covered.
*
* Silencing ringing via pressing the power key through fabric
* of a pocket easily leads to several power key presses getting
* emitted and we do not want the display to get activated by such
* activity.
*/
if( call_state == CALL_STATE_RINGING ) {
if( !pwrkey_ignore_incoming_call ) {
mce_log(LL_DEVEL, "skip unblank; incoming call not ignored");
goto EXIT;
}
if( proximity_sensor_actual != COVER_OPEN ) {
mce_log(LL_DEVEL, "skip unblank; proximity covered/unknown");
goto EXIT;
}
}
if( !pwrkey_unblank_allowed() ) {
mce_log(LL_DEVEL, "skip unblank; predicate condition not met");
goto EXIT;
}
display_state_t request = MCE_DISPLAY_ON;
mce_log(LL_DEBUG, "Requesting display=%s",
display_state_repr(request));
mce_tklock_unblank(request);
EXIT:
return;
}
static void
pwrkey_action_devlock(void)
{
static const char service[] = DEVICELOCK_SERVICE;
static const char object[] = DEVICELOCK_REQUEST_PATH;
static const char interface[] = DEVICELOCK_REQUEST_IF;
static const char method[] = "setState";
dbus_int32_t request = DEVICELOCK_STATE_LOCKED;
if( devicelock_service_state != SERVICE_STATE_RUNNING ) {
mce_log(LL_WARN, "devicelock service state is %s; skip %s request",
service_state_repr(devicelock_service_state),
devicelock_state_repr(request));
goto EXIT;
}
mce_log(LL_DEBUG, "Requesting devicelock=%s",
devicelock_state_repr(request));
dbus_send(service, object, interface, method, 0,
DBUS_TYPE_INT32, &request,
DBUS_TYPE_INVALID);
EXIT:
return;
}
static void
pwrkey_action_dbus1(void)
{
pwrkey_dbus_action_execute(0);
}
static void
pwrkey_action_dbus2(void)
{
pwrkey_dbus_action_execute(1);
}
static void
pwrkey_action_dbus3(void)
{
pwrkey_dbus_action_execute(2);
}
static void
pwrkey_action_dbus4(void)
{
pwrkey_dbus_action_execute(3);
}
static void
pwrkey_action_dbus5(void)
{
pwrkey_dbus_action_execute(4);
}
static void
pwrkey_action_dbus6(void)
{
pwrkey_dbus_action_execute(5);
}
static void
pwrkey_action_dbus7(void)
{
pwrkey_dbus_action_execute(6);
}
static void
pwrkey_action_dbus8(void)
{
pwrkey_dbus_action_execute(7);
}
static void
pwrkey_action_dbus9(void)
{
pwrkey_dbus_action_execute(8);
}
static void
pwrkey_action_dbus10(void)
{
pwrkey_dbus_action_execute(9);
}
static void
pwrkey_action_nop(void)
{
/* Do nothing */
}
/* ========================================================================= *
* ACTION_SETS
* ========================================================================= */
/** Config string to callback function mapping
*
* The configured actions are executed in order defined by this array.
*
* This is needed for determining actions that common to both single and
* double press handling.
*/
static const pwrkey_bitconf_t pwrkey_action_lut[] =
{
// Direction: ON->OFF
{
.name = "blank",
.func = pwrkey_action_blank,
},
{
.name = "tklock",
.func = pwrkey_action_tklock,
},
{
.name = "devlock",
.func = pwrkey_action_devlock,
},
{
.name = "shutdown",
.func = pwrkey_action_shutdown,
},
{
.name = "vibrate",
.func = pwrkey_action_vibrate,
},
// Direction: OFF->ON
{
.name = "unblank",
.func = pwrkey_action_unblank,
},
{
.name = "tkunlock2",
.func = pwrkey_action_tkunlock2,
},
{
.name = "tkunlock",
.func = pwrkey_action_tkunlock,
},
// D-Bus actions
{
.name = "dbus1",
.func = pwrkey_action_dbus1,
},
{
.name = "dbus2",
.func = pwrkey_action_dbus2,
},
{
.name = "dbus3",
.func = pwrkey_action_dbus3,
},
{
.name = "dbus4",
.func = pwrkey_action_dbus4,
},
{
.name = "dbus5",
.func = pwrkey_action_dbus5,
},
{
.name = "dbus6",
.func = pwrkey_action_dbus6,
},
{
.name = "dbus7",
.func = pwrkey_action_dbus7,
},
{
.name = "dbus8",
.func = pwrkey_action_dbus8,
},
{
.name = "dbus9",
.func = pwrkey_action_dbus9,
},
{
.name = "dbus10",
.func = pwrkey_action_dbus10,
},
// Low priority placeholder/dummy action
{
.name = "nop",
.func = pwrkey_action_nop,
},
};
static void pwrkey_mask_execute_cb(gpointer aptr)
{
const char *name = aptr;
for( size_t i = 0; i < G_N_ELEMENTS(pwrkey_action_lut); ++i ) {
if( strcmp(pwrkey_action_lut[i].name, name) )
continue;
mce_log(LL_DEBUG, "* exec(%s)", name);
pwrkey_action_lut[i].func();
break;
}
}
static void
pwrkey_mask_execute(uint32_t mask)
{
for( size_t i = 0; i < G_N_ELEMENTS(pwrkey_action_lut); ++i ) {
if( mask & (1u << i) ) {
const char *name = pwrkey_action_lut[i].name;
mce_log(LL_DEBUG, "* queue(%s)", name);
common_on_proximity_schedule(MODULE_NAME,
pwrkey_mask_execute_cb,
(gpointer)name);
}
}
}
static uint32_t
pwrkey_mask_from_name(const char *name)
{
uint32_t mask = 0;
for( size_t i = 0; i < G_N_ELEMENTS(pwrkey_action_lut); ++i ) {
if( strcmp(pwrkey_action_lut[i].name, name) )
continue;
mask |= 1u << i;
break;
}
return mask;
}
static uint32_t
pwrkey_mask_from_names(const char *names)
{
uint32_t mask = 0;
char *work = 0;
char *pos;
char *end;
if( !names )
goto EXIT;
if( !(work = strdup(names)) )
goto EXIT;
for( pos = work; pos; pos = end ) {
if( (end = strchr(pos, ',')) )
*end++ = 0;
mask |= pwrkey_mask_from_name(pos);
}
EXIT:
free(work);
return mask;
}
static gchar *
pwrkey_mask_to_names(uint32_t mask)
{
char tmp[256];
char *pos = tmp;
char *end = tmp + sizeof tmp - 1;
auto void add(const char *str)
{
while( pos < end && *str )
*pos++ = *str++;
};
for( size_t i = 0; i < G_N_ELEMENTS(pwrkey_action_lut); ++i ) {
if( mask & (1u << i) ) {
if( pos > tmp )
add(",");
add(pwrkey_action_lut[i].name);
}
}
*pos = 0;
return g_strdup(tmp);
}
/* ------------------------------------------------------------------------- *
* GESTURE_FILTERING
* ------------------------------------------------------------------------- */
/** Predicate for: touchscreen gesture actions are allowed
*/
static bool
pwrkey_gestures_allowed(bool synthesized)
{
bool allowed = false;
/* Check enable setting */
switch( pwrkey_gestures_enable_mode ) {
case DBLTAP_ENABLE_ALWAYS:
break;
case DBLTAP_ENABLE_NEVER:
if( !synthesized ) {
mce_log(LL_DEVEL, "[gesture] ignored due to setting=never");
goto EXIT;
}
/* Synthesized events (e.g. double tap from lpm) are implicitly
* subjected to proximity rules.
*
* Fall through */
default:
case DBLTAP_ENABLE_NO_PROXIMITY:
if( lid_sensor_filtered == COVER_CLOSED ) {
mce_log(LL_DEVEL, "[gesture] ignored due to lid=closed");
goto EXIT;
}
if( proximity_sensor_actual == COVER_CLOSED ) {
mce_log(LL_DEVEL, "[gesture] ignored due to proximity");
goto EXIT;
}
break;
}
switch( system_state ) {
case MCE_SYSTEM_STATE_USER:
case MCE_SYSTEM_STATE_ACTDEAD:
break;
default:
mce_log(LL_DEVEL, "[gesture] ignored due to system state");
goto EXIT;
}
/* Note: In case we happen to be in middle of display state transition
* the double tap blocking must use the next stable display state
* rather than the current - potentially transitional - state.
*/
switch( display_state_next )
{
case MCE_DISPLAY_OFF:
case MCE_DISPLAY_LPM_OFF:
case MCE_DISPLAY_POWER_DOWN:
case MCE_DISPLAY_LPM_ON:
break;
default:
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_POWER_UP:
case MCE_DISPLAY_UNDEF:
mce_log(LL_DEVEL, "[gesture] ignored due to display state");
goto EXIT;
}
allowed = true;
EXIT:
return allowed;
}
/** Predicate for: fpwakeup actions are allowed
*/
static bool
pwrkey_fpwakeup_allowed(void)
{
bool allowed = false;
/* Only in USER state */
if( system_state != MCE_SYSTEM_STATE_USER ) {
mce_log(LL_DEVEL, "[fpwakeup] ignored due to system_state=%s",
system_state_repr(system_state));
goto EXIT;
}
/* Not while lid is closed or proximity sensor covered */
if( lid_sensor_filtered == COVER_CLOSED ) {
mce_log(LL_DEVEL, "[gesture] ignored due to lid=%s",
cover_state_repr(lid_sensor_filtered));
goto EXIT;
}
if( proximity_sensor_actual == COVER_CLOSED ) {
mce_log(LL_DEVEL, "[gesture] ignored due to proximity=%s",
proximity_state_repr(proximity_sensor_actual));
goto EXIT;
}
/* To have something sensible to do with fpwakeup
* - display must be off, or
* - display is on and lockscreen active
*/
submode_t submode = mce_get_submode_int32();
switch( display_state_next )
{
case MCE_DISPLAY_LPM_ON:
case MCE_DISPLAY_LPM_OFF:
case MCE_DISPLAY_OFF:
break;
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_ON:
if( !(submode & MCE_SUBMODE_TKLOCK) ) {
mce_log(LL_DEVEL, "[fpwakeup] ignored due to tklock=false");
goto EXIT;
}
break;
default:
goto EXIT;
}
allowed = true;
EXIT:
return allowed;
}
/* ========================================================================= *
* PWRKEY_UNBLANK
* ========================================================================= */
/** Check if unblanking is allowed according to currently active rules
*
* @return true if unblanking is allowed, false otherwise
*/
static bool
pwrkey_unblank_allowed(void)
{
bool allowed = false;
switch( pwrkey_unblank_predicate ) {
case PWRKEY_UNBLANK_PREDICATE_POWERKEY:
allowed = !pwrkey_stm_ignore_action();
break;
case PWRKEY_UNBLANK_PREDICATE_FPWAKEUP:
allowed = pwrkey_fpwakeup_allowed();
break;
case PWRKEY_UNBLANK_PREDICATE_GESTURE_REAL:
allowed = pwrkey_gestures_allowed(false);
break;
case PWRKEY_UNBLANK_PREDICATE_GESTURE_SYNTH:
allowed = pwrkey_gestures_allowed(true);
break;
default:
break;
}
mce_log(LL_DEBUG, "evaluate predicate %s => %s",
pwrkey_unblank_predicate_name[pwrkey_unblank_predicate],
allowed ? "allowed" : "denied");
return allowed;
}
/** On-proximity callback for selecting unblank rules
*
* @param aptr unblank predicate to use (as void pointer)
*/
static void
pwrkey_unblank_set_predicate_cb(gpointer aptr)
{
pwrkey_unblank_predicate = GPOINTER_TO_INT(aptr);
mce_log(LL_DEBUG, "execute predicate = %s",
pwrkey_unblank_predicate_name[pwrkey_unblank_predicate]);
}
/** Select which rules apply in delayed on-proximity action handling
*
* @param predicate unblank predicate to use (as void pointer)
*/
static void
pwrkey_unblank_set_predicate(pwrkey_unblank_predicate_t predicate)
{
mce_log(LL_DEBUG, "schedule predicate = %s",
pwrkey_unblank_predicate_name[predicate]);
common_on_proximity_schedule(MODULE_NAME,
pwrkey_unblank_set_predicate_cb,
GINT_TO_POINTER(predicate));
}
/* ========================================================================= *
* ACTION_TRIGGERING
* ========================================================================= */
static void
pwrkey_actions_do_gesture(size_t gesture)
{
/* Extract modifier bits */
bool synthetized = (gesture & GESTURE_SYNTHESIZED) != 0;
gesture &= ~GESTURE_SYNTHESIZED;
/* Treat unconfigurable gestures as doubletaps */
if( gesture >= POWERKEY_ACTIONS_GESTURE_COUNT )
gesture = GESTURE_DOUBLETAP;
/* Check settings, proximity sensor state, etc */
pwrkey_unblank_predicate_t predicate = synthetized
? PWRKEY_UNBLANK_PREDICATE_GESTURE_SYNTH
: PWRKEY_UNBLANK_PREDICATE_GESTURE_REAL;
switch( gesture ) {
case GESTURE_FPWAKEUP:
if( !pwrkey_fpwakeup_allowed() )
goto EXIT;
predicate = PWRKEY_UNBLANK_PREDICATE_FPWAKEUP;
break;
default:
if( !pwrkey_gestures_allowed(synthetized) )
goto EXIT;
break;
}
pwrkey_unblank_set_predicate(predicate);
pwrkey_mask_execute(pwrkey_actions_from_gesture[gesture].mask_single);
EXIT:
return;
}
static void
pwrkey_actions_do_common(void)
{
pwrkey_unblank_set_predicate(PWRKEY_UNBLANK_PREDICATE_POWERKEY);
pwrkey_mask_execute(pwrkey_actions_now->mask_common);
}
static void
pwrkey_actions_do_single_press(void)
{
pwrkey_unblank_set_predicate(PWRKEY_UNBLANK_PREDICATE_POWERKEY);
pwrkey_mask_execute(pwrkey_actions_now->mask_single);
}
static bool
pwrkey_actions_use_double_press(void)
{
return pwrkey_actions_now->mask_double != 0;
}
static void
pwrkey_actions_do_double_press(void)
{
pwrkey_unblank_set_predicate(PWRKEY_UNBLANK_PREDICATE_POWERKEY);
pwrkey_mask_execute(pwrkey_actions_now->mask_double);
}
static void
pwrkey_actions_do_long_press(void)
{
/* The action configuration applies only in the USER mode */
switch( system_state ) {
case MCE_SYSTEM_STATE_SHUTDOWN:
case MCE_SYSTEM_STATE_REBOOT:
/* Ignore if we're already shutting down/rebooting */
break;
case MCE_SYSTEM_STATE_ACTDEAD:
/* Activate power on led pattern and power up to user mode*/
mce_log(LL_DEBUG, "activate MCE_LED_PATTERN_POWER_ON");
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_POWER_ON);
mce_dsme_request_powerup();
break;
case MCE_SYSTEM_STATE_USER:
/* Apply configured actions */
pwrkey_unblank_set_predicate(PWRKEY_UNBLANK_PREDICATE_POWERKEY);
pwrkey_mask_execute(pwrkey_actions_now->mask_long);
break;
default:
/* Default to powering off */
mce_log(LL_WARN, "Requesting shutdown from state: %s",
system_state_repr(system_state));
mce_dsme_request_normal_shutdown();
break;
}
}
static bool
pwrkey_actions_update(const pwrkey_actions_t *self,
gchar **names_single,
gchar **names_double,
gchar **names_long)
{
bool changed = false;
auto void update(gchar **prev, gchar *curr)
{
if( prev && !eq(*prev, curr) )
changed = true, g_free(*prev), *prev = curr, curr = 0;
g_free(curr);
}
update(names_single,
pwrkey_mask_to_names(self->mask_single | self->mask_common));
update(names_double,
pwrkey_mask_to_names(self->mask_double | self->mask_common));
update(names_long,
pwrkey_mask_to_names(self->mask_long));
return changed;
}
static void
pwrkey_actions_parse(pwrkey_actions_t *self,
const char *names_single,
const char *names_double,
const char *names_long)
{
/* Parse from configuration strings */
self->mask_common = 0;
self->mask_single = pwrkey_mask_from_names(names_single);
self->mask_double = pwrkey_mask_from_names(names_double);
self->mask_long = pwrkey_mask_from_names(names_long);
/* Separate leading actions that are common to both
* single and double press */
uint32_t diff = self->mask_single ^ self->mask_double;
uint32_t mask = (diff - 1) & ~diff;
uint32_t comm = self->mask_single & self->mask_double & mask;
self->mask_common |= comm;
self->mask_single &= ~comm;
self->mask_double &= ~comm;
}
static void pwrkey_actions_select(bool display_is_on)
{
if( display_is_on )
pwrkey_actions_now = &pwrkey_actions_from_display_on;
else
pwrkey_actions_now = &pwrkey_actions_from_display_off;
}
/* ========================================================================= *
* LONG_PRESS_TIMEOUT
* ========================================================================= */
static gboolean pwrkey_long_press_timer_cb(gpointer aptr)
{
(void) aptr;
if( !pwrkey_long_press_timer_id )
goto EXIT;
pwrkey_long_press_timer_id = 0;
pwrkey_stm_long_press_timeout();
pwrkey_stm_rethink_wakelock();
EXIT:
return FALSE;
}
static bool
pwrkey_long_press_timer_pending(void)
{
return pwrkey_long_press_timer_id != 0;
}
static bool
pwrkey_long_press_timer_cancel(void)
{
bool canceled = false;
if( pwrkey_long_press_timer_id ) {
g_source_remove(pwrkey_long_press_timer_id),
pwrkey_long_press_timer_id = 0;
canceled = true;
}
return canceled;
}
static void
pwrkey_long_press_timer_start(void)
{
pwrkey_long_press_timer_cancel();
pwrkey_long_press_timer_id = g_timeout_add(pwrkey_long_press_delay,
pwrkey_long_press_timer_cb, 0);
}
/* ========================================================================= *
* DOUBLE_PRESS_TIMEOUT
* ========================================================================= */
static gboolean pwrkey_double_press_timer_cb(gpointer aptr)
{
(void) aptr;
if( !pwrkey_double_press_timer_id )
goto EXIT;
pwrkey_double_press_timer_id = 0;
pwrkey_stm_double_press_timeout();
pwrkey_stm_rethink_wakelock();
EXIT:
return FALSE;
}
static bool
pwrkey_double_press_timer_pending(void)
{
return pwrkey_double_press_timer_id != 0;
}
static bool
pwrkey_double_press_timer_cancel(void)
{
bool canceled = false;
if( pwrkey_double_press_timer_id ) {
g_source_remove(pwrkey_double_press_timer_id),
pwrkey_double_press_timer_id = 0;
canceled = true;
}
return canceled;
}
static void
pwrkey_double_press_timer_start(void)
{
pwrkey_double_press_timer_cancel();
pwrkey_double_press_timer_id = g_timeout_add(pwrkey_double_press_delay,
pwrkey_double_press_timer_cb, 0);
}
/* ========================================================================= *
* DBUS_ACTIONS
* ========================================================================= */
static void
pwrkey_dbus_action_clear(pwrkey_dbus_action_t *self)
{
free(self->destination), self->destination = 0;
free(self->object), self->object = 0;
free(self->interface), self->interface = 0;
free(self->member), self->member = 0;
free(self->argument), self->argument = 0;
}
static void
pwrkey_dbus_action_reset(pwrkey_dbus_action_t *self)
{
pwrkey_dbus_action_clear(self);
/* Builtin default is always just a signal arg, no parsing required */
self->argument = strdup(self->setting_def);
}
static bool
pwrkey_dbus_action_is_methodcall(const pwrkey_dbus_action_t *self)
{
bool valid = false;
if( empty(self->destination) ||
empty(self->object) ||
empty(self->interface) ||
empty(self->member) ) {
goto cleanup;
}
if( !dbus_validate_bus_name(self->destination, 0) ||
!dbus_validate_path(self->object, 0) ||
!dbus_validate_interface(self->interface, 0) ||
!dbus_validate_member(self->member, 0) ) {
goto cleanup;
}
if( !empty(self->argument) && !dbus_validate_utf8(self->argument, 0) )
goto cleanup;
valid = true;
cleanup:
return valid;
}
static bool
pwrkey_dbus_action_is_signal(const pwrkey_dbus_action_t *self)
{
bool valid = false;
// must have an argument
if( empty(self->argument) )
goto cleanup;
// ... and only the argument
if( !empty(self->destination) ||
!empty(self->object) ||
!empty(self->interface) ||
!empty(self->member) ) {
goto cleanup;
}
// which needs to be valid utf8 string
if( !dbus_validate_utf8(self->argument, 0) )
goto cleanup;
valid = true;
cleanup:
return valid;
}
static gchar *
pwrkey_dbus_action_to_string(const pwrkey_dbus_action_t *self)
{
gchar *res = 0;
if( pwrkey_dbus_action_is_signal(self) ) {
res = g_strdup(self->argument);
}
else if( pwrkey_dbus_action_is_methodcall(self) ) {
res = g_strdup_printf("%s,%s,%s,%s,%s",
self->destination ?: "",
self->object ?: "",
self->interface ?: "",
self->member ?: "",
self->argument ?: "");
}
return res;
}
static void
pwrkey_dbus_action_parse(pwrkey_dbus_action_t *self)
{
char *tmp = 0;
char *pos = 0;
char *arg = 0;
pwrkey_dbus_action_clear(self);
if( empty(self->setting_val) )
goto cleanup;
pos = tmp = strdup(self->setting_val);
arg = pwrkey_get_token(&pos);
if( *arg && !*pos ) {
self->argument = strdup(arg);
}
else {
self->destination = strdup(arg);
self->object = strdup(pwrkey_get_token(&pos));
self->interface = strdup(pwrkey_get_token(&pos));
self->member = strdup(pwrkey_get_token(&pos));
self->argument = strdup(pwrkey_get_token(&pos));
}
cleanup:
free(tmp);
}
static void
pwrkey_dbus_action_sanitize(pwrkey_dbus_action_t *self)
{
if( !pwrkey_dbus_action_is_methodcall(self) &&
!pwrkey_dbus_action_is_signal(self) ) {
pwrkey_dbus_action_reset(self);
}
}
static void
pwrkey_dbus_action_configure(size_t action_id, bool force_reset)
{
gchar *use = 0;
if( action_id >= POWEKEY_DBUS_ACTION_COUNT )
goto cleanup;
pwrkey_dbus_action_t *action = pwrkey_dbus_action + action_id;
if( force_reset ) {
pwrkey_dbus_action_reset(action);
}
else {
pwrkey_dbus_action_parse(action);
pwrkey_dbus_action_sanitize(action);
}
use = pwrkey_dbus_action_to_string(action);
if( !eq(action->setting_val, use) ) {
/* Change locally cached value */
g_free(action->setting_val), action->setting_val = use, use = 0;
/* Flush change to settings */
mce_setting_set_string(action->setting_key, action->setting_val);
}
cleanup:
g_free(use);
}
static void
pwrkey_dbus_action_execute(size_t action_id)
{
bool flag_created = false;
if( action_id >= POWEKEY_DBUS_ACTION_COUNT )
goto cleanup;
mce_log(LL_DEBUG, "Executing dbus action %zd", action_id);
const pwrkey_dbus_action_t *action = pwrkey_dbus_action + action_id;
/* We're potentially creating dbus messages using user specified
* parameters. Since libdbus will abort the process rather than
* returning some error code -> have a flag file around while
* doing the hazardous ipc operations -> if abort occurs, the
* flag file is left behind -> mce will reset dbus action config
* back to default on restart */
if( !(flag_created = pwrkey_create_flagfile(pwrkey_dbus_action_flag)) ) {
mce_log(LL_CRIT, "%s: could not create flagfile: %m",
pwrkey_dbus_action_flag);
goto cleanup;
}
if( pwrkey_dbus_action_is_signal(action) ) {
pwrkey_dbus_send_signal(MCE_POWER_BUTTON_TRIGGER,
action->argument);
goto cleanup;
}
if( !pwrkey_dbus_action_is_methodcall(action) ) {
mce_log(LL_WARN, "dbus%zd action does not have valid configuration",
action_id + 1);
goto cleanup;
}
if( empty(action->argument) ) {
dbus_send(action->destination,
action->object,
action->interface,
action->member,
0,
DBUS_TYPE_INVALID);
}
else {
dbus_send(action->destination,
action->object,
action->interface,
action->member,
0,
DBUS_TYPE_STRING, &action->argument,
DBUS_TYPE_INVALID);
}
cleanup:
if( flag_created && !pwrkey_delete_flagfile(pwrkey_dbus_action_flag) ) {
mce_log(LL_CRIT, "%s: could not delete flagfile: %m",
pwrkey_dbus_action_flag);
}
return;
}
/* ========================================================================= *
* POWER_KEY_STATE_MACHINE
* ========================================================================= */
/** Check if we need to hold a wakelock for power key handling
*
* Wakelock is held if there are pending timers.
*/
static void
pwrkey_stm_rethink_wakelock(void)
{
#ifdef ENABLE_WAKELOCKS
static bool have_lock = false;
bool want_lock = pwrkey_stm_pending_timers();;
if( have_lock == want_lock )
goto EXIT;
if( (have_lock = want_lock) ) {
wakelock_lock("mce_pwrkey_stm", -1);
mce_log(LL_DEBUG, "acquire wakelock");
}
else {
mce_log(LL_DEBUG, "release wakelock");
wakelock_unlock("mce_pwrkey_stm");
}
EXIT:
return;
#endif
}
static bool
pwrkey_stm_pending_timers(void)
{
return (pwrkey_long_press_timer_pending() ||
pwrkey_double_press_timer_pending());
}
static void
pwrkey_stm_terminate(void)
{
/* Cancel timers */
pwrkey_double_press_timer_cancel();
pwrkey_long_press_timer_cancel();
/* Release wakelock */
pwrkey_stm_rethink_wakelock();
}
static void pwrkey_stm_long_press_timeout(void)
{
// execute long press
pwrkey_actions_do_long_press();
}
static void pwrkey_stm_double_press_timeout(void)
{
// execute single press
pwrkey_actions_do_single_press();
}
static void pwrkey_stm_powerkey_pressed(void)
{
if( pwrkey_double_press_timer_cancel() ) {
/* Pressed while we were waiting for double press */
pwrkey_actions_do_double_press();
}
else if( !pwrkey_long_press_timer_pending() ) {
/* Pressed while there are no timers active */
/* Store display state we started from */
pwrkey_stm_store_initial_state();
/* Start short vs long press detection timer */
if( !pwrkey_stm_ignore_action() ) {
pwrkey_long_press_timer_start();
}
}
}
static void pwrkey_stm_powerkey_released(void)
{
if( pwrkey_long_press_timer_cancel() ) {
/* Released while we were waiting for long press */
/* Always do actions that are common to both short and
* double press */
pwrkey_actions_do_common();
if( pwrkey_actions_use_double_press() ) {
/* There is config for double press -> wait a while
* to see if it is double press */
pwrkey_double_press_timer_start();
}
else {
/* There is no config for double press -> just do
* actions for single press without further delays */
pwrkey_actions_do_single_press();
}
}
}
static void pwrkey_stm_store_initial_state(void)
{
/* Cache display state */
pwrkey_stm_display_state = display_state_curr;
/* MCE_DISPLAY_OFF requests must be queued only
* from fully powered up display states.
* Otherwise we create a situation where multiple
* power key presses done while the display is off
* or powering up will bounce back to display off
* once initial the off->on transition finishes */
bool display_is_on = false;
switch( pwrkey_stm_display_state ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
display_is_on = true;
break;
default:
break;
}
pwrkey_actions_select(display_is_on);
}
/** Should power key action be ignored predicate
*/
static bool
pwrkey_stm_ignore_action(void)
{
/* Assume that power key action should not be ignored */
bool ignore_powerkey = false;
/* If alarm dialog is up, power key is used for snoozing */
switch( alarm_ui_state ) {
case MCE_ALARM_UI_VISIBLE_INT32:
case MCE_ALARM_UI_RINGING_INT32:
mce_log(LL_DEVEL, "[powerkey] ignored due to active alarm");
ignore_powerkey = true;
pwrkey_dbus_send_signal(MCE_ALARM_UI_FEEDBACK_SIG, MCE_FEEDBACK_EVENT_POWERKEY);
break;
default:
case MCE_ALARM_UI_OFF_INT32:
case MCE_ALARM_UI_INVALID_INT32:
// dontcare
break;
}
/* During incoming call power key is used to silence ringing */
switch( call_state ) {
case CALL_STATE_RINGING:
if( pwrkey_ignore_incoming_call ) {
/* Call ui has signaled mce that the incoming call has
* been ignored -> powerkey can be used for display
* control even if there is incoming call. */
break;
}
mce_log(LL_DEVEL, "[powerkey] ignored due to incoming call");
ignore_powerkey = true;
pwrkey_dbus_send_signal(MCE_CALL_UI_FEEDBACK_SIG, MCE_FEEDBACK_EVENT_POWERKEY);
break;
default:
case CALL_STATE_INVALID:
case CALL_STATE_NONE:
case CALL_STATE_ACTIVE:
case CALL_STATE_SERVICE:
// dontcare
break;
}
/* If user is enrolling a fingerprint, do not blank with powerkey */
if( enroll_in_progress ) {
/* We only want to block actions that would blank / lock the
* device i.e. what would happen from display on/dimmed state.
*/
switch( display_state_next ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
mce_log(LL_DEVEL, "[powerkey] ignored due to fingerprint enroll");
ignore_powerkey = true;
break;
default:
break;
}
}
/* Skip rest if already desided to ignore */
if( ignore_powerkey )
goto EXIT;
/* Proximity sensor state vs power key press handling mode */
switch( pwrkey_stm_enable_mode ) {
case PWRKEY_ENABLE_NEVER:
mce_log(LL_DEVEL, "[powerkey] ignored due to setting=never");
ignore_powerkey = true;
goto EXIT;
case PWRKEY_ENABLE_ALWAYS:
break;
case PWRKEY_ENABLE_NO_PROXIMITY2:
/* do not ignore if display is on */
if( pwrkey_stm_display_state == MCE_DISPLAY_ON ||
pwrkey_stm_display_state == MCE_DISPLAY_DIM ||
pwrkey_stm_display_state == MCE_DISPLAY_LPM_ON ) {
break;
}
/* fall through */
default:
case PWRKEY_ENABLE_NO_PROXIMITY:
if( lid_sensor_filtered == COVER_CLOSED ) {
mce_log(LL_DEVEL, "[powerkey] ignored due to lid");
ignore_powerkey = true;
goto EXIT;
}
if( proximity_sensor_actual == COVER_CLOSED ) {
mce_log(LL_DEVEL, "[powerkey] ignored due to proximity");
ignore_powerkey = true;
goto EXIT;
}
break;
}
EXIT:
return ignore_powerkey;
}
/* ========================================================================= *
* HOME_KEY_STATE_MACHINE
* ========================================================================= */
/** Convert homekey_stm_t enum to human readable string
*
* @param state homekey_stm_t enumeration value
*
* @return human readable representation of state
*/
static const char *
homekey_stm_repr(homekey_stm_t state)
{
const char *repr = "HOMEKEY_STM_UNKNOWN";
switch( state ) {
case HOMEKEY_STM_WAIT_PRESS: repr = "HOMEKEY_STM_WAIT_PRESS"; break;
case HOMEKEY_STM_WAIT_UNBLANK: repr = "HOMEKEY_STM_WAIT_UNBLANK"; break;
case HOMEKEY_STM_SEND_SIGNAL: repr = "HOMEKEY_STM_SEND_SIGNAL"; break;
case HOMEKEY_STM_WAIT_RELEASE: repr = "HOMEKEY_STM_WAIT_RELEASE"; break;
default:
break;
}
return repr;
}
/** Current state of home key handling state machine */
static homekey_stm_t homekey_stm_state = HOMEKEY_STM_WAIT_PRESS;
/** Cached home key is pressed down state */
static bool homekey_stm_pressed = false;
/** Set current state of home key handling state machine
*
* Perform any actions that are related to leaving current and/or
* entering the new state.
*
* @param state homekey_stm_t enumeration value
*/
static void
homekey_stm_set_state(homekey_stm_t state)
{
if( homekey_stm_state == state )
goto EXIT;
mce_log(LL_DEBUG, "state: %s -> %s",
homekey_stm_repr(homekey_stm_state),
homekey_stm_repr(state));
/* Handle entering new state */
switch( (homekey_stm_state = state) ) {
case HOMEKEY_STM_WAIT_PRESS:
break;
case HOMEKEY_STM_WAIT_UNBLANK:
/* Check if policy allows display unblanking */
if( proximity_sensor_actual != COVER_OPEN ) {
mce_log(LL_DEBUG, "Proximity sensor %s; skip unblank",
proximity_state_repr(proximity_sensor_actual));
break;
}
/* Initiate display power up */
mce_log(LL_DEBUG, "request %s",
display_state_repr(MCE_DISPLAY_ON));
mce_datapipe_request_display_state(MCE_DISPLAY_ON);
break;
case HOMEKEY_STM_SEND_SIGNAL:
/* Inform compositor that it should perform home key actions */
pwrkey_dbus_send_signal(MCE_POWER_BUTTON_TRIGGER, "home-key");
break;
case HOMEKEY_STM_WAIT_RELEASE:
break;
default:
break;
}
EXIT:
return;
}
/** Perform one home key handling state machine transition
*
* @return true if state transition took place, false otherwise
*/
static bool
homekey_stm_exec_step(void)
{
homekey_stm_t prev = homekey_stm_state;
switch( homekey_stm_state ) {
default:
case HOMEKEY_STM_WAIT_PRESS:
if( homekey_stm_pressed )
homekey_stm_set_state(HOMEKEY_STM_WAIT_UNBLANK);
break;
case HOMEKEY_STM_WAIT_UNBLANK:
if( display_state_next != MCE_DISPLAY_ON )
homekey_stm_set_state(HOMEKEY_STM_WAIT_RELEASE);
else if( display_state_curr == MCE_DISPLAY_ON )
homekey_stm_set_state(HOMEKEY_STM_SEND_SIGNAL);
break;
case HOMEKEY_STM_SEND_SIGNAL:
homekey_stm_set_state(HOMEKEY_STM_WAIT_RELEASE);
break;
case HOMEKEY_STM_WAIT_RELEASE:
if( !homekey_stm_pressed )
homekey_stm_set_state(HOMEKEY_STM_WAIT_PRESS);
break;
}
return homekey_stm_state != prev;
}
/** Update current state of home key handling state machine
*
* Repeatedly executes transitions until stable state is reached.
*/
static void
homekey_stm_eval_state(void)
{
while( homekey_stm_exec_step() )
;
}
/** Set home key pressed down state and update state machine
*/
static void
homekey_stm_set_pressed(bool pressed)
{
if( homekey_stm_pressed == pressed )
goto EXIT;
homekey_stm_pressed = pressed;
homekey_stm_eval_state();
EXIT:
return;
}
/* ========================================================================= *
* DBUS_IPC
* ========================================================================= */
/** Helper for sending powerkey feedback dbus signal
*
* @param sig name of the signal to send
*/
static void
pwrkey_dbus_send_signal(const char *sig, const char *arg)
{
mce_log(LL_DEVEL, "sending dbus signal: %s %s", sig, arg);
dbus_send(0, MCE_SIGNAL_PATH, MCE_SIGNAL_IF, sig, 0,
DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
}
/**
* D-Bus callback for powerkey event triggering
*
* @param msg D-Bus message
*
* @return TRUE
*/
static gboolean pwrkey_dbus_trigger_event_cb(DBusMessage *const req)
{
DBusMessage *rsp = 0;
dbus_uint32_t act = 0;
DBusMessageIter iter;
mce_log(LL_DEVEL, "[power] button trigger request from %s",
mce_dbus_get_message_sender_ident(req));
if( !dbus_message_iter_init(req, &iter) )
goto EXIT;
switch( dbus_message_iter_get_arg_type(&iter) ) {
case DBUS_TYPE_BOOLEAN:
{
dbus_bool_t tmp = 0;
dbus_message_iter_get_basic(&iter, &tmp);
act = tmp ? 1 : 0;
}
break;
case DBUS_TYPE_UINT32:
dbus_message_iter_get_basic(&iter, &act);
break;
default:
mce_log(LL_ERR, "Argument passed to %s.%s has incorrect type",
MCE_REQUEST_IF, MCE_TRIGGER_POWERKEY_EVENT_REQ);
goto EXIT;
}
if( act > 2 ) {
mce_log(LL_ERR, "Incorrect powerkey event passed to %s.%s; "
"ignoring request",
MCE_REQUEST_IF, MCE_TRIGGER_POWERKEY_EVENT_REQ);
goto EXIT;
}
mce_log(LL_DEBUG, "[power] button event trigger value: %d", act);
/* Terminate state machine actions for real power key */
pwrkey_stm_terminate();
/* Choose actions based on display state */
pwrkey_stm_store_initial_state();
if( pwrkey_stm_ignore_action() )
goto EXIT;
switch (act) {
default:
case MCE_POWERKEY_EVENT_SHORT_PRESS:
/* short press */
pwrkey_actions_do_common();
pwrkey_actions_do_single_press();
break;
case MCE_POWERKEY_EVENT_LONG_PRESS:
/* long press */
pwrkey_actions_do_long_press();
break;
case MCE_POWERKEY_EVENT_DOUBLE_PRESS:
/* double press */
pwrkey_actions_do_common();
pwrkey_actions_do_double_press();
break;
}
EXIT:
if( !dbus_message_get_no_reply(req) ) {
/* Need to send reply, create a dummy one if we do
* not have already existing error reply */
if( !rsp )
rsp = dbus_new_method_reply(req);
dbus_send_message(rsp), rsp = 0;
}
if( rsp )
dbus_message_unref(rsp);
pwrkey_stm_rethink_wakelock();
return TRUE;
}
/** D-Bus callback for ignoring incoming call
*
* @param req D-Bus method call message
*
* @return TRUE
*/
static gboolean pwrkey_dbus_ignore_incoming_call_cb(DBusMessage *const req)
{
mce_log(LL_DEVEL, "ignore incoming call from %s",
mce_dbus_get_message_sender_ident(req));
if( call_state == CALL_STATE_RINGING ) {
mce_log(LL_DEBUG, "start ignoring incoming calls");
/* Update powerkey module specific toggle */
pwrkey_ignore_incoming_call = true;
/* Make also callstate plugin ignore incoming calls. This
* should lead to call_state changing from RINGING to ACTIVE
* or NONE depending on whether there are other calls or not. */
datapipe_exec_full(&ignore_incoming_call_event_pipe, GINT_TO_POINTER(true));
}
if( !dbus_message_get_no_reply(req) ) {
DBusMessage *rsp = dbus_new_method_reply(req);
dbus_send_message(rsp), rsp = 0;
}
return TRUE;
}
/** Array of dbus message handlers */
static mce_dbus_handler_t pwrkey_dbus_handlers[] =
{
/* signals - outbound (for Introspect purposes only) */
{
.interface = MCE_SIGNAL_IF,
.name = MCE_ALARM_UI_FEEDBACK_SIG,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.args =
" \n"
},
{
.interface = MCE_SIGNAL_IF,
.name = MCE_CALL_UI_FEEDBACK_SIG,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.args =
" \n"
},
{
.interface = MCE_SIGNAL_IF,
.name = MCE_POWER_BUTTON_TRIGGER,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.args =
" \n"
},
/* method calls */
{
.interface = MCE_REQUEST_IF,
.name = MCE_TRIGGER_POWERKEY_EVENT_REQ,
.type = DBUS_MESSAGE_TYPE_METHOD_CALL,
.callback = pwrkey_dbus_trigger_event_cb,
.args =
" \n"
},
{
.interface = MCE_REQUEST_IF,
.name = MCE_IGNORE_INCOMING_CALL_REQ,
.type = DBUS_MESSAGE_TYPE_METHOD_CALL,
.callback = pwrkey_dbus_ignore_incoming_call_cb,
.args = 0
},
/* sentinel */
{
.interface = 0
}
};
/** Add dbus handlers
*/
static void
pwrkey_dbus_init(void)
{
mce_dbus_handler_register_array(pwrkey_dbus_handlers);
}
/** Remove dbus handlers
*/
static void
pwrkey_dbus_quit(void)
{
mce_dbus_handler_unregister_array(pwrkey_dbus_handlers);
}
/* ========================================================================= *
* DYNAMIC_SETTINGS
* ========================================================================= */
static void
pwrkey_setting_sanitize_action_masks(void)
{
/* parse settings -> bitmasks */
pwrkey_actions_parse(&pwrkey_actions_from_display_on,
pwrkey_actions_single_on,
pwrkey_actions_double_on,
pwrkey_actions_long_on);
pwrkey_actions_parse(&pwrkey_actions_from_display_off,
pwrkey_actions_single_off,
pwrkey_actions_double_off,
pwrkey_actions_long_off);
/* bitmasks -> setting strings */
bool on_changed =
pwrkey_actions_update(&pwrkey_actions_from_display_on,
&pwrkey_actions_single_on,
&pwrkey_actions_double_on,
&pwrkey_actions_long_on);
bool off_changed =
pwrkey_actions_update(&pwrkey_actions_from_display_off,
&pwrkey_actions_single_off,
&pwrkey_actions_double_off,
&pwrkey_actions_long_off);
/* send notifications if something changed */
if( on_changed ) {
mce_setting_set_string(MCE_SETTING_POWERKEY_ACTIONS_SINGLE_ON,
pwrkey_actions_single_on);
mce_setting_set_string(MCE_SETTING_POWERKEY_ACTIONS_DOUBLE_ON,
pwrkey_actions_double_on);
mce_setting_set_string(MCE_SETTING_POWERKEY_ACTIONS_LONG_ON,
pwrkey_actions_long_on);
}
if( off_changed ) {
mce_setting_set_string(MCE_SETTING_POWERKEY_ACTIONS_SINGLE_OFF,
pwrkey_actions_single_off);
mce_setting_set_string(MCE_SETTING_POWERKEY_ACTIONS_DOUBLE_OFF,
pwrkey_actions_double_off);
mce_setting_set_string(MCE_SETTING_POWERKEY_ACTIONS_LONG_OFF,
pwrkey_actions_long_off);
}
for( size_t i = 0; i < POWERKEY_ACTIONS_GESTURE_COUNT; ++i ) {
pwrkey_actions_parse(&pwrkey_actions_from_gesture[i],
pwrkey_actions_gesture[i], 0, 0);
bool gesture_changed =
pwrkey_actions_update(&pwrkey_actions_from_gesture[i],
&pwrkey_actions_gesture[i], 0, 0);
if( gesture_changed ) {
mce_setting_set_string(pwrkey_actions_gesture_key[i],
pwrkey_actions_gesture[i]);
}
}
}
static void
pwrkey_setting_sanitize_dbus_actions(void)
{
/* The custom dbus action settings can cause mce to
* get aborted by dbus_message_new_xxx().
*
* Assume having the flag file present means that
* mce is restarting after abort and reset the dbus
* action config back to defaults to avoid repeating
* the abort.
*/
bool force_reset = pwrkey_delete_flagfile(pwrkey_dbus_action_flag);
if( force_reset ) {
mce_log(LL_CRIT, "%s: flagfile was present; resetting"
"dbus action config", pwrkey_dbus_action_flag);
}
for( size_t action_id = 0; action_id < POWEKEY_DBUS_ACTION_COUNT; ++action_id )
pwrkey_dbus_action_configure(action_id, force_reset);
}
static void
pwrkey_setting_sanitize_now(void)
{
pwrkey_setting_sanitize_action_masks();
pwrkey_setting_sanitize_dbus_actions();
}
static gboolean pwrkey_setting_sanitize_cb(gpointer aptr)
{
(void)aptr;
if( !pwrkey_setting_sanitize_id )
goto EXIT;
pwrkey_setting_sanitize_id = 0;
pwrkey_setting_sanitize_now();
EXIT:
return FALSE;
}
static void pwrkey_setting_sanitize_later(void)
{
if( !pwrkey_setting_sanitize_id )
pwrkey_setting_sanitize_id = g_idle_add(pwrkey_setting_sanitize_cb, 0);
}
static void pwrkey_setting_sanitize_cancel(void)
{
if( pwrkey_setting_sanitize_id ) {
g_source_remove(pwrkey_setting_sanitize_id),
pwrkey_setting_sanitize_id = 0;
}
}
static bool
pwrkey_setting_handle_gesture(const GConfValue *gcv, guint id)
{
bool handled = false;
for( size_t i = 0; i < POWERKEY_ACTIONS_GESTURE_COUNT; ++i ) {
if( pwrkey_actions_gesture_setting_id[i] != id )
continue;
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_gesture[i], val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_gesture[%zu]: '%s' -> '%s'",
i, pwrkey_actions_gesture[i], val);
g_free(pwrkey_actions_gesture[i]);
pwrkey_actions_gesture[i] = g_strdup(val);
pwrkey_setting_sanitize_later();
}
handled = true;
break;
}
return handled;
}
/** GConf callback for powerkey related settings
*
* @param gcc (not used)
* @param id Connection ID from gconf_client_notify_add()
* @param entry The modified GConf entry
* @param data (not used)
*/
static void
pwrkey_setting_cb(GConfClient *const gcc, const guint id,
GConfEntry *const entry, gpointer const data)
{
(void)gcc;
(void)data;
(void)id;
const GConfValue *gcv = gconf_entry_get_value(entry);
if( !gcv ) {
mce_log(LL_DEBUG, "GConf Key `%s' has been unset",
gconf_entry_get_key(entry));
goto EXIT;
}
if( id == pwrkey_stm_enable_mode_setting_id ) {
gint old = pwrkey_stm_enable_mode;
pwrkey_stm_enable_mode = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_stm_enable_mode: %d -> %d",
old, pwrkey_stm_enable_mode);
}
else if( id == pwrkey_action_blank_mode_setting_id ) {
gint old = pwrkey_action_blank_mode;
pwrkey_action_blank_mode = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_action_blank_mode: %d -> %d",
old, pwrkey_action_blank_mode);
}
else if( id == pwrkey_ps_override_count_setting_id ) {
gint old = pwrkey_ps_override_count;
pwrkey_ps_override_count = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_ps_override_count: %d -> %d",
old, pwrkey_ps_override_count);
}
else if( id == pwrkey_ps_override_timeout_setting_id ) {
gint old = pwrkey_ps_override_timeout;
pwrkey_ps_override_timeout = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_ps_override_timeout: %d -> %d",
old, pwrkey_ps_override_timeout);
}
else if( id == pwrkey_long_press_delay_setting_id ) {
gint old = pwrkey_long_press_delay;
pwrkey_long_press_delay = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_long_press_delay: %d -> %d",
old, pwrkey_long_press_delay);
}
else if( id == pwrkey_double_press_delay_setting_id ) {
gint old = pwrkey_double_press_delay;
pwrkey_double_press_delay = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_double_press_delay: %d -> %d",
old, pwrkey_double_press_delay);
}
else if( id == pwrkey_actions_single_on_setting_id ) {
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_single_on, val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_single_on: '%s' -> '%s'",
pwrkey_actions_single_on, val);
g_free(pwrkey_actions_single_on);
pwrkey_actions_single_on = g_strdup(val);
pwrkey_setting_sanitize_later();
}
}
else if( id == pwrkey_actions_double_on_setting_id ) {
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_double_on, val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_double_on: '%s' -> '%s'",
pwrkey_actions_double_on, val);
g_free(pwrkey_actions_double_on);
pwrkey_actions_double_on = g_strdup(val);
pwrkey_setting_sanitize_later();
}
}
else if( id == pwrkey_actions_long_on_setting_id ) {
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_long_on, val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_long_on: '%s' -> '%s'",
pwrkey_actions_long_on, val);
g_free(pwrkey_actions_long_on);
pwrkey_actions_long_on = g_strdup(val);
pwrkey_setting_sanitize_later();
}
}
else if( id == pwrkey_actions_single_off_setting_id ) {
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_single_off, val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_single_off: '%s' -> '%s'",
pwrkey_actions_single_off, val);
g_free(pwrkey_actions_single_off);
pwrkey_actions_single_off = g_strdup(val);
pwrkey_setting_sanitize_later();
}
}
else if( id == pwrkey_actions_double_off_setting_id ) {
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_double_off, val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_double_off: '%s' -> '%s'",
pwrkey_actions_double_off, val);
g_free(pwrkey_actions_double_off);
pwrkey_actions_double_off = g_strdup(val);
pwrkey_setting_sanitize_later();
}
}
else if( id == pwrkey_actions_long_off_setting_id ) {
const char *val = gconf_value_get_string(gcv);
if( !eq(pwrkey_actions_long_off, val) ) {
mce_log(LL_NOTICE, "pwrkey_actions_long_off: '%s' -> '%s'",
pwrkey_actions_long_off, val);
g_free(pwrkey_actions_long_off);
pwrkey_actions_long_off = g_strdup(val);
pwrkey_setting_sanitize_later();
}
}
else if( id == pwrkey_gestures_enable_mode_cb_id ) {
gint old = pwrkey_gestures_enable_mode;
pwrkey_gestures_enable_mode = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "pwrkey_gestures_enable_mode: %d -> %d",
old, pwrkey_gestures_enable_mode);
}
else if( pwrkey_setting_handle_gesture(gcv, id) ) {
// nop
}
else {
for( size_t action_id = 0; ; ++action_id ) {
if( action_id >= POWEKEY_DBUS_ACTION_COUNT ) {
mce_log(LL_WARN, "Spurious GConf value received; confused!");
goto EXIT;
}
pwrkey_dbus_action_t *action = pwrkey_dbus_action + action_id;
if( id != action->setting_id )
continue;
const char *val = gconf_value_get_string(gcv);
if( eq(action->setting_val, val) )
break;
mce_log(LL_NOTICE, "pwrkey_dbus_action%zd_val: '%s' -> '%s'",
action_id, action->setting_val, val);
g_free(action->setting_val), action->setting_val = g_strdup(val);
pwrkey_setting_sanitize_later();
break;
}
}
EXIT:
return;
}
/** Get setting values and start tracking changes
*/
static void
pwrkey_setting_init(void)
{
/* Power key press handling mode */
mce_setting_track_int(MCE_SETTING_POWERKEY_MODE,
&pwrkey_stm_enable_mode,
MCE_DEFAULT_POWERKEY_MODE,
pwrkey_setting_cb,
&pwrkey_stm_enable_mode_setting_id);
/* Power key display blanking mode */
mce_setting_track_int(MCE_SETTING_POWERKEY_BLANKING_MODE,
&pwrkey_action_blank_mode,
MCE_DEFAULT_POWERKEY_BLANKING_MODE,
pwrkey_setting_cb,
&pwrkey_action_blank_mode_setting_id);
/* Power key press count for proximity sensor override */
mce_setting_track_int(MCE_SETTING_POWERKEY_PS_OVERRIDE_COUNT,
&pwrkey_ps_override_count,
MCE_DEFAULT_POWERKEY_PS_OVERRIDE_COUNT,
pwrkey_setting_cb,
&pwrkey_ps_override_count_setting_id);
/* Maximum time between power key presses for ps override */
mce_setting_track_int(MCE_SETTING_POWERKEY_PS_OVERRIDE_TIMEOUT,
&pwrkey_ps_override_timeout,
MCE_DEFAULT_POWERKEY_PS_OVERRIDE_TIMEOUT,
pwrkey_setting_cb,
&pwrkey_ps_override_timeout_setting_id);
/* Delay for waiting long press */
mce_setting_track_int(MCE_SETTING_POWERKEY_LONG_PRESS_DELAY,
&pwrkey_long_press_delay,
MCE_DEFAULT_POWERKEY_LONG_PRESS_DELAY,
pwrkey_setting_cb,
&pwrkey_long_press_delay_setting_id);
/* Delay for waiting double press */
mce_setting_track_int(MCE_SETTING_POWERKEY_DOUBLE_PRESS_DELAY,
&pwrkey_double_press_delay,
MCE_DEFAULT_POWERKEY_DOUBLE_PRESS_DELAY,
pwrkey_setting_cb,
&pwrkey_double_press_delay_setting_id);
/* Action sets */
mce_setting_track_string(MCE_SETTING_POWERKEY_ACTIONS_SINGLE_ON,
&pwrkey_actions_single_on,
MCE_DEFAULT_POWERKEY_ACTIONS_SINGLE_ON,
pwrkey_setting_cb,
&pwrkey_actions_single_on_setting_id);
mce_setting_track_string(MCE_SETTING_POWERKEY_ACTIONS_DOUBLE_ON,
&pwrkey_actions_double_on,
MCE_DEFAULT_POWERKEY_ACTIONS_DOUBLE_ON,
pwrkey_setting_cb,
&pwrkey_actions_double_on_setting_id);
mce_setting_track_string(MCE_SETTING_POWERKEY_ACTIONS_LONG_ON,
&pwrkey_actions_long_on,
MCE_DEFAULT_POWERKEY_ACTIONS_LONG_ON,
pwrkey_setting_cb,
&pwrkey_actions_long_on_setting_id);
mce_setting_track_string(MCE_SETTING_POWERKEY_ACTIONS_SINGLE_OFF,
&pwrkey_actions_single_off,
MCE_DEFAULT_POWERKEY_ACTIONS_SINGLE_OFF,
pwrkey_setting_cb,
&pwrkey_actions_single_off_setting_id);
mce_setting_track_string(MCE_SETTING_POWERKEY_ACTIONS_DOUBLE_OFF,
&pwrkey_actions_double_off,
MCE_DEFAULT_POWERKEY_ACTIONS_DOUBLE_OFF,
pwrkey_setting_cb,
&pwrkey_actions_double_off_setting_id);
mce_setting_track_string(MCE_SETTING_POWERKEY_ACTIONS_LONG_OFF,
&pwrkey_actions_long_off,
MCE_DEFAULT_POWERKEY_ACTIONS_LONG_OFF,
pwrkey_setting_cb,
&pwrkey_actions_long_off_setting_id);
mce_setting_track_int(MCE_SETTING_DOUBLETAP_MODE,
&pwrkey_gestures_enable_mode,
MCE_DEFAULT_DOUBLETAP_MODE,
pwrkey_setting_cb,
&pwrkey_gestures_enable_mode_cb_id);
for( size_t i = 0; i < POWERKEY_ACTIONS_GESTURE_COUNT; ++i ) {
mce_setting_track_string(pwrkey_actions_gesture_key[i],
&pwrkey_actions_gesture[i],
pwrkey_actions_gesture_val[i],
pwrkey_setting_cb,
&pwrkey_actions_gesture_setting_id[i]);
}
/* D-Bus actions */
for( size_t action_id = 0; action_id < POWEKEY_DBUS_ACTION_COUNT; ++action_id ) {
pwrkey_dbus_action_t *action = pwrkey_dbus_action + action_id;
mce_setting_track_string(action->setting_key,
&action->setting_val,
action->setting_def,
pwrkey_setting_cb,
&action->setting_id);
}
/* Apply sanity checks */
pwrkey_setting_sanitize_now();
}
/** Stop tracking setting changes
*/
static void
pwrkey_setting_quit(void)
{
/* Power key press handling mode */
mce_setting_notifier_remove(pwrkey_stm_enable_mode_setting_id),
pwrkey_stm_enable_mode_setting_id = 0;
/* Power key press blanking mode */
mce_setting_notifier_remove(pwrkey_action_blank_mode_setting_id),
pwrkey_action_blank_mode_setting_id = 0;
/* Power key press blanking mode */
mce_setting_notifier_remove(pwrkey_ps_override_count_setting_id),
pwrkey_ps_override_count_setting_id = 0;
/* Power key press blanking mode */
mce_setting_notifier_remove(pwrkey_ps_override_timeout_setting_id),
pwrkey_ps_override_timeout_setting_id = 0;
/* Action sets */
mce_setting_notifier_remove(pwrkey_actions_single_on_setting_id),
pwrkey_actions_single_on_setting_id = 0;
mce_setting_notifier_remove(pwrkey_actions_double_on_setting_id),
pwrkey_actions_double_on_setting_id = 0;
mce_setting_notifier_remove(pwrkey_actions_long_on_setting_id),
pwrkey_actions_long_on_setting_id = 0;
mce_setting_notifier_remove(pwrkey_actions_single_off_setting_id),
pwrkey_actions_single_off_setting_id = 0;
mce_setting_notifier_remove(pwrkey_actions_double_off_setting_id),
pwrkey_actions_double_off_setting_id = 0;
mce_setting_notifier_remove(pwrkey_actions_long_off_setting_id),
pwrkey_actions_long_off_setting_id = 0;
mce_setting_notifier_remove(pwrkey_gestures_enable_mode_cb_id),
pwrkey_gestures_enable_mode_cb_id = 0;
for( size_t i = 0; i < POWERKEY_ACTIONS_GESTURE_COUNT; ++i ) {
mce_setting_notifier_remove(pwrkey_actions_gesture_setting_id[i]),
pwrkey_actions_gesture_setting_id[i] = 0;
}
g_free(pwrkey_actions_single_on),
pwrkey_actions_single_on = 0;
g_free(pwrkey_actions_double_on),
pwrkey_actions_double_on = 0;
g_free(pwrkey_actions_long_on),
pwrkey_actions_long_on = 0;
g_free(pwrkey_actions_single_off),
pwrkey_actions_single_off = 0;
g_free(pwrkey_actions_double_off),
pwrkey_actions_double_off = 0;
g_free(pwrkey_actions_long_off),
pwrkey_actions_long_off = 0;;
for( size_t i = 0; i < POWERKEY_ACTIONS_GESTURE_COUNT; ++i ) {
g_free(pwrkey_actions_gesture[i]),
pwrkey_actions_gesture[i] = 0;;
}
/* Cancel pending delayed setting sanitizing */
pwrkey_setting_sanitize_cancel();
/* D-Bus actions */
for( size_t action_id = 0; action_id < POWEKEY_DBUS_ACTION_COUNT; ++action_id ) {
pwrkey_dbus_action_t *action = pwrkey_dbus_action + action_id;
mce_setting_notifier_remove(action->setting_id),
action->setting_id = 0;
g_free(action->setting_val),
action->setting_val = 0;;
}
}
/* ========================================================================= *
* DATAPIPE_HANDLING
* ========================================================================= */
/** Change notifications for system_state
*/
static void pwrkey_datapipe_system_state_cb(gconstpointer data)
{
system_state_t prev = system_state;
system_state = GPOINTER_TO_INT(data);
if( prev == system_state )
goto EXIT;
mce_log(LL_DEBUG, "system_state: %s -> %s",
system_state_repr(prev),
system_state_repr(system_state));
EXIT:
return;
}
/** Notification callback for devicelock_state_pipe
*
* @param data devicelock_state_t value as void pointer
*/
static void
pwrkey_datapipe_devicelock_state_cb(gconstpointer data)
{
devicelock_state_t prev = devicelock_state;
devicelock_state = GPOINTER_TO_INT(data);
if( devicelock_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "devicelock_state = %s -> %s",
devicelock_state_repr(prev),
devicelock_state_repr(devicelock_state));
EXIT:
return;
}
/** Handle display state change notifications
*
* @param data display state (as void pointer)
*/
static void
pwrkey_datapipe_display_state_curr_cb(gconstpointer data)
{
display_state_t prev = display_state_curr;
display_state_curr = GPOINTER_TO_INT(data);
if( display_state_curr == prev )
goto EXIT;
mce_log(LL_DEBUG, "display_state_curr = %s -> %s",
display_state_repr(prev),
display_state_repr(display_state_curr));
homekey_stm_eval_state();
EXIT:
return;
}
/** Pre-change notifications for display_state_curr
*/
static void pwrkey_datapipe_display_state_next_cb(gconstpointer data)
{
display_state_t prev = display_state_next;
display_state_next = GPOINTER_TO_INT(data);
if( prev == display_state_next )
goto EXIT;
mce_log(LL_DEBUG, "display_state_next = %s -> %s",
display_state_repr(prev),
display_state_repr(display_state_next));
homekey_stm_eval_state();
EXIT:
return;
}
/** Change notifications from lid_sensor_filtered_pipe
*/
static void pwrkey_datapipe_lid_sensor_filtered_cb(gconstpointer data)
{
cover_state_t prev = lid_sensor_filtered;
lid_sensor_filtered = GPOINTER_TO_INT(data);
if( lid_sensor_filtered == prev )
goto EXIT;
mce_log(LL_DEBUG, "lid_sensor_filtered = %s -> %s",
cover_state_repr(prev),
cover_state_repr(lid_sensor_filtered));
EXIT:
return;
}
/** Change notifications for proximity_sensor_actual
*/
static void pwrkey_datapipe_proximity_sensor_actual_cb(gconstpointer data)
{
cover_state_t prev = proximity_sensor_actual;
proximity_sensor_actual = GPOINTER_TO_INT(data);
if( proximity_sensor_actual == prev )
goto EXIT;
mce_log(LL_DEBUG, "proximity_sensor_actual = %s -> %s",
proximity_state_repr(prev),
proximity_state_repr(proximity_sensor_actual));
EXIT:
return;
}
/** Handle ngfd_service_state notifications
*
* @param data service availability (as void pointer)
*/
static void
pwrkey_datapipe_ngfd_service_state_cb(gconstpointer data)
{
service_state_t prev = ngfd_service_state;
ngfd_service_state = GPOINTER_TO_INT(data);
if( ngfd_service_state == prev )
goto EXIT;
mce_log(LL_NOTICE, "ngfd_service_state = %s -> %s",
service_state_repr(prev),
service_state_repr(ngfd_service_state));
if( ngfd_service_state != SERVICE_STATE_RUNNING )
xngf_delete_client();
EXIT:
return;
}
/**
* Datapipe trigger for the [power] key
*
* @param data A pointer to the input_event struct
*/
static void
pwrkey_datapipe_keypress_event_cb(gconstpointer const data)
{
/* Faulty/aged physical power key buttons can generate
* bursts of press and release events that are then
* interpreted as double presses. To avoid this we
* ignore power key presses that occur so soon after
* previous release that they are unlikely to be
* caused by human activity. */
/* Minimum delay between power key release and press. */
static const int64_t press_delay = 50;
/* Time limit for accepting the next power key press */
static int64_t press_limit = 0;
const struct input_event * const *evp;
const struct input_event *ev;
if( !(evp = data) )
goto EXIT;
if( !(ev = *evp) )
goto EXIT;
switch( ev->type ) {
case EV_KEY:
switch( ev->code ) {
case KEY_POWER:
if( ev->value == 1 ) {
if( mce_lib_get_boot_tick() < press_limit ) {
/* Too soon after the previous powerkey
* release -> assume faulty hw sending
* bursts of presses */
mce_log(LL_CRUCIAL, "powerkey press ignored");
}
else {
mce_log(LL_CRUCIAL, "powerkey pressed");
/* Detect repeated power key pressing while
* proximity sensor is covered; assume it means
* the sensor is stuck and user wants to be able
* to turn on the display regardless of the sensor
* state */
pwrkey_ps_override_evaluate();
/* Power key pressed */
pwrkey_stm_powerkey_pressed();
/* Some devices report both power key press and release
* already when the physical button is pressed down.
* Other devices wait for physical release before
* reporting key release. And in some devices it depends
* on whether the device is suspended or not.
*
* To normalize behavior in default configuration (i.e.
* begin display power up already on power key press
* without waiting for user to lift finger off the button):
* Synthetize key release, if no actions are bound to long
* power key press from display off state. */
if( pwrkey_stm_display_state == MCE_DISPLAY_OFF ) {
if( !pwrkey_actions_from_display_off.mask_long ) {
mce_log(LL_DEBUG, "powerkey release simulated");
pwrkey_stm_powerkey_released();
}
}
}
}
else if( ev->value == 0 ) {
mce_log(LL_CRUCIAL, "powerkey released");
/* Power key released */
pwrkey_stm_powerkey_released();
/* Adjust time limit for accepting the next power
* key press */
press_limit = mce_lib_get_boot_tick() + press_delay;
}
pwrkey_stm_rethink_wakelock();
break;
case KEY_HOME:
if( ev->value == 1 ) {
mce_log(LL_CRUCIAL, "homekey pressed");
homekey_stm_set_pressed(true);
}
else if( ev->value == 0 ) {
mce_log(LL_CRUCIAL, "homekey released");
homekey_stm_set_pressed(false);
}
break;
default:
break;
}
break;
case EV_MSC:
if( ev->code == MSC_GESTURE ) {
mce_log(LL_CRUCIAL, "gesture(%d)", ev->value);
pwrkey_actions_do_gesture(ev->value);
}
break;
default:
break;
}
EXIT:
return;
}
/** Handle call state change notifications
*
* @param data call state (as void pointer)
*/
static void
pwrkey_datapipe_call_state_cb(gconstpointer data)
{
call_state_t prev = call_state;
call_state = GPOINTER_TO_INT(data);
if( call_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "call_state = %s -> %s",
call_state_repr(prev),
call_state_repr(call_state));
if( pwrkey_ignore_incoming_call ) {
mce_log(LL_DEBUG, "stop ignoring incoming calls");
pwrkey_ignore_incoming_call = false;
}
EXIT:
return;
}
/** Handle alarm ui state change notifications
*
* @param data alarm ui state (as void pointer)
*/
static void
pwrkey_datapipe_alarm_ui_state_cb(gconstpointer data)
{
alarm_ui_state_t prev = alarm_ui_state;
alarm_ui_state = GPOINTER_TO_INT(data);
if( alarm_ui_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "alarm_ui_state = %s -> %s",
alarm_state_repr(prev),
alarm_state_repr(alarm_ui_state));
EXIT:
return;
}
/** Change notifications for devicelock_service_state
*/
static void pwrkey_datapipe_devicelock_service_state_cb(gconstpointer data)
{
service_state_t prev = devicelock_service_state;
devicelock_service_state = GPOINTER_TO_INT(data);
if( devicelock_service_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "devicelock_service_state = %s -> %s",
service_state_repr(prev),
service_state_repr(devicelock_service_state));
/* no immediate action, but see pwrkey_action_devlock() */
EXIT:
return;
}
/** Change notifications for enroll_in_progress
*/
static void pwrkey_datapipe_enroll_in_progress_cb(gconstpointer data)
{
bool prev = enroll_in_progress;
enroll_in_progress = GPOINTER_TO_INT(data);
if( enroll_in_progress == prev )
goto EXIT;
mce_log(LL_DEBUG, "enroll_in_progress = %s -> %s",
prev ? "true" : "false",
enroll_in_progress ? "true" : "false");
/* no immediate action, but see pwrkey_stm_ignore_action() */
EXIT:
return;
}
/** Handle NGFD play event requests
*
* @param data Requested event name (as void pointer)
*/
static void pwrkey_datapipe_ngfd_event_request_cb(gconstpointer data)
{
const char *event = data;
mce_log(LL_DEBUG, "ngfd event request = %s", event);
xngf_play_event(event);
}
/** Array of datapipe handlers */
static datapipe_handler_t pwrkey_datapipe_handlers[] =
{
// input triggers
{
.datapipe = &keypress_event_pipe,
.input_cb = pwrkey_datapipe_keypress_event_cb,
},
{
.datapipe = &ngfd_event_request_pipe,
.input_cb = pwrkey_datapipe_ngfd_event_request_cb,
},
// output triggers
{
.datapipe = &ngfd_service_state_pipe,
.output_cb = pwrkey_datapipe_ngfd_service_state_cb,
},
{
.datapipe = &system_state_pipe,
.output_cb = pwrkey_datapipe_system_state_cb,
},
{
.datapipe = &devicelock_state_pipe,
.output_cb = pwrkey_datapipe_devicelock_state_cb,
},
{
.datapipe = &display_state_curr_pipe,
.output_cb = pwrkey_datapipe_display_state_curr_cb,
},
{
.datapipe = &display_state_next_pipe,
.output_cb = pwrkey_datapipe_display_state_next_cb,
},
{
.datapipe = &lid_sensor_filtered_pipe,
.output_cb = pwrkey_datapipe_lid_sensor_filtered_cb,
},
{
.datapipe = &proximity_sensor_actual_pipe,
.output_cb = pwrkey_datapipe_proximity_sensor_actual_cb,
},
{
.datapipe = &alarm_ui_state_pipe,
.output_cb = pwrkey_datapipe_alarm_ui_state_cb,
},
{
.datapipe = &call_state_pipe,
.output_cb = pwrkey_datapipe_call_state_cb,
},
{
.datapipe = &devicelock_service_state_pipe,
.output_cb = pwrkey_datapipe_devicelock_service_state_cb,
},
{
.datapipe = &enroll_in_progress_pipe,
.output_cb = pwrkey_datapipe_enroll_in_progress_cb,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t pwrkey_datapipe_bindings =
{
.module = MODULE_NAME,
.handlers = pwrkey_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void
pwrkey_datapipe_init(void)
{
mce_datapipe_init_bindings(&pwrkey_datapipe_bindings);
}
/** Remove triggers/filters from datapipes
*/
static void
pwrkey_datapipe_quit(void)
{
mce_datapipe_quit_bindings(&pwrkey_datapipe_bindings);
}
/* ========================================================================= *
* NGFD_GLUE
* ========================================================================= */
static NgfClient *ngf_client_hnd = 0;
static DBusConnection *ngf_dbus_con = 0;
static uint32_t ngf_event_id = 0;
static const char *
xngf_state_repr(NgfEventState state)
{
const char *repr = "unknown";
switch( state ) {
case NGF_EVENT_FAILED: repr = "failed"; break;
case NGF_EVENT_COMPLETED: repr = "completed"; break;
case NGF_EVENT_PLAYING: repr = "playing"; break;
case NGF_EVENT_PAUSED: repr = "paused"; break;
default: break;
}
return repr;
}
static void
xngf_status_cb(NgfClient *client, uint32_t event_id, NgfEventState state, void *userdata)
{
(void) client;
(void) userdata;
mce_log(LL_DEBUG, "%s(%d)", xngf_state_repr(state), event_id);
switch( state ) {
default:
case NGF_EVENT_PLAYING:
case NGF_EVENT_PAUSED:
break;
case NGF_EVENT_COMPLETED:
ngf_event_id = 0;
break;
case NGF_EVENT_FAILED:
mce_log(LL_ERR, "Failed to play id %d", event_id);
ngf_event_id = 0;
break;
}
}
static bool
xngf_create_client(void)
{
if( !ngf_dbus_con ) {
mce_log(LL_WARN, "can't use ngfd - no dbus connection");
goto EXIT;
}
if( ngfd_service_state != SERVICE_STATE_RUNNING ) {
mce_log(LL_WARN, "can't use ngfd - service not running");
goto EXIT;
}
if( ngf_client_hnd )
goto EXIT;
ngf_client_hnd = ngf_client_create(NGF_TRANSPORT_DBUS, ngf_dbus_con);
if( !ngf_client_hnd ) {
mce_log(LL_WARN, "can't use ngfd - failed to create client");
goto EXIT;
}
ngf_client_set_callback(ngf_client_hnd, xngf_status_cb, NULL);
mce_log(LL_DEBUG, "ngfd client created");
EXIT:
return ngf_client_hnd != 0;
}
static void
xngf_delete_client(void)
{
if( ngf_client_hnd ) {
ngf_client_destroy(ngf_client_hnd), ngf_client_hnd = 0;
mce_log(LL_DEBUG, "ngfd client deleted");
}
ngf_event_id = 0;
}
static void
xngf_play_event(const char *event_name)
{
if( ngf_event_id ) {
mce_log(LL_WARN, "previous event not finished yet");
goto EXIT;
}
if( !xngf_create_client() )
goto EXIT;
ngf_event_id = ngf_client_play_event (ngf_client_hnd, event_name, NULL);
mce_log(LL_DEBUG, "event=%s, id=%d", event_name, ngf_event_id);
EXIT:
return;
}
static void
xngf_init(void)
{
ngf_dbus_con = dbus_connection_get();
}
static void
xngf_quit(void)
{
xngf_delete_client();
if (ngf_dbus_con)
dbus_connection_unref(ngf_dbus_con), ngf_dbus_con = 0;
}
/* ========================================================================= *
* MODULE_INTEFACE
* ========================================================================= */
/**
* Init function for the powerkey component
*
* @return TRUE on success, FALSE on failure
*/
gboolean mce_powerkey_init(void)
{
pwrkey_datapipe_init();
pwrkey_dbus_init();
pwrkey_setting_init();
xngf_init();
return TRUE;
}
/**
* Exit function for the powerkey component
*
* @todo D-Bus unregistration
*/
void mce_powerkey_exit(void)
{
xngf_quit();
pwrkey_dbus_quit();
pwrkey_setting_quit();
pwrkey_datapipe_quit();
/* Remove all timer sources & release wakelock */
pwrkey_stm_terminate();
common_on_proximity_cancel(MODULE_NAME, 0, 0);
return;
}