/**
* @file fingerprint.c
*
* Fingerprint daemon tracking module for the Mode Control Entity
*
* Copyright (c) 2015-2019 Jolla Ltd.
*
* @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-lib.h"
#include "../mce-log.h"
#include "../mce-dbus.h"
#include "../mce-setting.h"
#include "../evdev.h"
#include
#include
/* ========================================================================= *
* Types
* ========================================================================= */
/** Return values for requests made to fingerprint daemon
*
* Keep fpreply_repr() in sync with any changes made here.
*/
typedef enum fpreply_t
{
/** Operation successfully started */
FPREPLY_STARTED = 0,
/** Unspecified (low level) failure */
FPREPLY_FAILED = 1,
/** Abort() while already idle */
FPREPLY_ALREADY_IDLE = 2,
/** Abort/Enroll/Identify() while busy */
FPREPLY_ALREADY_BUSY = 3,
/** Not allowed */
FPREPLY_DENIED = 4,
/** Enroll() key that already exists */
FPREPLY_KEY_ALREADY_EXISTS = 5,
/** Remove() key that does not exist */
FPREPLY_KEY_DOES_NOT_EXIST = 6,
/** Identify() without having any keys */
FPREPLY_NO_KEYS_AVAILABLE = 7,
/** Null or otherwise illegal key name */
FPREPLY_KEY_IS_INVALID = 8,
} fpreply_t;
/** Resulting events from accepted fingerprint daemon requests
*
* Keep fpresult_repr() in sync with any changes made here.
*/
typedef enum fpresult_t
{
FPRESULT_ABORTED,
FPRESULT_FAILED,
FPRESULT_IDENTIFIED,
FPRESULT_VERIFIED,
} fpresult_t;
/** Fingerprint daemon ipc operation state
*
* Keep fpopstate_repr() in sync with any changes made here.
*/
typedef enum fpopstate_t
{
/** Initial state */
FPOPSTATE_INITIALIZE,
/** Wait until operation is required and fpd is idle */
FPOPSTATE_WAITING,
/** Send asynchronous dbus method call and wait for reply */
FPOPSTATE_REQUEST,
/** Wait for operation results / errors / cancellation */
FPOPSTATE_PENDING,
/* Operation was successfully finished */
FPOPSTATE_SUCCESS,
/* Operation failed */
FPOPSTATE_FAILURE,
/* Send asynchronous abort dbus method call and wait for reply */
FPOPSTATE_ABORT,
/** Wait for fpd to make transition to idle state */
FPOPSTATE_ABORTING,
/** Opearation was aborted */
FPOPSTATE_ABORTED,
/** Delay in between operation retry attempts */
FPOPSTATE_THROTTLING,
/** Number of possible states */
FPOPSTATE_NUMOF
} fpopstate_t;
/** State machine for performing ipc operations with fingerprint daemon
*
* The happy path for making request to fingerprint daemon over dbus is:
*
* 1. Wait for daemon to be idle
* 2. Request start of operation
* 3. Wait for operation started acknowledgement
* 4. Wait for operation result
*
* To facilitate overlapping use by multiple clients, all clients must
* expect requests to be denied (while busy with requests from other
* clients), daemon dropping out of system bus and coming back up, and
* illogical seeming state transitions and be prepared to retry until
* succeeding.
*
* The fpoperation_t structure contains bookkeeping data for generic
* state machine that can be used to perform any fingerprint daemon
* request by providing suitable hooks.
*/
typedef struct fpoperation_t fpoperation_t;
struct fpoperation_t
{
/** State machine name */
const char *fpo_name;
/** Current state */
fpopstate_t fpo_state;
/** Expected/tracked fpstate
*
* Used for detecting situations where we're obviously out of
* sync with what is going on at the fingerprint daemon side.
*/
fpstate_t fpo_fpstate;
/** Pending async D-Bus method call
*
* This is either the operation this state machine is expected
* to perform, or abort used for canceling successfully started
* request.
*/
DBusPendingCall *fpo_pending;
/** Pending timeout
*
* Used for throttling consecutive operations, so that we allow
* time for system state changes affecting the use of this state
* machine to occur.
*/
guint fpo_timer;
/** Hook for entering a state */
void (*fpo_enter_cb)(fpoperation_t *self);
/** Hook for leaving a state */
void (*fpo_leave_cb)(fpoperation_t *self);
/** Hook for evaluating staying in a state */
void (*fpo_eval_cb) (fpoperation_t *self);
/** Hook for handling operation result events */
void (*fpo_result_cb)(fpoperation_t *self, fpresult_t event);
};
/* ========================================================================= *
* Prototypes
* ========================================================================= */
/* ------------------------------------------------------------------------- *
* FPREPLY
* ------------------------------------------------------------------------- */
static const char *fpreply_repr(fpreply_t val);
/* ------------------------------------------------------------------------- *
* FPRESULT
* ------------------------------------------------------------------------- */
static const char *fpresult_repr(fpresult_t event);
/* ------------------------------------------------------------------------- *
* FPOPSTATE
* ------------------------------------------------------------------------- */
static const char *fpopstate_repr(fpopstate_t state);
/* ------------------------------------------------------------------------- *
* FPOPERATION
* ------------------------------------------------------------------------- */
static const char *fpoperation_name (const fpoperation_t *self);
static fpopstate_t fpoperation_state (const fpoperation_t *self);
static void fpoperation_enter (fpoperation_t *self);
static void fpoperation_leave (fpoperation_t *self);
static bool fpoperation_eval_overrides (fpoperation_t *self);
static void fpoperation_eval (fpoperation_t *self);
static void fpoperation_result (fpoperation_t *self, fpresult_t event);
static void fpoperation_trans (fpoperation_t *self, fpopstate_t state);
static fpstate_t fpoperation_get_fpstate (const fpoperation_t *self);
static void fpoperation_set_fpstate (fpoperation_t *self, fpstate_t state);
static void fpoperation_cancel_timout (fpoperation_t *self);
static bool fpoperation_detach_timout (fpoperation_t *self);
static void fpoperation_attach_timeout (fpoperation_t *self, int delay, GSourceFunc cb);
static gboolean fpoperation_trigger_fpwakeup_cb(gpointer aptr);
static gboolean fpoperation_throttling_ended_cb(gpointer aptr);
static void fpoperation_cancel_pending_call(fpoperation_t *self);
static bool fpoperation_detach_pending_call(fpoperation_t *self, DBusPendingCall *pc);
static void fpoperation_attach_pending_call(fpoperation_t *self, DBusPendingCall *pc);
static void fpoperation_identify_reply_cb (DBusPendingCall *pc, void *aptr);
static void fpoperation_start_identify (fpoperation_t *self);
static void fpoperation_abort_reply_cb (DBusPendingCall *pc, void *aptr);
static void fpoperation_start_abort (fpoperation_t *self);
/* ------------------------------------------------------------------------- *
* FPIDENTIFY
* ------------------------------------------------------------------------- */
static void fpidentify_enter_cb (fpoperation_t *self);
static void fpidentify_leave_cb (fpoperation_t *self);
static void fpidentify_eval_cb (fpoperation_t *self);
static void fpidentify_result_cb(fpoperation_t *self, fpresult_t event);
/* ------------------------------------------------------------------------- *
* FINGERPRINT_DATA
* ------------------------------------------------------------------------- */
static gpointer fingerprint_data_create (const char *name);
static void fingerprint_data_detete_cb(gpointer aptr);
static void fingerprint_data_flush (void);
static void fingerprint_data_remove (const char *name);
static void fingerprint_data_add (const char *name);
static bool fingerprint_data_exists (void);
static void fingerprint_data_init (void);
static void fingerprint_data_quit (void);
/* ------------------------------------------------------------------------- *
* FINGERPRINT_LED_SCANNING
* ------------------------------------------------------------------------- */
static void fingerprint_led_scanning_activate(bool activate);
/* ------------------------------------------------------------------------- *
* FINGERPRINT_LED_ACQUIRED
* ------------------------------------------------------------------------- */
static void fingerprint_led_acquired_activate(bool activate);
static gboolean fingerprint_led_acquired_timer_cb(gpointer aptr);
static void fingerprint_led_acquired_trigger (void);
static void fingerprint_led_acquired_cancel (void);
/* ------------------------------------------------------------------------- *
* FINGERPRINT_DATAPIPE
* ------------------------------------------------------------------------- */
static void fingerprint_datapipe_set_fpstate (fpstate_t state);
static bool fingerprint_datapipe_evaluate_enroll_in_progress(void);
static void fingerprint_datapipe_update_enroll_in_progress (void);
static void fingerprint_datapipe_generate_activity (void);
static void fingerprint_datapipe_fpd_service_state_cb (gconstpointer data);
static void fingerprint_datapipe_system_state_cb (gconstpointer data);
static void fingerprint_datapipe_devicelock_state_cb (gconstpointer data);
static void fingerprint_datapipe_submode_cb (gconstpointer data);
static void fingerprint_datapipe_display_state_next_cb (gconstpointer data);
static void fingerprint_datapipe_interaction_expected_cb (gconstpointer data);
static void fingerprint_datapipe_topmost_window_pid_cb (gconstpointer data);
static void fingerprint_datapipe_proximity_sensor_actual_cb (gconstpointer data);
static void fingerprint_datapipe_lid_sensor_filtered_cb (gconstpointer data);
static void fingerprint_datapipe_keypress_event_cb (gconstpointer const data);
static void fingerprint_datapipe_init (void);
static void fingerprint_datapipe_quit (void);
/* ------------------------------------------------------------------------- *
* FINGERPRINT_SETTING
* ------------------------------------------------------------------------- */
static void fingerprint_setting_cb (GConfClient *const gcc, const guint id, GConfEntry *const entry, gpointer const data);
static void fingerprint_setting_init(void);
static void fingerprint_setting_quit(void);
/* ------------------------------------------------------------------------- *
* FINGERPRINT_DBUS
* ------------------------------------------------------------------------- */
static gboolean fingerprint_dbus_fpstate_changed_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpacquired_info_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpadded_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpremoved_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpidentified_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpaborted_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpfailed_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpverified_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fperror_cb (DBusMessage *const msg);
static gboolean fingerprint_dbus_fpprogress_cb (DBusMessage *const msg);
static void fingerprint_dbus_init (void);
static void fingerprint_dbus_quit (void);
static void fingerprint_dbus_fpstate_query_cb (DBusPendingCall *pc, void *aptr);
static void fingerprint_dbus_fpstate_query_cancel(void);
static void fingerprint_dbus_fpstate_query_start (void);
static void fingerprint_dbus_fpdata_query_cb (DBusPendingCall *pc, void *aptr);
static void fingerprint_dbus_fpdata_query_cancel (void);
static void fingerprint_dbus_fpdata_query_start (void);
/* ------------------------------------------------------------------------- *
* FPWAKEUP
* ------------------------------------------------------------------------- */
static bool fpwakeup_is_allowed (void);
static void fpwakeup_set_allowed (bool allowed);
static gboolean fpwakeup_allow_cb (gpointer aptr);
static void fpwakeup_cancel_allow (void);
static void fpwakeup_schedule_allow (void);
static bool fpwakeup_evaluate_allowed (void);
static void fpwakeup_update_allowed (void);
static void fpwakeup_rethink_now (void);
static gboolean fpwakeup_rethink_cb (gpointer aptr);
static void fpwakeup_schedule_rethink (void);
static void fpwakeup_cancel_rethink (void);
static void fpwakeup_propagate_fpstate (void);
static void fpwakeup_propagate_fpresult(fpresult_t event);
static void fpwakeup_propagate_eval (void);
static bool fpwakeup_set_primed (bool prime);
static void fpwakeup_trigger (void);
/* ------------------------------------------------------------------------- *
* G_MODULE
* ------------------------------------------------------------------------- */
const gchar *g_module_check_init(GModule *module);
void g_module_unload (GModule *module);
/* ========================================================================= *
* FINGERPRINT_DATAPIPE
* ========================================================================= */
/** Cached fpd service availability; assume unknown */
static service_state_t fpd_service_state = SERVICE_STATE_UNDEF;
/** Cached system_state; assume unknown */
static system_state_t system_state = MCE_SYSTEM_STATE_UNDEF;
/** Cached devicelock_state ; assume unknown */
static devicelock_state_t devicelock_state = DEVICELOCK_STATE_UNDEFINED;
/** Cached submode ; assume invalid */
static submode_t submode = MCE_SUBMODE_INVALID;
/** Cached target display_state; assume unknown */
static display_state_t display_state_next = MCE_DISPLAY_UNDEF;
/** Interaction expected; assume false */
static bool interaction_expected = false;
/** Cached PID of process owning the topmost window on UI */
static int topmost_window_pid = -1;
/** Cached proximity sensor state */
static cover_state_t proximity_sensor_actual = COVER_UNDEF;
/** Lid cover policy state; assume unknown */
static cover_state_t lid_sensor_filtered = COVER_UNDEF;
/** Cached power key pressed down state */
static bool powerkey_pressed = false;
/* ========================================================================= *
* FINGERPRINT_SETTINGS
* ========================================================================= */
/** Fingerprint wakeup enable mode */
static gint fingerprint_wakeup_mode = MCE_DEFAULT_FPWAKEUP_MODE;
static guint fingerprint_wakeup_mode_setting_id = 0;
static gint fingerprint_allow_delay = MCE_DEFAULT_FPWAKEUP_ALLOW_DELAY;
static guint fingerprint_allow_delay_setting_id = 0;
static gint fingerprint_trigger_delay = MCE_DEFAULT_FPWAKEUP_TRIGGER_DELAY;
static guint fingerprint_trigger_delay_setting_id = 0;
static gint fingerprint_throttle_delay = MCE_DEFAULT_FPWAKEUP_THROTTLE_DELAY;
static guint fingerprint_throttle_delay_setting_id = 0;
/* ========================================================================= *
* MANAGED_STATES
* ========================================================================= */
/** Tracked fpd operational state; assume unknown */
static fpstate_t fpstate = FPSTATE_UNSET;
/** Tracked fingerprint enroll status; assume not in progress */
static bool enroll_in_progress = false;
/** State machine data for handling fpd requests */
static fpoperation_t fpoperation_lut[] =
{
{
.fpo_name = "identify_stm",
.fpo_state = FPOPSTATE_INITIALIZE,
.fpo_fpstate = FPSTATE_UNSET,
.fpo_pending = 0,
.fpo_timer = 0,
.fpo_enter_cb = fpidentify_enter_cb,
.fpo_leave_cb = fpidentify_leave_cb,
.fpo_eval_cb = fpidentify_eval_cb,
.fpo_result_cb = fpidentify_result_cb,
},
};
/* ========================================================================= *
* FPREPLY
* ========================================================================= */
static const char *
fpreply_repr(fpreply_t val)
{
const char *repr = "FPREPLY_UNKNOWN";
#define REPR_VAL(NAME) case NAME: repr = #NAME; break
switch( val ) {
REPR_VAL(FPREPLY_STARTED);
REPR_VAL(FPREPLY_FAILED);
REPR_VAL(FPREPLY_ALREADY_IDLE);
REPR_VAL(FPREPLY_ALREADY_BUSY);
REPR_VAL(FPREPLY_DENIED);
REPR_VAL(FPREPLY_KEY_ALREADY_EXISTS);
REPR_VAL(FPREPLY_KEY_DOES_NOT_EXIST);
REPR_VAL(FPREPLY_NO_KEYS_AVAILABLE);
REPR_VAL(FPREPLY_KEY_IS_INVALID);
default: break;
}
#undef REPR_VAL
return repr;
}
/* ========================================================================= *
* FPRESULT
* ========================================================================= */
static const char *
fpresult_repr(fpresult_t event)
{
static const char * const fpresult_lut[] =
{
[FPRESULT_ABORTED] = "FPRESULT_ABORTED",
[FPRESULT_FAILED] = "FPRESULT_FAILED",
[FPRESULT_IDENTIFIED] = "FPRESULT_IDENTIFIED",
[FPRESULT_VERIFIED] = "FPRESULT_VERIFIED",
};
return fpresult_lut[event];
}
/* ========================================================================= *
* FPOPSTATE
* ========================================================================= */
static const char *
fpopstate_repr(fpopstate_t state)
{
static const char * const fpopstate_lut[FPOPSTATE_NUMOF] = {
[FPOPSTATE_INITIALIZE] = "FPOPSTATE_INITIALIZE",
[FPOPSTATE_WAITING] = "FPOPSTATE_WAITING",
[FPOPSTATE_REQUEST] = "FPOPSTATE_REQUEST",
[FPOPSTATE_PENDING] = "FPOPSTATE_PENDING",
[FPOPSTATE_SUCCESS] = "FPOPSTATE_SUCCESS",
[FPOPSTATE_FAILURE] = "FPOPSTATE_FAILURE",
[FPOPSTATE_ABORT] = "FPOPSTATE_ABORT",
[FPOPSTATE_ABORTING] = "FPOPSTATE_ABORTING",
[FPOPSTATE_ABORTED] = "FPOPSTATE_ABORTED",
[FPOPSTATE_THROTTLING] = "FPOPSTATE_THROTTLING",
};
return fpopstate_lut[state];
}
/* ========================================================================= *
* FPOPERATION
* ========================================================================= */
/** Accessor for operation name
*/
static const char *
fpoperation_name(const fpoperation_t *self)
{
return self->fpo_name ?: "unnamed";
}
/** Accessor for operation state
*/
static fpopstate_t
fpoperation_state(const fpoperation_t *self)
{
return self->fpo_state;
}
/** Handle tasks after entering to a state
*/
static void
fpoperation_enter(fpoperation_t *self)
{
if( self->fpo_enter_cb )
self->fpo_enter_cb(self);
}
/** Handle tasks after leaving a state
*/
static void
fpoperation_leave(fpoperation_t *self)
{
if( self->fpo_leave_cb )
self->fpo_leave_cb(self);
}
/** Handle evaluation of generic rules
*/
static bool
fpoperation_eval_overrides(fpoperation_t *self)
{
bool overridden = false;
/* If fingerprint daemon is not on system bus, cancel any
* ongoing async activity via transition to aborted state.
*/
if( fpstate == FPSTATE_UNSET ) {
switch( fpoperation_state(self) ) {
case FPOPSTATE_INITIALIZE:
case FPOPSTATE_WAITING:
/* Nothing initiated -> NOP */
break;
case FPOPSTATE_REQUEST:
case FPOPSTATE_PENDING:
case FPOPSTATE_SUCCESS:
case FPOPSTATE_FAILURE:
case FPOPSTATE_ABORT:
case FPOPSTATE_ABORTING:
fpoperation_trans(self, FPOPSTATE_ABORTED);
overridden = true;
break;
default:
case FPOPSTATE_ABORTED:
case FPOPSTATE_THROTTLING:
/* No pending ipc -> NOP */
break;
}
}
return overridden;
}
/** Evaluate whether current state is still valid
*/
static void
fpoperation_eval(fpoperation_t *self)
{
if( !fpoperation_eval_overrides(self) ) {
if( self->fpo_eval_cb )
self->fpo_eval_cb(self);
}
}
/** Handle operation result events
*/
static void
fpoperation_result(fpoperation_t *self, fpresult_t event)
{
mce_log(LL_DEBUG, "%s @ %s: got event %s",
fpoperation_name(self),
fpopstate_repr(fpoperation_state(self)),
fpresult_repr(event));
if( self->fpo_result_cb )
self->fpo_result_cb(self, event);
}
/** Handle state transition
*/
static void
fpoperation_trans(fpoperation_t *self, fpopstate_t state)
{
if( self->fpo_state != state ) {
mce_log(LL_DEBUG, "%s @ %s: transition to %s",
fpoperation_name(self),
fpopstate_repr(self->fpo_state),
fpopstate_repr(state));
fpoperation_leave(self);
self->fpo_state = state;
fpoperation_enter(self);
fpwakeup_schedule_rethink();
}
}
/** Accessor for cached fpd state
*/
static fpstate_t
fpoperation_get_fpstate(const fpoperation_t *self)
{
return self->fpo_fpstate;
}
/** Set cached fpd state
*/
static void
fpoperation_set_fpstate(fpoperation_t *self, fpstate_t state)
{
fpstate_t prev = self->fpo_fpstate;
self->fpo_fpstate = state;
if( prev != self->fpo_fpstate ) {
mce_log(LL_DEBUG, "%s @ %s: fpstate: %s -> %s",
fpoperation_name(self),
fpopstate_repr(fpoperation_state(self)),
fpstate_repr(prev),
fpstate_repr(self->fpo_fpstate));
}
}
/** Cancel timer
*/
static void
fpoperation_cancel_timout(fpoperation_t *self)
{
if( self->fpo_timer ) {
g_source_remove(self->fpo_timer),
self->fpo_timer = 0;
}
}
/** Remove timer id from bookkeeping data
*/
static bool
fpoperation_detach_timout(fpoperation_t *self)
{
bool detached = false;
if( self->fpo_timer ) {
self->fpo_timer = 0;
detached = true;
}
return detached;
}
/** Attach timer id to bookkeeping data
*/
static void
fpoperation_attach_timeout(fpoperation_t *self, int delay, GSourceFunc cb)
{
fpoperation_cancel_timout(self);
self->fpo_timer = mce_wakelocked_timeout_add(delay, cb, self);
}
/** Timer callback for triggering fpwakeup
*/
static gboolean
fpoperation_trigger_fpwakeup_cb(gpointer aptr)
{
fpoperation_t *self = aptr;
if( !fpoperation_detach_timout(self) )
goto EXIT;
fpwakeup_trigger();
fpoperation_trans(self, FPOPSTATE_THROTTLING);
EXIT:
return G_SOURCE_REMOVE;
}
/** Timer callback for exiting FPOPSTATE_THROTTLING state
*/
static gboolean
fpoperation_throttling_ended_cb(gpointer aptr)
{
fpoperation_t *self = aptr;
if( !fpoperation_detach_timout(self) )
goto EXIT;
fpoperation_trans(self, FPOPSTATE_WAITING);
EXIT:
return G_SOURCE_REMOVE;
}
/** Cancel pending async dbus method call
*/
static void
fpoperation_cancel_pending_call(fpoperation_t *self)
{
if( self->fpo_pending ) {
dbus_pending_call_cancel(self->fpo_pending);
dbus_pending_call_unref(self->fpo_pending),
self->fpo_pending = 0;
}
}
/** Detach pending async dbus method call from bookkeeping data
*/
static bool
fpoperation_detach_pending_call(fpoperation_t *self, DBusPendingCall *pc)
{
bool detached = false;
if( pc != 0 && self->fpo_pending == pc ) {
//dbus_pending_call_unref(self->fpo_pending),
self->fpo_pending = 0;
detached = true;
}
return detached;
}
/** Attach pending async dbus method call to bookkeeping data
*/
static void
fpoperation_attach_pending_call(fpoperation_t *self, DBusPendingCall *pc)
{
fpoperation_cancel_pending_call(self);
self->fpo_pending = pc;
}
/** Callback for handling reply to FINGERPRINT1_DBUS_REQ_IDENTIFY calls
*/
static void
fpoperation_identify_reply_cb(DBusPendingCall *pc, void *aptr)
{
fpoperation_t *self = aptr;
DBusMessage *rsp = 0;
DBusError err = DBUS_ERROR_INIT;
dbus_int32_t res = 0;
if( !fpoperation_detach_pending_call(self, pc) )
goto EXIT;
if( !(rsp = dbus_pending_call_steal_reply(pc)) ) {
mce_log(LL_WARN, "no reply");
goto EXIT;
}
if( dbus_set_error_from_message(&err, rsp) ||
!dbus_message_get_args(rsp, &err,
DBUS_TYPE_INT32, &res,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "identify reply: %s", fpreply_repr(res));
switch( res ) {
case FPREPLY_STARTED:
fpoperation_trans(self, FPOPSTATE_PENDING);
break;
default:
fpoperation_trans(self, FPOPSTATE_FAILURE);
break;
}
EXIT:
if( rsp ) dbus_message_unref(rsp);
dbus_error_free(&err);
dbus_pending_call_unref(pc);
return;
}
/** Initiate async FINGERPRINT1_DBUS_REQ_IDENTIFY method call
*/
static void
fpoperation_start_identify(fpoperation_t *self)
{
DBusPendingCall *pc = 0;
dbus_send_ex(FINGERPRINT1_DBUS_SERVICE,
FINGERPRINT1_DBUS_ROOT_OBJECT,
FINGERPRINT1_DBUS_INTERFACE,
FINGERPRINT1_DBUS_REQ_IDENTIFY,
fpoperation_identify_reply_cb,
self, 0,
&pc,
DBUS_TYPE_INVALID);
fpoperation_attach_pending_call(self, pc);
}
/** Callback for handling reply to FINGERPRINT1_DBUS_REQ_ABORT calls
*/
static void
fpoperation_abort_reply_cb(DBusPendingCall *pc, void *aptr)
{
fpoperation_t *self = aptr;
DBusMessage *rsp = 0;
DBusError err = DBUS_ERROR_INIT;
dbus_int32_t res = 0;
if( !fpoperation_detach_pending_call(self, pc) )
goto EXIT;
if( !(rsp = dbus_pending_call_steal_reply(pc)) ) {
mce_log(LL_WARN, "no reply");
goto EXIT;
}
if( dbus_set_error_from_message(&err, rsp) ||
!dbus_message_get_args(rsp, &err,
DBUS_TYPE_INT32, &res,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "abort reply: %s", fpreply_repr(res));
switch( res ) {
case FPREPLY_STARTED:
fpoperation_trans(self, FPOPSTATE_ABORTING);
break;
case FPREPLY_ALREADY_IDLE:
fpoperation_trans(self, FPOPSTATE_ABORTED);
break;
default:
fpoperation_trans(self, FPOPSTATE_FAILURE);
break;
}
EXIT:
if( rsp ) dbus_message_unref(rsp);
dbus_error_free(&err);
dbus_pending_call_unref(pc);
return;
}
/** Initiate async FINGERPRINT1_DBUS_REQ_ABORT method call
*/
static void
fpoperation_start_abort(fpoperation_t *self)
{
DBusPendingCall *pc = 0;
dbus_send_ex(FINGERPRINT1_DBUS_SERVICE,
FINGERPRINT1_DBUS_ROOT_OBJECT,
FINGERPRINT1_DBUS_INTERFACE,
FINGERPRINT1_DBUS_REQ_ABORT,
fpoperation_abort_reply_cb,
self, 0,
&pc,
DBUS_TYPE_INVALID);
fpoperation_attach_pending_call(self, pc);
}
/* ========================================================================= *
* FPIDENTIFY
* ========================================================================= */
/** Identify operation - Hook for entering a state
*/
static void
fpidentify_enter_cb(fpoperation_t *self)
{
switch( fpoperation_state(self) ) {
case FPOPSTATE_INITIALIZE:
break;
case FPOPSTATE_WAITING:
break;
case FPOPSTATE_REQUEST:
fpoperation_start_identify(self);
break;
case FPOPSTATE_PENDING:
fpoperation_set_fpstate(self, FPSTATE_IDENTIFYING);
break;
case FPOPSTATE_SUCCESS:
/* We have identified fingerprint. Delay execution of fp wakeup
* briefly to see if some higher priority event such as power key
* press happens in close proximity, */
if( fpwakeup_set_primed(true) )
mce_log(LL_DEBUG, "fp wakeup primed");
fpoperation_attach_timeout(self, fingerprint_trigger_delay,
fpoperation_trigger_fpwakeup_cb);
break;
case FPOPSTATE_FAILURE:
break;
case FPOPSTATE_ABORT:
fpoperation_start_abort(self);
break;
case FPOPSTATE_ABORTING:
fpoperation_set_fpstate(self, FPSTATE_ABORTING);
break;
case FPOPSTATE_ABORTED:
break;
case FPOPSTATE_THROTTLING:
fpoperation_attach_timeout(self, fingerprint_throttle_delay,
fpoperation_throttling_ended_cb);
break;
default:
break;
}
}
/** Identify operation - Hook for leaving a state
*/
static void
fpidentify_leave_cb(fpoperation_t *self)
{
switch( fpoperation_state(self) ) {
case FPOPSTATE_INITIALIZE:
break;
case FPOPSTATE_WAITING:
break;
case FPOPSTATE_REQUEST:
fpoperation_cancel_pending_call(self);
break;
case FPOPSTATE_PENDING:
break;
case FPOPSTATE_SUCCESS:
break;
case FPOPSTATE_FAILURE:
break;
case FPOPSTATE_ABORT:
break;
case FPOPSTATE_ABORTING:
break;
case FPOPSTATE_ABORTED:
break;
case FPOPSTATE_THROTTLING:
fpoperation_cancel_timout(self);
break;
default:
break;
}
}
/** Identify operation - Hook for evaluating a state
*/
static void
fpidentify_eval_cb(fpoperation_t *self)
{
switch( fpoperation_state(self) ) {
case FPOPSTATE_INITIALIZE:
fpoperation_trans(self, FPOPSTATE_WAITING);
break;
case FPOPSTATE_WAITING:
if( !fpwakeup_is_allowed() )
break;
if( fpstate != FPSTATE_IDLE )
break;
fpoperation_trans(self, FPOPSTATE_REQUEST);
break;
case FPOPSTATE_REQUEST:
break;
case FPOPSTATE_PENDING:
if( !fpwakeup_is_allowed() ) {
fpoperation_trans(self, FPOPSTATE_ABORT);
}
else if( fpoperation_get_fpstate(self) != FPSTATE_IDENTIFYING ) {
fpoperation_trans(self, FPOPSTATE_FAILURE);
}
break;
case FPOPSTATE_ABORT:
break;
case FPOPSTATE_ABORTING:
switch( fpoperation_get_fpstate(self) ) {
case FPSTATE_ABORTING:
break;
case FPSTATE_IDLE:
fpoperation_trans(self, FPOPSTATE_ABORTED);
break;
default:
fpoperation_trans(self, FPOPSTATE_FAILURE);
break;
}
break;
case FPOPSTATE_SUCCESS:
break;
case FPOPSTATE_FAILURE:
case FPOPSTATE_ABORTED:
fpoperation_trans(self, FPOPSTATE_THROTTLING);
break;
case FPOPSTATE_THROTTLING:
break;
default:
break;
}
}
/** Identify operation - Hook for handling result events
*/
static void
fpidentify_result_cb(fpoperation_t *self, fpresult_t event)
{
switch( fpoperation_state(self) ) {
case FPOPSTATE_INITIALIZE:
break;
case FPOPSTATE_WAITING:
break;
case FPOPSTATE_REQUEST:
break;
case FPOPSTATE_PENDING:
switch( event ) {
case FPRESULT_IDENTIFIED:
fpoperation_trans(self, FPOPSTATE_SUCCESS);
break;
case FPRESULT_FAILED:
fpoperation_trans(self, FPOPSTATE_FAILURE);
break;
case FPRESULT_ABORTED:
fpoperation_trans(self, FPOPSTATE_ABORTED);
break;
default:
break;
}
break;
case FPOPSTATE_ABORT:
break;
case FPOPSTATE_ABORTING:
switch( event ) {
case FPRESULT_ABORTED:
fpoperation_trans(self, FPOPSTATE_ABORTED);
break;
default:
break;
}
break;
case FPOPSTATE_SUCCESS:
case FPOPSTATE_FAILURE:
case FPOPSTATE_ABORTED:
case FPOPSTATE_THROTTLING:
break;
default:
break;
}
}
/* ========================================================================= *
* FINGERPRINT_DATA
* ========================================================================= */
/** Hash table for tracking fingerprint template names known to fpd
*/
static GHashTable *fingerprint_data_lut = 0;
/** Allocate fingerprint template names
*
* Gives debug visibility to template names that get added
*/
static gpointer
fingerprint_data_create(const char *name)
{
mce_log(LL_DEBUG, "fingerprint '%s' added", name);
return g_strdup(name);
}
/** Callback for releasing fingerprint template names
*
* Gives debug visibility to template names that get dropped
*/
static void
fingerprint_data_detete_cb(gpointer aptr)
{
mce_log(LL_DEBUG, "fingerprint '%s' removed", (char *)aptr);
g_free(aptr);
}
/** Flush all cached fingerprint template names
*/
static void
fingerprint_data_flush(void)
{
if( !fingerprint_data_lut )
goto EXIT;
if( g_hash_table_size(fingerprint_data_lut) > 0 ) {
g_hash_table_remove_all(fingerprint_data_lut);
fpwakeup_schedule_rethink();
}
EXIT:
return;
}
/** Remove a fingerprint template name from cache
*/
static void
fingerprint_data_remove(const char *name)
{
if( !fingerprint_data_lut )
goto EXIT;
if( g_hash_table_remove(fingerprint_data_lut, name) )
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Add fingerprint template name to cache
*/
static void
fingerprint_data_add(const char *name)
{
if( !fingerprint_data_lut )
goto EXIT;
if( g_hash_table_lookup(fingerprint_data_lut, name))
goto EXIT;
g_hash_table_insert(fingerprint_data_lut,
fingerprint_data_create(name),
GINT_TO_POINTER(1));
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Predicate for: There are registered fingerprints
*/
static bool
fingerprint_data_exists(void)
{
guint count = 0;
if( !fingerprint_data_lut )
goto EXIT;
count = g_hash_table_size(fingerprint_data_lut);
EXIT:
return count > 0;
}
/** Initialize fingerprint template name cache
*/
static void
fingerprint_data_init(void)
{
if( !fingerprint_data_lut ) {
mce_log(LL_DEBUG, "fingerprint data init");
fingerprint_data_lut = g_hash_table_new_full(g_str_hash, g_str_equal,
fingerprint_data_detete_cb, 0);
}
}
/** Cleanup fingerprint template name cache
*/
static void
fingerprint_data_quit(void)
{
if( fingerprint_data_lut ) {
mce_log(LL_DEBUG, "fingerprint data cleanup");
g_hash_table_unref(fingerprint_data_lut),
fingerprint_data_lut = 0;
}
}
/* ========================================================================= *
* FINGERPRINT_LED_SCANNING
* ========================================================================= */
/** Control led pattern for indicating fingerprint scanner status
*
* @param activate true to activate led, false to deactivate
*/
static void
fingerprint_led_scanning_activate(bool activate)
{
static bool activated = false;
if( activated != activate ) {
datapipe_exec_full((activated = activate) ?
&led_pattern_activate_pipe :
&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_SCANNING_FINGERPRINT);
}
}
/* ========================================================================= *
* FINGERPRINT_LED_ACQUIRED
* ========================================================================= */
/** Control led pattern for indicating fingerprint acquisition events
*
* @param activate true to activate led, false to deactivate
*/
static void
fingerprint_led_acquired_activate(bool activate)
{
static bool activated = false;
if( activated != activate ) {
datapipe_exec_full((activated = activate) ?
&led_pattern_activate_pipe :
&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_FINGERPRINT_ACQUIRED);
}
}
/** Timer id for: Stop fingerprint acquisition event led */
static guint fingerprint_led_acquired_timer_id = 0;
/** Timer callback for: Stop fingerprint acquisition event led */
static gboolean
fingerprint_led_acquired_timer_cb(gpointer aptr)
{
(void)aptr;
fingerprint_led_acquired_timer_id = 0;
fingerprint_led_acquired_activate(false);
return FALSE;
}
/** Briefly activate fingerprint acquisition event led
*/
static void
fingerprint_led_acquired_trigger(void)
{
if( fingerprint_led_acquired_timer_id )
g_source_remove(fingerprint_led_acquired_timer_id);
fingerprint_led_acquired_timer_id =
mce_wakelocked_timeout_add(200, fingerprint_led_acquired_timer_cb, 0);
fingerprint_led_acquired_activate(true);
}
/** Dctivate fingerprint acquisition event led
*/
static void
fingerprint_led_acquired_cancel(void)
{
if( fingerprint_led_acquired_timer_id ) {
g_source_remove(fingerprint_led_acquired_timer_id),
fingerprint_led_acquired_timer_id = 0;
}
fingerprint_led_acquired_activate(false);
}
/* ========================================================================= *
* FINGERPRINT_DATAPIPE
* ========================================================================= */
/** Update fpstate_pipe content
*
* @param state fingerprint operation state reported by fpd
*/
static void
fingerprint_datapipe_set_fpstate(fpstate_t state)
{
fpstate_t prev = fpstate;
fpstate = state;
if( fpstate == prev )
goto EXIT;
mce_log(LL_NOTICE, "fpstate: %s -> %s",
fpstate_repr(prev),
fpstate_repr(fpstate));
datapipe_exec_full(&fpstate_pipe, GINT_TO_POINTER(fpstate));
switch( fpstate ) {
case FPSTATE_ENROLLING:
case FPSTATE_IDENTIFYING:
case FPSTATE_VERIFYING:
fingerprint_led_scanning_activate(true);
break;
default:
fingerprint_led_scanning_activate(false);
break;
}
fingerprint_datapipe_update_enroll_in_progress();
fpwakeup_propagate_fpstate();
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Evaluate value for enroll_in_progress_pipe
*
* Enrolling a fingerprint needs to block display blanking.
*
* To avoid hiccups / false negatives we try to be relatively
* sure that system state is such that settings ui at least
* in theory can be handing enroll operation on screen.
*
* Require that:
* - fingerprint daemon is in enrolling state
* - display is already on
* - lockscreen is not active
* - device is unlocked
* - we are in user mode
*
* @return true if fp enroll is in progress, false otherwise
*/
static bool
fingerprint_datapipe_evaluate_enroll_in_progress(void)
{
bool in_progress = false;
if( fpstate != FPSTATE_ENROLLING )
goto EXIT;
if( display_state_next != MCE_DISPLAY_ON &&
display_state_next != MCE_DISPLAY_DIM )
goto EXIT;
if( submode & MCE_SUBMODE_TKLOCK )
goto EXIT;
if( devicelock_state != DEVICELOCK_STATE_UNLOCKED )
goto EXIT;
if( system_state != MCE_SYSTEM_STATE_USER )
goto EXIT;
in_progress = true;
EXIT:
return in_progress;
}
/** Update enroll_in_progress_pipe content
*/
static void
fingerprint_datapipe_update_enroll_in_progress(void)
{
bool prev = enroll_in_progress;
enroll_in_progress = fingerprint_datapipe_evaluate_enroll_in_progress();
if( enroll_in_progress == prev )
goto EXIT;
mce_log(LL_NOTICE, "enroll_in_progress: %s -> %s",
prev ? "true" : "false",
enroll_in_progress ? "true" : "false");
datapipe_exec_full(&enroll_in_progress_pipe,
GINT_TO_POINTER(enroll_in_progress));
EXIT:
return;
}
/** Generate user activity to reset blanking timers
*/
static void
fingerprint_datapipe_generate_activity(void)
{
/* Display must be in powered on state */
switch( display_state_next ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
break;
default:
goto EXIT;
}
mce_log(LL_DEBUG, "generating activity from fingerprint sensor");
mce_datapipe_generate_activity();
EXIT:
return;
}
/** Notification callback for fpd_service_state_pipe
*
* @param data service_state_t value as void pointer
*/
static void
fingerprint_datapipe_fpd_service_state_cb(gconstpointer data)
{
service_state_t prev = fpd_service_state;
fpd_service_state = GPOINTER_TO_INT(data);
if( fpd_service_state == prev )
goto EXIT;
mce_log(LL_NOTICE, "fpd_service_state = %s -> %s",
service_state_repr(prev),
service_state_repr(fpd_service_state));
if( fpd_service_state == SERVICE_STATE_RUNNING ) {
fingerprint_dbus_fpstate_query_start();
fingerprint_dbus_fpdata_query_start();
}
else {
fingerprint_dbus_fpdata_query_cancel();
fingerprint_dbus_fpstate_query_cancel();
fingerprint_datapipe_set_fpstate(FPSTATE_UNSET);
fingerprint_data_flush();
}
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Notification callback for system_state_pipe
*
* @param data system_state_t value as void pointer
*/
static void
fingerprint_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));
fingerprint_datapipe_update_enroll_in_progress();
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Notification callback for devicelock_state_pipe
*
* @param data devicelock_state_t value as void pointer
*/
static void
fingerprint_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));
fingerprint_datapipe_update_enroll_in_progress();
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Notification callback for submode_pipe
*
* @param data submode_t value as void pointer
*/
static void
fingerprint_datapipe_submode_cb(gconstpointer data)
{
submode_t prev = submode;
submode = GPOINTER_TO_INT(data);
if( submode == prev )
goto EXIT;
mce_log(LL_DEBUG, "submode = %s", submode_change_repr(prev, submode));
fingerprint_datapipe_update_enroll_in_progress();
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Notification callback for display_state_next_pipe
*
* @param data display_state_t value as void pointer
*/
static void
fingerprint_datapipe_display_state_next_cb(gconstpointer data)
{
display_state_t prev = display_state_next;
display_state_next = GPOINTER_TO_INT(data);
if( display_state_next == prev )
goto EXIT;
mce_log(LL_DEBUG, "display_state_next = %s -> %s",
display_state_repr(prev),
display_state_repr(display_state_next));
fingerprint_datapipe_update_enroll_in_progress();
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Change notifications for interaction_expected_pipe
*/
static void
fingerprint_datapipe_interaction_expected_cb(gconstpointer data)
{
bool prev = interaction_expected;
interaction_expected = GPOINTER_TO_INT(data);
if( prev == interaction_expected )
goto EXIT;
mce_log(LL_DEBUG, "interaction_expected: %d -> %d",
prev, interaction_expected);
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Change notifications for topmost_window_pid_pipe
*/
static void
fingerprint_datapipe_topmost_window_pid_cb(gconstpointer data)
{
int prev = topmost_window_pid;
topmost_window_pid = GPOINTER_TO_INT(data);
if( prev == topmost_window_pid )
goto EXIT;
mce_log(LL_DEBUG, "topmost_window_pid: %d -> %d",
prev, topmost_window_pid);
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Change notifications for proximity_sensor_actual
*/
static void
fingerprint_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));
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Change notifications from lid_sensor_filtered_pipe
*/
static void
fingerprint_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));
fpwakeup_schedule_rethink();
EXIT:
return;
}
/** Datapipe trigger for power key events
*
* @param data A pointer to the input_event struct
*/
static void
fingerprint_datapipe_keypress_event_cb(gconstpointer const data)
{
const struct input_event * const *evp;
const struct input_event *ev;
if( !(evp = data) )
goto EXIT;
if( !(ev = *evp) )
goto EXIT;
/* For example in Sony Xperia X fingerprint scanner is located
* on the power key. This creates interesting situations as
* we can also get fingerprint identification while user intents
* to just press the power key...
*/
if( ev->type == EV_KEY && ev->code == KEY_POWER ) {
/* Unprime on power key event of any kind. This effectively
* cancels fingerprint wakeup that has been detected just
* before power key press / release.
*/
if( fpwakeup_set_primed(false) )
mce_log(LL_WARN, "powerkey event; fp wakeup unprimed");
/* Denying fpwakeups via policy when power key is pressed
* down should inhibit fingerprint wakeups in those cases
* where we see the powerkey press before getting fingerprint
* identified.
*/
bool pressed = (ev->value != 0);
if( powerkey_pressed != pressed ) {
mce_log(LL_DEBUG, "powerkey_pressed: %d -> %d",
powerkey_pressed, pressed);
powerkey_pressed = pressed;
fpwakeup_schedule_rethink();
}
}
EXIT:
return;
}
/** Array of datapipe handlers */
static datapipe_handler_t fingerprint_datapipe_handlers[] =
{
// input triggers
{
.datapipe = &keypress_event_pipe,
.input_cb = fingerprint_datapipe_keypress_event_cb,
},
// output triggers
{
.datapipe = &fpd_service_state_pipe,
.output_cb = fingerprint_datapipe_fpd_service_state_cb,
},
{
.datapipe = &system_state_pipe,
.output_cb = fingerprint_datapipe_system_state_cb,
},
{
.datapipe = &devicelock_state_pipe,
.output_cb = fingerprint_datapipe_devicelock_state_cb,
},
{
.datapipe = &submode_pipe,
.output_cb = fingerprint_datapipe_submode_cb,
},
{
.datapipe = &display_state_next_pipe,
.output_cb = fingerprint_datapipe_display_state_next_cb,
},
{
.datapipe = &interaction_expected_pipe,
.output_cb = fingerprint_datapipe_interaction_expected_cb,
},
{
.datapipe = &topmost_window_pid_pipe,
.output_cb = fingerprint_datapipe_topmost_window_pid_cb,
},
{
.datapipe = &proximity_sensor_actual_pipe,
.output_cb = fingerprint_datapipe_proximity_sensor_actual_cb,
},
{
.datapipe = &lid_sensor_filtered_pipe,
.output_cb = fingerprint_datapipe_lid_sensor_filtered_cb,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t fingerprint_datapipe_bindings =
{
.module = "fingerprint",
.handlers = fingerprint_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void
fingerprint_datapipe_init(void)
{
// triggers
mce_datapipe_init_bindings(&fingerprint_datapipe_bindings);
}
/** Remove triggers/filters from datapipes */
static void
fingerprint_datapipe_quit(void)
{
// triggers
mce_datapipe_quit_bindings(&fingerprint_datapipe_bindings);
}
/* ========================================================================= *
* FINGERPRINT_SETTINGS
* ========================================================================= */
/** Setting changed callback
*
* @param gcc Unused
* @param id Connection ID from gconf_client_notify_add()
* @param entry The modified GConf entry
* @param data Unused
*/
static void
fingerprint_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 == fingerprint_wakeup_mode_setting_id ) {
gint old = fingerprint_wakeup_mode;
fingerprint_wakeup_mode = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "fingerprint_wakeup_mode: %d -> %d",
old, fingerprint_wakeup_mode);
fpwakeup_schedule_rethink();
}
else if( id == fingerprint_trigger_delay_setting_id ) {
gint old = fingerprint_trigger_delay;
fingerprint_trigger_delay = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "fingerprint_trigger_delay: %d -> %d",
old, fingerprint_trigger_delay);
/* Takes effect on the next identify */
}
else if( id == fingerprint_throttle_delay_setting_id ) {
gint old = fingerprint_throttle_delay;
fingerprint_throttle_delay = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "fingerprint_throttle_delay: %d -> %d",
old, fingerprint_throttle_delay);
/* Takes effect after the next ipc attempt */
}
else if( id == fingerprint_allow_delay_setting_id ) {
gint old = fingerprint_allow_delay;
fingerprint_allow_delay = gconf_value_get_int(gcv);
mce_log(LL_NOTICE, "fingerprint_allow_delay: %d -> %d",
old, fingerprint_allow_delay);
/* Takes effect on the next policy change */
}
else {
mce_log(LL_WARN, "Spurious GConf value received; confused!");
}
EXIT:
return;
}
/** Get intial setting values and start tracking changes
*/
static void
fingerprint_setting_init(void)
{
mce_setting_track_int(MCE_SETTING_FPWAKEUP_MODE,
&fingerprint_wakeup_mode,
MCE_DEFAULT_FPWAKEUP_MODE,
fingerprint_setting_cb,
&fingerprint_wakeup_mode_setting_id);
mce_setting_track_int(MCE_SETTING_FPWAKEUP_ALLOW_DELAY,
&fingerprint_allow_delay,
MCE_DEFAULT_FPWAKEUP_ALLOW_DELAY,
fingerprint_setting_cb,
&fingerprint_allow_delay_setting_id);
mce_setting_track_int(MCE_SETTING_FPWAKEUP_TRIGGER_DELAY,
&fingerprint_trigger_delay,
MCE_DEFAULT_FPWAKEUP_TRIGGER_DELAY,
fingerprint_setting_cb,
&fingerprint_trigger_delay_setting_id);
mce_setting_track_int(MCE_SETTING_FPWAKEUP_THROTTLE_DELAY,
&fingerprint_throttle_delay,
MCE_DEFAULT_FPWAKEUP_THROTTLE_DELAY,
fingerprint_setting_cb,
&fingerprint_throttle_delay_setting_id);
}
/** Stop tracking setting changes
*/
static void
fingerprint_setting_quit(void)
{
mce_setting_notifier_remove(fingerprint_wakeup_mode_setting_id),
fingerprint_wakeup_mode_setting_id = 0;
mce_setting_notifier_remove(fingerprint_allow_delay_setting_id),
fingerprint_allow_delay_setting_id = 0;
mce_setting_notifier_remove(fingerprint_trigger_delay_setting_id),
fingerprint_trigger_delay_setting_id = 0;
mce_setting_notifier_remove(fingerprint_throttle_delay_setting_id),
fingerprint_throttle_delay_setting_id = 0;
}
/* ========================================================================= *
* FINGERPRINT_DBUS
* ========================================================================= */
/** Handle fpd operation state change signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpstate_changed_cb(DBusMessage *const msg)
{
const char *state = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &state,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
fingerprint_datapipe_set_fpstate(fpstate_parse(state));
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle fpd acquisition info signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpacquired_info_cb(DBusMessage *const msg)
{
const char *info = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &info,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "fpacquired: %s", info);
/* Fingerprint aquisition info notifications during
* enroll, identify and verify operations must delay
* display blanking.
*/
switch( fpstate ) {
case FPSTATE_ENROLLING:
case FPSTATE_IDENTIFYING:
case FPSTATE_VERIFYING:
fingerprint_datapipe_generate_activity();
break;
default:
break;
}
fingerprint_led_acquired_trigger();
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle fpd fingerprint added signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpadded_cb(DBusMessage *const msg)
{
const char *name = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "fpadded: %s", name);
fingerprint_data_add(name);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle fpd fingerprint removed signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpremoved_cb(DBusMessage *const msg)
{
const char *name = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "fpremoved: %s", name);
fingerprint_data_remove(name);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle fpd fingerprint identify succeeded signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpidentified_cb(DBusMessage *const msg)
{
const char *name = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "fpidentified: %s", name);
fpwakeup_propagate_fpresult(FPRESULT_IDENTIFIED);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle fpd fingerprint operation aborted signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpaborted_cb(DBusMessage *const msg)
{
(void)msg;
mce_log(LL_DEBUG, "fpaborted");
fpwakeup_propagate_fpresult(FPRESULT_ABORTED);
return TRUE;
}
/** Handle fpd fingerprint operation failed signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpfailed_cb(DBusMessage *const msg)
{
(void)msg;
mce_log(LL_DEBUG, "fpfailed");
fpwakeup_propagate_fpresult(FPRESULT_FAILED);
return TRUE;
}
/** Handle fpd fingerprint verify operation succeeded signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpverified_cb(DBusMessage *const msg)
{
(void)msg;
mce_log(LL_DEBUG, "fpverified");
fpwakeup_propagate_fpresult(FPRESULT_VERIFIED);
return TRUE;
}
/** Handle fpd fingerprint acquisition error signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fperror_cb(DBusMessage *const msg)
{
const char *name = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_STRING, &name,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "fperror: %s", name);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Handle fpd fingerprint enroll progress signals
*
* @param msg The D-Bus message
*
* @return TRUE
*/
static gboolean
fingerprint_dbus_fpprogress_cb(DBusMessage *const msg)
{
dbus_int32_t percent = 0;
DBusError err = DBUS_ERROR_INIT;
if( !dbus_message_get_args(msg, &err,
DBUS_TYPE_INT32, &percent,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "parse error: %s: %s", err.name, err.message);
goto EXIT;
}
mce_log(LL_DEBUG, "fpprogress: %d%%", percent);
EXIT:
dbus_error_free(&err);
return TRUE;
}
/** Array of dbus message handlers */
static mce_dbus_handler_t fingerprint_dbus_handlers[] =
{
/* signals */
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_STATE_CHANGED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpstate_changed_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_ACQUISITION_INFO,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpacquired_info_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_ERROR_INFO,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fperror_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_ADDED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpadded_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_REMOVED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpremoved_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_IDENTIFIED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpidentified_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_ABORTED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpaborted_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_FAILED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpfailed_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_VERIFIED,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpverified_cb,
},
{
.interface = FINGERPRINT1_DBUS_INTERFACE,
.name = FINGERPRINT1_DBUS_SIG_ENROLL_PROGRESS,
.type = DBUS_MESSAGE_TYPE_SIGNAL,
.callback = fingerprint_dbus_fpprogress_cb,
},
/* sentinel */
{
.interface = 0
}
};
/** Install dbus message handlers
*/
static void
fingerprint_dbus_init(void)
{
mce_dbus_handler_register_array(fingerprint_dbus_handlers);
}
/** Remove dbus message handlers
*/
static void
fingerprint_dbus_quit(void)
{
mce_dbus_handler_unregister_array(fingerprint_dbus_handlers);
}
/* ------------------------------------------------------------------------- *
* FINGERPRINT1_DBUS_REQ_GET_STATE
* ------------------------------------------------------------------------- */
static DBusPendingCall *fingerprint_dbus_fpstate_query_pc = 0;
/** Handle reply to async fpstate query
*
* @param pc pending call handle
* @param aptr (unused) user data pointer
*/
static void
fingerprint_dbus_fpstate_query_cb(DBusPendingCall *pc, void *aptr)
{
(void)aptr;
DBusMessage *rsp = 0;
DBusError err = DBUS_ERROR_INIT;
const char *state = 0;
if( pc != fingerprint_dbus_fpstate_query_pc )
goto EXIT;
fingerprint_dbus_fpstate_query_pc = 0;
if( !(rsp = dbus_pending_call_steal_reply(pc)) ) {
mce_log(LL_WARN, "no reply");
goto EXIT;
}
if( dbus_set_error_from_message(&err, rsp) ||
!dbus_message_get_args(rsp, &err,
DBUS_TYPE_STRING, &state,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "error: %s: %s", err.name, err.message);
goto EXIT;
}
fingerprint_datapipe_set_fpstate(fpstate_parse(state));
EXIT:
if( rsp ) dbus_message_unref(rsp);
dbus_error_free(&err);
dbus_pending_call_unref(pc);
return;
}
/** Cancel pending async fpstate query
*/
static void
fingerprint_dbus_fpstate_query_cancel(void)
{
if( fingerprint_dbus_fpstate_query_pc ) {
dbus_pending_call_cancel(fingerprint_dbus_fpstate_query_pc);
dbus_pending_call_unref(fingerprint_dbus_fpstate_query_pc);
fingerprint_dbus_fpstate_query_pc = 0;
}
}
/** Initiate async query to find out current fpstate
*/
static void
fingerprint_dbus_fpstate_query_start(void)
{
fingerprint_dbus_fpstate_query_cancel();
dbus_send_ex(FINGERPRINT1_DBUS_SERVICE,
FINGERPRINT1_DBUS_ROOT_OBJECT,
FINGERPRINT1_DBUS_INTERFACE,
FINGERPRINT1_DBUS_REQ_GET_STATE,
fingerprint_dbus_fpstate_query_cb, 0, 0,
&fingerprint_dbus_fpstate_query_pc,
DBUS_TYPE_INVALID);
}
/* ------------------------------------------------------------------------- *
* FINGERPRINT1_DBUS_REQ_GET_ALL
* ------------------------------------------------------------------------- */
static DBusPendingCall *fingerprint_dbus_fpdata_query_pc = 0;
/** Handle reply to async fpdata query
*
* @param pc pending call handle
* @param aptr (unused) user data pointer
*/
static void
fingerprint_dbus_fpdata_query_cb(DBusPendingCall *pc, void *aptr)
{
(void)aptr;
DBusMessage *rsp = 0;
DBusError err = DBUS_ERROR_INIT;
char **arr = 0;
int len = 0;
if( pc != fingerprint_dbus_fpdata_query_pc )
goto EXIT;
fingerprint_dbus_fpdata_query_pc = 0;
if( !(rsp = dbus_pending_call_steal_reply(pc)) ) {
mce_log(LL_WARN, "no reply");
goto EXIT;
}
if( dbus_set_error_from_message(&err, rsp) ||
!dbus_message_get_args(rsp, &err,
DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, &arr, &len,
DBUS_TYPE_INVALID) ) {
mce_log(LL_WARN, "error: %s: %s", err.name, err.message);
goto EXIT;
}
for( int i = 0; i < len; ++i )
fingerprint_data_add(arr[i]);
EXIT:
if( rsp ) dbus_message_unref(rsp);
dbus_error_free(&err);
dbus_pending_call_unref(pc);
return;
}
/** Cancel pending async fpdata query
*/
static void
fingerprint_dbus_fpdata_query_cancel(void)
{
if( fingerprint_dbus_fpdata_query_pc ) {
dbus_pending_call_cancel(fingerprint_dbus_fpdata_query_pc);
dbus_pending_call_unref(fingerprint_dbus_fpdata_query_pc);
fingerprint_dbus_fpdata_query_pc = 0;
}
}
/** Initiate async query to find out current fpdata
*/
static void
fingerprint_dbus_fpdata_query_start(void)
{
fingerprint_dbus_fpdata_query_cancel();
dbus_send_ex(FINGERPRINT1_DBUS_SERVICE,
FINGERPRINT1_DBUS_ROOT_OBJECT,
FINGERPRINT1_DBUS_INTERFACE,
FINGERPRINT1_DBUS_REQ_GET_ALL,
fingerprint_dbus_fpdata_query_cb, 0, 0,
&fingerprint_dbus_fpdata_query_pc,
DBUS_TYPE_INVALID);
}
/* ========================================================================= *
* FPWAKEUP
* ========================================================================= */
/** Policy state: Using fpd for fingerprint wakeups is allowed
*/
static bool fpwakeup_allowed_state = false;
/** Predicate for: Using fpd for fingerprint wakeups is allowed
*/
static bool
fpwakeup_is_allowed(void)
{
return fpwakeup_allowed_state;
}
/** Allow/deny using fpd for fingerprint wakeups
*/
static void
fpwakeup_set_allowed(bool allowed)
{
fpwakeup_cancel_allow();
if( fpwakeup_allowed_state != allowed ) {
fpwakeup_allowed_state = allowed;
mce_log(LL_DEBUG, "fingerprint_wakeup = %s",
fpwakeup_allowed_state ? "allowed" : "denied");
fpwakeup_schedule_rethink();
}
}
/** Timer for: Adding hysteresis to allowing fingerprint wakeups
*/
static guint fpwakeup_allow_id = 0;
/** Timer callback for allowing fingerprint wakeups
*/
static gboolean
fpwakeup_allow_cb(gpointer aptr)
{
(void)aptr;
if( !fpwakeup_allow_id )
goto EXIT;
fpwakeup_allow_id = 0;
fpwakeup_set_allowed(true);
EXIT:
return G_SOURCE_REMOVE;
}
/** Cancel delayed fingerprint wakeup allowing
*/
static void
fpwakeup_cancel_allow(void)
{
if( fpwakeup_allow_id ) {
g_source_remove(fpwakeup_allow_id),
fpwakeup_allow_id = 0;
}
}
/** Allow fingerprint wakeups after slight delay
*
* The purpose is to add hysteresis to denied -> allowed policy
* decisions, so that short living state transitions do not cause
* unwanted fpd ipc activity.
*/
static void
fpwakeup_schedule_allow(void)
{
if( !fpwakeup_allow_id ) {
fpwakeup_allow_id = mce_wakelocked_timeout_add(fingerprint_allow_delay,
fpwakeup_allow_cb, 0);
}
}
/** Evaluate whether system state allows fingerprint wakeups
*/
static bool
fpwakeup_evaluate_allowed(void)
{
bool allowed = false;
/* Must be running in USER mode */
if( system_state != MCE_SYSTEM_STATE_USER )
goto EXIT;
/* Fingerprint daemon must be running */
if( fpd_service_state != SERVICE_STATE_RUNNING )
goto EXIT;
/* Need to have fingerprints registered */
if( !fingerprint_data_exists() )
goto EXIT;
/* Check fpwakeup policy */
switch( fingerprint_wakeup_mode ) {
default:
case FPWAKEUP_ENABLE_NEVER:
goto EXIT;
case FPWAKEUP_ENABLE_ALWAYS:
/* Lid must not be closed */
if( lid_sensor_filtered == COVER_CLOSED )
goto EXIT;
/* Proximity sensor state: don't care */
break;
case FPWAKEUP_ENABLE_NO_PROXIMITY:
/* Lid must not be closed */
if( lid_sensor_filtered == COVER_CLOSED )
goto EXIT;
/* Proximity sensor must not be covered or unknown */
if( proximity_sensor_actual != COVER_OPEN )
goto EXIT;
break;
}
/* Power key must not be pressed down */
if( powerkey_pressed )
goto EXIT;
switch( display_state_next ) {
case MCE_DISPLAY_OFF:
case MCE_DISPLAY_LPM_OFF:
/* Devicelock ui disables auth in truly powered
* off display states -> mce can step in*/
break;
case MCE_DISPLAY_LPM_ON:
/* Devicelock ui handles unlocking in lpm */
if( devicelock_state != DEVICELOCK_STATE_UNLOCKED )
goto EXIT;
break;
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
/* Devicelock ui handles unlocking on/dimmed */
if( devicelock_state != DEVICELOCK_STATE_UNLOCKED )
goto EXIT;
/* Nothing to do if lockscreen is deactivated */
if( !(submode & MCE_SUBMODE_TKLOCK) )
goto EXIT;
/* Nothing to do when interacting with lockscreen */
if( interaction_expected )
goto EXIT;
/* Nothing to do when some app is on top of lockscreen */
if( topmost_window_pid != -1 )
goto EXIT;
break;
default:
goto EXIT;
}
/* MCE can use fingerprint scanner as kind of power key */
allowed = true;
EXIT:
return allowed;
}
/** Update fingerprint wakeups allowed policy state
*/
static void
fpwakeup_update_allowed(void)
{
bool allowed = fpwakeup_evaluate_allowed();
if( !allowed ) {
fpwakeup_set_allowed(false);
}
else if( !fpwakeup_allowed_state ) {
fpwakeup_schedule_allow();
}
}
/** Re-evaluate everything related to fingerprint wakeups
*/
static void
fpwakeup_rethink_now(void)
{
fpwakeup_cancel_rethink();
fpwakeup_update_allowed();
fpwakeup_propagate_eval();
return;
}
/** Idle timer for: Re-evaluating fingerprint wakeup
*/
static guint fpwakeup_rethink_id = 0;
/** Idle callback for: Re-evaluating fingerprint wakeup
*/
static gboolean
fpwakeup_rethink_cb(gpointer aptr)
{
(void)aptr;
if( !fpwakeup_rethink_id )
goto EXIT;
fpwakeup_rethink_id = 0;
fpwakeup_rethink_now();
EXIT:
return G_SOURCE_REMOVE;
}
/** Schedule re-evaluation of fingerprint wakeup policy and state
*
* Fingerprint wakeup policy depends on multitude of state variables
* and datapipes. Some of these are interconnected, and can cause
* a flurry of triggers. To avoid excessive re-evaluation, an idle
* callback is used to compress N triggers into just one evaluation.
*/
static void
fpwakeup_schedule_rethink(void)
{
if( !fpwakeup_rethink_id ) {
fpwakeup_rethink_id = mce_wakelocked_idle_add(fpwakeup_rethink_cb, 0);
}
}
/** Cancel re-evaluation of fingerprint wakeup policy and state
*/
static void
fpwakeup_cancel_rethink(void)
{
if( fpwakeup_rethink_id ) {
g_source_remove(fpwakeup_rethink_id),
fpwakeup_rethink_id = 0;
}
}
/** Propagate fingerprint daemon state changes to operation state machines
*/
static void
fpwakeup_propagate_fpstate(void)
{
for( size_t i = 0; i < G_N_ELEMENTS(fpoperation_lut); ++i )
fpoperation_set_fpstate(&fpoperation_lut[i], fpstate);
}
/** Propagate fingerprint daemon result events to operation state machines
*/
static void
fpwakeup_propagate_fpresult(fpresult_t event)
{
for( size_t i = 0; i < G_N_ELEMENTS(fpoperation_lut); ++i )
fpoperation_result(&fpoperation_lut[i], event);
}
/** Propagate state re-evaluation to operation state machines
*/
static void
fpwakeup_propagate_eval(void)
{
for( size_t i = 0; i < G_N_ELEMENTS(fpoperation_lut); ++i )
fpoperation_eval(&fpoperation_lut[i]);
}
static bool fpwakeup_primed = false;
static bool
fpwakeup_set_primed(bool prime)
{
bool changed = false;
if( fpwakeup_primed != prime ) {
fpwakeup_primed = prime;
changed = true;
}
return changed;
}
/** Execute display wakeup
*/
static void
fpwakeup_trigger(void)
{
if( !fpwakeup_set_primed(false) ) {
/* Other overlapping inputs, such as power key press,
* have taken priority over fingerprint wakeup.
*/
mce_log(LL_WARN, "fingerprint wakeup; explicitly ignored");
}
else if( !fpwakeup_is_allowed() ) {
/* Policy state changed somewhere in between requesting
* fingerprint identification and getting the result.
*/
mce_log(LL_WARN, "fingerprint wakeup; ignored due to policy");
}
else {
mce_log(LL_CRUCIAL, "fingerprint wakeup triggered");
/* (Mis)use haptic feedback associated with device unlocking */
datapipe_exec_full(&ngfd_event_request_pipe,
"unlock_device");
/* Deactivate type=6 led patterns (e.g. sms/email notifications)
* by signaling "true user activity" via synthetized gesture
* input event. (The event type ought not matter, but using
* double tap event is somewhat logical and does not cause side
* effects in the few places where the event type is actually
* checked.)
*/
const struct input_event ev = {
.type = EV_MSC,
.code = MSC_GESTURE,
.value = GESTURE_FPWAKEUP | GESTURE_SYNTHESIZED,
};
datapipe_exec_full(&user_activity_event_pipe, &ev);
/* Forward to powerkey.c for configurable action handling */
const struct input_event *evp = &ev;
datapipe_exec_full(&keypress_event_pipe, &evp);
}
}
/* ========================================================================= *
* G_MODULE
* ========================================================================= */
/** Init function for the fpd tracking module
*
* @param module (unused) module handle
*
* @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;
fingerprint_data_init();
fingerprint_setting_init();
fingerprint_datapipe_init();
fingerprint_dbus_init();
return NULL;
}
/** Exit function for the fpd tracking module
*
* @param module (unused) module handle
*/
G_MODULE_EXPORT void
g_module_unload(GModule *module)
{
(void)module;
fingerprint_data_quit();
fingerprint_setting_quit();
fingerprint_dbus_quit();
fingerprint_datapipe_quit();
fingerprint_dbus_fpstate_query_cancel();
fingerprint_dbus_fpdata_query_cancel();
fpwakeup_cancel_rethink();
fpwakeup_cancel_allow();
fingerprint_led_scanning_activate(false);
fingerprint_led_acquired_cancel();
return;
}