Skip to content
Permalink
master
Switch branches/tags
Go to file
 
 
Cannot retrieve contributors at this time
/**
* @file tklock.c
* This file implements the touchscreen/keypad lock component
* of the Mode Control Entity
* <p>
* Copyright © 2004-2011 Nokia Corporation and/or its subsidiary(-ies).
* Copyright (C) 2012-2019 Jolla Ltd.
* <p>
* @author David Weinehall <david.weinehall@nokia.com>
* @author Tapio Rantala <ext-tapio.rantala@nokia.com>
* @author Santtu Lakkala <ext-santtu.1.lakkala@nokia.com>
* @author Jukka Turunen <ext-jukka.t.turunen@nokia.com>
* @author Irina Bezruk <ext-irina.bezruk@nokia.com>
* @author Kalle Jokiniemi <kalle.jokiniemi@jolla.com>
* @author Mika Laitio <lamikr@pilppa.org>
* @author Markus Lehtonen <markus.lehtonen@iki.fi>
* @author Simo Piiroinen <simo.piiroinen@jollamobile.com>
* @author Vesa Halttunen <vesa.halttunen@jollamobile.com>
* @author Andrew den Exter <andrew.den.exter@jolla.com>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include "tklock.h"
#include "mce-common.h"
#include "mce-log.h"
#include "mce-lib.h"
#include "mce-io.h"
#include "mce-setting.h"
#include "mce-dbus.h"
#include "mce-hbtimer.h"
#include "evdev.h"
#ifdef ENABLE_WAKELOCKS
# include "libwakelock.h"
#endif
#include "modules/doubletap.h"
#include "modules/display.h"
#include "systemui/dbus-names.h"
#include "systemui/tklock-dbus-names.h"
#include <linux/input.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <mce/dbus-names.h>
#include <mce/mode-names.h>
#include <glib/gstdio.h>
typedef enum
{
/** No autorelock triggers */
AUTORELOCK_NO_TRIGGERS,
/** Autorelock on keyboard slide closed */
AUTORELOCK_KBD_SLIDE,
/** Autorelock on lens cover */
AUTORELOCK_LENS_COVER,
} autorelock_t;
/** Helper for evaluation number of items in an array */
#define numof(a) (sizeof(a)/sizeof*(a))
/* ========================================================================= *
* CONSTANTS
* ========================================================================= */
#define MODULE_NAME "tklock"
/** Max valid time_t value in milliseconds */
#define MAX_TICK (INT_MAX * (int64_t)1000)
/** Min valid time_t value in milliseconds */
#define MIN_TICK 0
/** Maximum number of concurrent notification ui exceptions */
#define TKLOCK_NOTIF_SLOTS 32
/** How long to wait for lid close after low lux [ms] */
#define TKLOCK_LIDFILTER_SET_WAIT_FOR_CLOSE_DELAY 1500
/** How long to wait for low lux after lid close [ms] */
#define TKLOCK_LIDFILTER_SET_WAIT_FOR_DARK_DELAY 1200
/** How long to wait for high lux after lid open [ms] */
#define TKLOCK_LIDFILTER_SET_WAIT_FOR_LIGHT_DELAY 1200
/* ========================================================================= *
* DATATYPES
* ========================================================================= */
typedef struct
{
/** BOOTTIME tick when notification autostops */
int64_t ns_until;
/** Amount of ms autostop extends from user input */
int64_t ns_renew;
/** Private D-Bus name of the slot owner */
gchar *ns_owner;
/** Assumed unique identification string */
gchar *ns_name;
} tklock_notif_slot_t;
typedef struct
{
/** Array of notification slots */
tklock_notif_slot_t tn_slot[TKLOCK_NOTIF_SLOTS];
/** BOOTTIME linger tick from deactivated slots */
int64_t tn_linger;
/** Timer id for autostopping notification slots */
guint tn_autostop_id;
/** Slot owner D-Bus name monitoring list */
GSList *tn_monitor_list;
} tklock_notif_state_t;
/** Proximity sensor history */
typedef struct
{
/** Monotonic timestamp, ms resolution */
int64_t tick;
/** Proximity sensor state */
cover_state_t state;
} ps_history_t;
/** Ambient light lux value mapped into enumerated states
*
* In case the lid sensor can't be trusted for some reason, data from
* ambient light sensor heuristics can be used for avoiding incorrect
* blank/unblank actions.
*
* For this purpose the raw data from ambient light sensor is tracked
* and mapped in to three states:
*
* - TKLOCK_LIDLIGHT_NA: The data from als is not applicable for filtering.
* - TKLOCK_LIDLIGHT_LO: The als indicates darkness.
* - TKLOCK_LIDLIGHT_HI: The als indicates some amount of light.
*/
typedef enum
{
/* Light level is not applicable for state evaluation */
TKLOCK_LIDLIGHT_NA,
/* Light level equals complete darkness */
TKLOCK_LIDLIGHT_LO,
/* Light level equals at least some light */
TKLOCK_LIDLIGHT_HI,
} tklock_lidlight_t;
/* ========================================================================= *
* PROTOTYPES
* ========================================================================= */
// datapipe values and triggers
static void tklock_datapipe_system_state_cb(gconstpointer data);
static void tklock_datapipe_devicelock_state_cb(gconstpointer data);
static void tklock_datapipe_devicelock_state_cb2(gpointer aptr);
static void tklock_datapipe_resume_detected_event_cb(gconstpointer data);
static void tklock_datapipe_devicelock_service_state_cb(gconstpointer data);
static void tklock_datapipe_lipstick_service_state_cb(gconstpointer data);
static void tklock_datapipe_osupdate_running_cb(gconstpointer data);
static void tklock_datapipe_shutting_down_cb(gconstpointer data);
static void tklock_datapipe_display_state_curr_cb(gconstpointer data);
static void tklock_datapipe_display_state_next_cb(gconstpointer data);
static void tklock_datapipe_proximity_eval_led(void);
static void tklock_datapipe_proximity_update(void);
static gboolean tklock_datapipe_proximity_uncover_cb(gpointer data);
static void tklock_datapipe_proximity_uncover_cancel(void);
static void tklock_datapipe_proximity_uncover_schedule(void);
static void tklock_datapipe_proximity_sensor_actual_cb(gconstpointer data);
static void tklock_datapipe_call_state_cb(gconstpointer data);
static void tklock_datapipe_music_playback_ongoing_cb(gconstpointer data);
static void tklock_datapipe_alarm_ui_state_cb(gconstpointer data);
static void tklock_datapipe_charger_state_cb(gconstpointer data);
static void tklock_datapipe_battery_status_cb(gconstpointer data);
static void tklock_datapipe_usb_cable_state_cb(gconstpointer data);
static void tklock_datapipe_jack_sense_state_cb(gconstpointer data);
static void tklock_datapipe_camera_button_state_cb(gconstpointer const data);
static void tklock_datapipe_keypress_event_cb(gconstpointer const data);
static void tklock_datapipe_uiexception_type_cb(gconstpointer data);
static void tklock_datapipe_audio_route_cb(gconstpointer data);
static void tklock_datapipe_tklock_request_cb(gconstpointer data);
static void tklock_datapipe_interaction_expected_cb(gconstpointer data);
static gpointer tklock_datapipe_submode_filter_cb(gpointer data);
static void tklock_datapipe_submode_cb(gconstpointer data);
static void tklock_datapipe_lockkey_state_cb(gconstpointer const data);
static void tklock_datapipe_heartbeat_event_cb(gconstpointer data);
static void tklock_datapipe_keyboard_slide_input_state_cb(gconstpointer const data);
static void tklock_datapipe_keyboard_slide_output_state_cb(gconstpointer const data);
static void tklock_datapipe_keyboard_available_state_cb(gconstpointer const data);
static void tklock_datapipe_mouse_available_state_cb(gconstpointer const data);
static void tklock_datapipe_light_sensor_poll_request_cb(gconstpointer const data);
static void tklock_datapipe_topmost_window_pid_cb(gconstpointer data);
static void tklock_datapipe_light_sensor_actual_cb(gconstpointer data);
static void tklock_datapipe_lid_sensor_is_working_cb(gconstpointer data);
static void tklock_datapipe_lid_sensor_actual_cb(gconstpointer data);
static void tklock_datapipe_lid_sensor_filtered_cb(gconstpointer data);
static void tklock_datapipe_lens_cover_state_cb(gconstpointer data);
static bool tklock_touch_activity_event_p(const struct input_event *ev);
static void tklock_datapipe_user_activity_event_cb(gconstpointer data);
static void tklock_datapipe_init_done_cb(gconstpointer data);
static bool tklock_datapipe_in_tklock_submode(void);
static void tklock_datapipe_set_tklock_submode(bool lock);
static void tklock_datapipe_set_devicelock_state(devicelock_state_t state);
static void tklock_datapipe_rethink_interaction_expected(void);
static void tklock_datapipe_update_interaction_expected(bool expected);
static void tklock_datapipe_init(void);
static void tklock_datapipe_quit(void);
// LID_SENSOR
static bool tklock_lidsensor_is_enabled (void);
static void tklock_lidsensor_init (void);
// LID_LIGHT
static const char *tklock_lidlight_repr (tklock_lidlight_t state);
static tklock_lidlight_t tklock_lidlight_from_lux (int lux);
// LID_FILTER
static bool tklock_lidfilter_is_enabled (void);
static void tklock_lidfilter_set_allow_close (bool allow);
static tklock_lidlight_t tklock_lidfilter_map_als_state (void);
static void tklock_lidfilter_set_als_state (tklock_lidlight_t state);
static gboolean tklock_lidfilter_wait_for_close_cb (gpointer aptr);
static bool tklock_lidfilter_get_wait_for_close (void);
static void tklock_lidfilter_set_wait_for_close (bool state);
static gboolean tklock_lidfilter_wait_for_dark_cb (gpointer aptr);
static bool tklock_lidfilter_get_wait_for_dark (void);
static void tklock_lidfilter_set_wait_for_dark (bool state);
static gboolean tklock_lidfilter_wait_for_light_cb (gpointer aptr);
static bool tklock_lidfilter_get_wait_for_light (void);
static void tklock_lidfilter_set_wait_for_light (bool state);
static void tklock_lidfilter_rethink_als_poll (void);
static void tklock_lidfilter_rethink_allow_close (void);
static void tklock_lidfilter_rethink_als_state (void);
static void tklock_lidfilter_rethink_lid_state (void);
// LID_POLICY
static void tklock_lidpolicy_rethink (void);
// keyboard slide state machine
static void tklock_keyboard_slide_opened(void);
static void tklock_keyboard_slide_opened_cb(gpointer aptr);
static void tklock_keyboard_slide_closed(void);
static void tklock_keyboard_slide_rethink(void);
// autolock state machine
static gboolean tklock_autolock_cb(gpointer aptr);
static void tklock_autolock_evaluate(void);
static void tklock_autolock_enable(void);
static void tklock_autolock_disable(void);
static void tklock_autolock_rethink(void);
static void tklock_autolock_init(void);
static void tklock_autolock_quit(void);
// proximity locking state machine
static gboolean tklock_proxlock_cb(gpointer aptr);
static void tklock_proxlock_resume(void);
static void tklock_proxlock_evaluate(void);
static void tklock_proxlock_enable(void);
static void tklock_proxlock_disable(void);
static void tklock_proxlock_rethink(void);
// autolock based on device lock changes
static void tklock_autolock_on_devlock_block(int duration_ms);
static void tklock_autolock_on_devlock_prime(void);
static void tklock_autolock_on_devlock_trigger(void);
// ui exception handling state machine
static uiexception_type_t topmost_active(uiexception_type_t mask);
static void tklock_uiexception_sync_to_datapipe(void);
static gboolean tklock_uiexception_linger_cb(gpointer aptr);
static void tklock_uiexception_begin(uiexception_type_t type, int64_t linger);
static void tklock_uiexception_end(uiexception_type_t type, int64_t linger);
static void tklock_uiexception_cancel(void);
static void tklock_uiexception_finish(void);
static bool tklock_uiexception_deny_state_restore(bool force, const char *cause);
static void tklock_uiexception_rethink(void);
// low power mode ui state machine
static void tklock_lpmui_set_state(bool enable);
static void tklock_lpmui_reset_history(void);
static void tklock_lpmui_update_history(cover_state_t state);
static bool tklock_lpmui_probe_from_pocket(void);
static bool tklock_lpmui_probe_on_table(void);
static bool tklock_lpmui_probe(void);
static void tklock_lpmui_rethink(void);
static void tklock_lpmui_pre_transition_actions(void);
// legacy hw event input enable/disable state machine
static void tklock_evctrl_set_state(output_state_t *output, bool enable);
static void tklock_evctrl_set_kp_state(bool enable);
static void tklock_evctrl_set_ts_state(bool enable);
static void tklock_evctrl_set_dt_state(bool enable);
static void tklock_evctrl_rethink(void);
// legacy hw double tap calibration
static void tklock_dtcalib_now(void);
static void tklock_dtcalib_from_heartbeat(void);
static gboolean tklock_dtcalib_cb(gpointer data);
static void tklock_dtcalib_start(void);
static void tklock_dtcalib_stop(void);
// DYNAMIC_SETTINGS
static void tklock_setting_sanitize_lid_open_actions(void);
static void tklock_setting_sanitize_lid_close_actions(void);
static void tklock_setting_sanitize_kbd_open_trigger(void);
static void tklock_setting_sanitize_kbd_open_actions(void);
static void tklock_setting_sanitize_kbd_close_trigger(void);
static void tklock_setting_sanitize_kbd_close_actions(void);
static void tklock_setting_cb(GConfClient *const gcc, const guint id, GConfEntry *const entry, gpointer const data);
static void tklock_setting_init(void);
static void tklock_setting_quit(void);
// sysfs probing
static void tklock_sysfs_probe(void);
// dbus ipc with systemui
static void tklock_ui_send_tklock_signal(void);
static void tklock_ui_notify_rethink_wakelock(void);
static bool tklock_ui_notify_must_be_delayed(void);
static gboolean tklock_ui_notify_end_cb(gpointer data);
static gboolean tklock_ui_notify_beg_cb(gpointer data);
static void tklock_ui_notify_schdule(void);
static gboolean tklock_ui_sync_cb(gpointer aptr);
static void tklock_ui_notify_cancel(void);
static void tklock_ui_eat_event(void);
static void tklock_ui_open(void);
static void tklock_ui_close(void);
static bool tklock_ui_is_enabled(void);
static void tklock_ui_set_enabled(bool enable);
static void tklock_ui_get_devicelock_cb(DBusPendingCall *pc, void *aptr);
static void tklock_ui_get_devicelock(void);
static void tklock_ui_send_lpm_signal(void);
static void tklock_ui_enable_lpm(void);
static void tklock_ui_disable_lpm(void);
static void tklock_ui_show_device_unlock(void);;
// dbus ipc
static void tklock_dbus_send_display_blanking_policy(DBusMessage *const req);
static gboolean tklock_dbus_display_blanking_policy_get_cb(DBusMessage *const msg);
static void tklock_dbus_send_keyboard_slide_state(DBusMessage *const req);
static gboolean tklock_dbus_keyboard_slide_state_get_req_cb(DBusMessage *const msg);
static void tklock_dbus_send_keyboard_available_state(DBusMessage *const req);
static gboolean tklock_dbus_keyboard_available_state_get_req_cb(DBusMessage *const msg);
static void tklock_dbus_send_mouse_available_state(DBusMessage *const req);
static gboolean tklock_dbus_mouse_available_state_get_req_cb(DBusMessage *const msg);
static gboolean tklock_dbus_send_tklock_mode(DBusMessage *const method_call);
static gboolean tklock_dbus_mode_get_req_cb(DBusMessage *const msg);
static tklock_request_t tklock_dbus_sanitize_requested_mode(tklock_request_t state);
static gboolean tklock_dbus_mode_change_req_cb(DBusMessage *const msg);
static gboolean tklock_dbus_interaction_expected_cb(DBusMessage *const msg);
static gboolean tklock_dbus_systemui_callback_cb(DBusMessage *const msg);
static gboolean tklock_dbus_devicelock_changed_cb(DBusMessage *const msg);
static gboolean tklock_dbus_notification_beg_cb(DBusMessage *const msg);
static gboolean tklock_dbus_notification_end_cb(DBusMessage *const msg);
static void mce_tklock_init_dbus(void);
static void mce_tklock_quit_dbus(void);
// NOTIFICATION_SLOTS
static void tklock_notif_slot_init(tklock_notif_slot_t *self);
static void tklock_notif_slot_free(tklock_notif_slot_t *self);
static void tklock_notif_slot_set(tklock_notif_slot_t *self, const char *owner, const char *name, int64_t until, int64_t renew);
static bool tklock_notif_slot_is_free(const tklock_notif_slot_t *self);
static bool tklock_notif_slot_has_name(const tklock_notif_slot_t *self, const char *name);
static bool tklock_notif_slot_validate(tklock_notif_slot_t *self, int64_t now);
static bool tklock_notif_slot_renew(tklock_notif_slot_t *self, int64_t now);
static bool tklock_notif_slot_has_owner(const tklock_notif_slot_t *self, const char *owner);
static gchar *tklock_notif_slot_steal_owner(tklock_notif_slot_t *self);
// NOTIFICATION_API
static void tklock_notif_init(void);
static void tklock_notif_quit(void);
static gboolean tklock_notif_autostop_cb(gpointer aptr);
static void tklock_notif_cancel_autostop(void);
static void tklock_notif_schedule_autostop(gint delay);
static void tklock_notif_update_state(void);
static void tklock_notif_extend_by_renew(void);
static void tklock_notif_vacate_slot(const char *owner, const char *name, int64_t linger);
static void tklock_notif_reserve_slot(const char *owner, const char *name, int64_t length, int64_t renew);
static void tklock_notif_vacate_slots_from(const char *owner);
static size_t tklock_notif_count_slots_from(const char *owner);
static gboolean tklock_notif_owner_dropped_cb(DBusMessage *const msg);
static void tklock_notif_add_owner_monitor(const char *owner);
static void tklock_notif_remove_owner_monitor(const char *owner);
static void mce_tklock_begin_notification(const char *owner, const char *name, int64_t length, int64_t renew);
static void mce_tklock_end_notification(const char *owner, const char *name, int64_t linger);
// "module" load/unload
extern gboolean mce_tklock_init(void);
extern void mce_tklock_exit(void);
extern void mce_tklock_unblank(display_state_t to_state);
/* ========================================================================= *
* DYNAMIC_SETTINGS
* ========================================================================= */
/** Flag: Devicelock is handled in lockscreen */
static gboolean tklock_devicelock_in_lockscreen = MCE_DEFAULT_TK_DEVICELOCK_IN_LOCKSCREEN;
static guint tklock_devicelock_in_lockscreen_setting_id = 0;
/** Flag: Convert denied tklock removal attempt to: show device unlock view */
static bool tklock_devicelock_want_to_unlock = false;
/** Flag: Automatically lock (after ON->DIM->OFF cycle) */
static gboolean tk_autolock_enabled = MCE_DEFAULT_TK_AUTOLOCK_ENABLED;
static guint tk_autolock_enabled_setting_id = 0;
/** Flag: Grabbing input devices is allowed */
static gboolean tk_input_policy_enabled = MCE_DEFAULT_TK_INPUT_POLICY_ENABLED;
static guint tk_input_policy_enabled_setting_id = 0;
/** Delay for automatick locking (after ON->DIM->OFF cycle) */
static gint tklock_autolock_delay = MCE_DEFAULT_TK_AUTOLOCK_DELAY;
static guint tklock_autolock_delay_setting_id = 0;
/** Flag: Proximity sensor can block touch input */
static gboolean proximity_blocks_touch = MCE_DEFAULT_TK_PROXIMITY_BLOCKS_TOUCH;
static guint proximity_blocks_touch_setting_id = 0;
/** Volume key input policy */
static gint volkey_policy = MCE_DEFAULT_TK_VOLKEY_POLICY;
static guint volkey_policy_setting_id = 0;
/** Touchscreen gesture (doubletap etc) enable mode */
static gint touchscreen_gesture_enable_mode = MCE_DEFAULT_DOUBLETAP_MODE;
static guint touchscreen_gesture_enable_mode_setting_id = 0;
/** Lid sensor open actions */
static gint tklock_lid_open_actions = MCE_DEFAULT_TK_LID_OPEN_ACTIONS;
static guint tklock_lid_open_actions_setting_id = 0;
/** Lid sensor close actions */
static gint tklock_lid_close_actions = MCE_DEFAULT_TK_LID_CLOSE_ACTIONS;
static guint tklock_lid_close_actions_setting_id = 0;
/** Flag: Is the lid sensor used for display blanking */
static gboolean lid_sensor_enabled = MCE_DEFAULT_TK_LID_SENSOR_ENABLED;
static guint lid_sensor_enabled_setting_id = 0;
/** When to react to keyboard open */
static gint tklock_kbd_open_trigger = MCE_DEFAULT_TK_KBD_OPEN_TRIGGER;
static guint tklock_kbd_open_trigger_setting_id = 0;
/** How to react to keyboard open */
static gint tklock_kbd_open_actions = MCE_DEFAULT_TK_KBD_OPEN_ACTIONS;
static guint tklock_kbd_open_actions_setting_id = 0;
/** When to react to keyboard close */
static gint tklock_kbd_close_trigger = MCE_DEFAULT_TK_KBD_CLOSE_TRIGGER;
static guint tklock_kbd_close_trigger_setting_id = 0;
/** How to react to keyboard close */
static gint tklock_kbd_close_actions = MCE_DEFAULT_TK_KBD_CLOSE_ACTIONS;
static guint tklock_kbd_close_actions_setting_id = 0;
/** Flag for: Using ALS is allowed */
static gboolean als_enabled = MCE_DEFAULT_DISPLAY_ALS_ENABLED;
static guint als_enabled_setting_id = 0;
/** Flag: Use ALS for lid close filtering */
static gboolean filter_lid_with_als = MCE_DEFAULT_TK_FILTER_LID_WITH_ALS;
static guint filter_lid_with_als_setting_id = 0;
/** Maximum amount of light ALS should report when LID is closed */
static gint filter_lid_als_limit = MCE_DEFAULT_TK_FILTER_LID_ALS_LIMIT;
static guint filter_lid_als_limit_setting_id = 0;
/** How long to keep display on after incoming call ends [ms] */
static gint exception_length_call_in = MCE_DEFAULT_TK_EXCEPT_LEN_CALL_IN;
static guint exception_length_call_in_setting_id = 0;
/** How long to keep display on after outgoing call ends [ms] */
static gint exception_length_call_out = MCE_DEFAULT_TK_EXCEPT_LEN_CALL_OUT;
static guint exception_length_call_out_setting_id = 0;
/** How long to keep display on after alarm is handled [ms] */
static gint exception_length_alarm = MCE_DEFAULT_TK_EXCEPT_LEN_ALARM;
static guint exception_length_alarm_setting_id = 0;
/** How long to keep display on when usb cable is connected [ms] */
static gint exception_length_usb_connect = MCE_DEFAULT_TK_EXCEPT_LEN_USB_CONNECT;
static guint exception_length_usb_connect_setting_id = 0;
/** How long to keep display on when usb mode dialog is shown [ms] */
static gint exception_length_usb_dialog = MCE_DEFAULT_TK_EXCEPT_LEN_USB_DIALOG;
static guint exception_length_usb_dialog_setting_id = 0;
/** How long to keep display on when charging starts [ms] */
static gint exception_length_charger = MCE_DEFAULT_TK_EXCEPT_LEN_CHARGER;
static guint exception_length_charger_setting_id = 0;
/** How long to keep display on after battery full [ms] */
static gint exception_length_battery = MCE_DEFAULT_TK_EXCEPT_LEN_BATTERY;
static guint exception_length_battery_setting_id = 0;
/** How long to keep display on when audio jack is inserted [ms] */
static gint exception_length_jack_in = MCE_DEFAULT_TK_EXCEPT_LEN_JACK_IN;
static guint exception_length_jack_in_setting_id = 0;
/** How long to keep display on when audio jack is removed [ms] */
static gint exception_length_jack_out = MCE_DEFAULT_TK_EXCEPT_LEN_JACK_OUT;
static guint exception_length_jack_out_setting_id = 0;
/** How long to keep display on when camera button is pressed [ms] */
static gint exception_length_camera = MCE_DEFAULT_TK_EXCEPT_LEN_CAMERA;
static guint exception_length_camera_setting_id = 0;
/** How long to keep display on when volume button is pressed [ms] */
static gint exception_length_volume = MCE_DEFAULT_TK_EXCEPT_LEN_VOLUME;
static guint exception_length_volume_setting_id = 0;
/** How long to extend display on when there is user activity [ms] */
static gint exception_length_activity = MCE_DEFAULT_TK_EXCEPT_LEN_ACTIVITY;
static guint exception_length_activity_setting_id = 0;
/** Flag for: Allow lockscreen animation during unblanking */
static gboolean lockscreen_anim_enabled = MCE_DEFAULT_TK_LOCKSCREEN_ANIM_ENABLED;
static guint lockscreen_anim_enabled_setting_id = 0;
/** Default delay for delaying proximity uncovered handling [ms] */
static gint tklock_proximity_delay_default = MCE_DEFAULT_TK_PROXIMITY_DELAY_DEFAULT;
static guint tklock_proximity_delay_default_setting_id = 0;
/** Delay for delaying proximity uncovered handling during calls [ms] */
static gint tklock_proximity_delay_incall = MCE_DEFAULT_TK_PROXIMITY_DELAY_INCALL;
static guint tklock_proximity_delay_incall_setting_id = 0;
/* ========================================================================= *
* probed control file paths
* ========================================================================= */
/** SysFS path to touchscreen event disable */
static output_state_t mce_touchscreen_sysfs_disable_output =
{
.context = "touchscreen_disable",
.truncate_file = TRUE,
.close_on_exit = TRUE,
};
/** SysFS path to touchscreen double-tap gesture control */
static const gchar *mce_touchscreen_gesture_enable_path = NULL;
/** SysFS path to touchscreen recalibration control */
static const gchar *mce_touchscreen_calibration_control_path = NULL;
/** SysFS path to keypad event disable */
static output_state_t mce_keypad_sysfs_disable_output =
{
.context = "keypad_disable",
.truncate_file = TRUE,
.close_on_exit = TRUE,
};
/* ========================================================================= *
* DATAPIPE VALUES AND TRIGGERS
* ========================================================================= */
/** Cached submode_pipe state; assume invalid */
static submode_t submode = MCE_SUBMODE_INVALID;
/** Cached PID of process owning the topmost window on UI */
static int topmost_window_pid = -1;
/** Cached init_done state; assume unknown */
static tristate_t init_done = TRISTATE_UNKNOWN;
/** Proximity state history for triggering low power mode ui */
static ps_history_t tklock_lpmui_hist[8];
/** Current tklock ui state
*
* Access only via tklock_ui_is_enabled() / tklock_ui_set_enabled().
*/
static bool tklock_ui_enabled_pvt = false;
/** Current tklock ui state that has been sent to lipstick */
static int tklock_ui_notified = -1; // does not match bool values
/** System state; is undefined at bootup, can't assume anything */
static system_state_t system_state = MCE_SYSTEM_STATE_UNDEF;
/** 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;
/** Call state; assume no active calls */
static call_state_t call_state = CALL_STATE_NONE;
/** Actual proximity state; assume not covered */
static cover_state_t proximity_sensor_actual = COVER_UNDEF;
/** Effective proximity state; assume not covered */
static cover_state_t proximity_sensor_effective = COVER_UNDEF;
/** Lid cover sensor state; assume unkown
*
* When in covered state, it is assumed that it is not physically
* possible to see/interact with the display and thus it should
* stay powered off.
*
* Originally was used to track Nokia N770 slidable cover. Now
* it is used also for things like the hammerhead magnetic lid
* sensor.
*/
static cover_state_t lid_sensor_actual = COVER_UNDEF;
/** Lid cover policy state; assume unknown */
static cover_state_t lid_sensor_filtered = COVER_UNDEF;
/** Change notifications for system_state
*/
static void tklock_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));
tklock_ui_set_enabled(false);
EXIT:
return;
}
/** Device lock state; assume undefined */
static devicelock_state_t devicelock_state = DEVICELOCK_STATE_UNDEFINED;
/** Push device lock state value into devicelock_state_pipe datapipe
*/
static void tklock_datapipe_set_devicelock_state(devicelock_state_t state)
{
switch( state ) {
case DEVICELOCK_STATE_UNLOCKED:
case DEVICELOCK_STATE_UNDEFINED:
case DEVICELOCK_STATE_LOCKED:
break;
default:
mce_log(LL_WARN, "unknown device lock state=%d; assuming locked",
state);
state = DEVICELOCK_STATE_LOCKED;
break;
}
if( devicelock_state != state ) {
datapipe_exec_full(&devicelock_state_pipe,
GINT_TO_POINTER(state));
}
}
/** Change notifications for devicelock_state
*/
static void tklock_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_DEVEL, "devicelock_state = %s -> %s",
devicelock_state_repr(prev),
devicelock_state_repr(devicelock_state));
tklock_uiexception_rethink();
tklock_autolock_rethink();
/* When lipstick is starting up we see device lock
* going through undefined -> locked/unlocked change.
* We must not trigger autolock due to these initial
* device lock transitions */
switch( prev ) {
case DEVICELOCK_STATE_UNDEFINED:
/* Block autolock for 60 seconds when leaving
* undefined state (= lipstick startup) */
tklock_autolock_on_devlock_block(60 * 1000);
break;
case DEVICELOCK_STATE_LOCKED:
/* Unblock autolock earlier if we see transition
* away from locked state (=unlocked by user) */
tklock_autolock_on_devlock_block(0);
break;
default:
break;
}
switch( devicelock_state ) {
case DEVICELOCK_STATE_LOCKED:
tklock_autolock_on_devlock_trigger();
break;
case DEVICELOCK_STATE_UNLOCKED:
switch( display_state_next ) {
case MCE_DISPLAY_OFF:
case MCE_DISPLAY_LPM_OFF:
case MCE_DISPLAY_LPM_ON:
/* Transitions from undefined -> unlocked state occur
* during device bootup / mce restart and should not
* cause any actions.
*/
if( prev == DEVICELOCK_STATE_UNDEFINED )
break;
/* Devicelock ui keeps fingerprint scanner active in LPM state
* and unlocks device on identify, but omits unlock feedback
* and leaves the display state as-is.
*
* As a workaround, execute unlock feedback from mce. Then
* exit from LPM by requesting display power up and removal
* of tklock submode.
*
* While this is mostly relevant to LPM, apply the same logic
* also when in actually powered off display states to guard
* against timing glitches (getting fingerprint identification
* when already decided to exit LPM, etc) and changes in device
* lock ui side logic.
*/
mce_log(LL_WARN, "device got unlocked while display is off; "
"assume fingerprint authentication occurred");
datapipe_exec_full(&ngfd_event_request_pipe,
"unlock_device");
/* Delay display state / tklock processing until proximity
* sensor state is known */
common_on_proximity_schedule(MODULE_NAME,
tklock_datapipe_devicelock_state_cb2,
0);
break;
default:
break;
}
break;
default:
break;
}
EXIT:
return;
}
/** Wait for proximity sensor -callback for fingerprint unlock handling
*
* @param aptr unused
*/
static void tklock_datapipe_devicelock_state_cb2(gpointer aptr)
{
(void)aptr;
/* Still unlocked ? */
if( devicelock_state == DEVICELOCK_STATE_UNLOCKED ) {
if( proximity_sensor_actual != COVER_OPEN ) {
mce_log(LL_WARN, "unblank skipped due to proximity sensor");
}
else {
mce_datapipe_request_display_state(MCE_DISPLAY_ON);
mce_datapipe_request_tklock(TKLOCK_REQUEST_OFF);
}
}
}
/** Resumed from suspend notification */
static void tklock_datapipe_resume_detected_event_cb(gconstpointer data)
{
(void) data;
/* Re-evaluate proximity locking after resuming from
* suspend. */
tklock_proxlock_resume();
}
/** devicelock dbus name is reserved; assume unknown */
static service_state_t devicelock_service_state = SERVICE_STATE_UNDEF;
/** Change notifications for devicelock_service_state
*/
static void tklock_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));
if( devicelock_service_state == SERVICE_STATE_RUNNING ) {
/* query initial device lock state on devicelock/mce startup */
tklock_ui_get_devicelock();
}
else {
/* if device lock service is not running, the device lock
* state is undefined */
tklock_datapipe_set_devicelock_state(DEVICELOCK_STATE_UNDEFINED);
}
EXIT:
return;
}
/** Lipstick dbus name is reserved; assume false */
static service_state_t lipstick_service_state = SERVICE_STATE_UNDEF;
/** Change notifications for lipstick_service_state
*/
static void tklock_datapipe_lipstick_service_state_cb(gconstpointer data)
{
service_state_t prev = lipstick_service_state;
lipstick_service_state = GPOINTER_TO_INT(data);
if( lipstick_service_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "lipstick_service_state = %s -> %s",
service_state_repr(prev),
service_state_repr(lipstick_service_state));
bool enable_tklock = false;
/* Tklock is applicable only when lipstick is running */
if( lipstick_service_state == SERVICE_STATE_RUNNING ) {
/* STOPPED -> RUNNING: Implies lipstick start / restart.
* In this case lockscreen status is decided by lipstick.
* We achieve tklock state synchronization by making a
* lockscreen deactivation request - which lipstick can
* then choose to honor or override.
*
* UNDEF -> RUNNING: Implies a mce restart while lipstick
* is running. What we would like to happen is that
* things stay exactly as they were. However there is
* no way to recover lockscreen state from lipstick.
* So in order to err on the safer side, we activate
* lockscreen to get tklock state in sync again.
*/
if( prev == SERVICE_STATE_UNDEF )
enable_tklock = true;
}
// force tklock ipc
tklock_ui_notified = -1;
tklock_ui_set_enabled(enable_tklock);
EXIT:
return;
}
/** Update mode is active; assume false */
static bool osupdate_running = false;
/** Change notifications for osupdate_running
*/
static void tklock_datapipe_osupdate_running_cb(gconstpointer data)
{
bool prev = osupdate_running;
osupdate_running = GPOINTER_TO_INT(data);
if( osupdate_running == prev )
goto EXIT;
mce_log(LL_DEBUG, "osupdate_running = %d -> %d", prev, osupdate_running);
if( osupdate_running ) {
/* undo tklock when update mode starts */
mce_datapipe_request_tklock(TKLOCK_REQUEST_OFF);
}
EXIT:
return;
}
/** Device is shutting down; assume false */
static bool shutting_down = false;
/** Change notifications for shutting_down
*/
static void tklock_datapipe_shutting_down_cb(gconstpointer data)
{
bool prev = shutting_down;
shutting_down = GPOINTER_TO_INT(data);
if( shutting_down == prev )
goto EXIT;
mce_log(LL_DEBUG, "shutting_down = %d -> %d",
prev, shutting_down);
tklock_evctrl_rethink();
EXIT:
return;
}
/** Autorelock trigger: assume disabled */
static autorelock_t autorelock_trigger = AUTORELOCK_NO_TRIGGERS;
/** Change notifications for display_state_curr
*/
static void tklock_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));
tklock_datapipe_rethink_interaction_expected();
tklock_lidfilter_rethink_allow_close();
/* Disable "wakeup with fake policy" hack
* when any stable display state is reached */
if( display_state_curr != MCE_DISPLAY_POWER_UP &&
display_state_curr != MCE_DISPLAY_POWER_DOWN )
tklock_uiexception_end(UIEXCEPTION_TYPE_NOANIM, 0);
if( display_state_curr == MCE_DISPLAY_DIM )
tklock_ui_eat_event();
tklock_uiexception_rethink();
tklock_autolock_rethink();
tklock_proxlock_rethink();
tklock_evctrl_rethink();
tklock_ui_notify_schdule();
EXIT:
return;
}
/** Pre-change notifications for display_state_curr
*/
static void tklock_datapipe_display_state_next_cb(gconstpointer data)
{
display_state_next = GPOINTER_TO_INT(data);
mce_log(LL_DEBUG, "display_state_next = %s -> %s",
display_state_repr(display_state_curr),
display_state_repr(display_state_next));
if( display_state_next == display_state_curr )
goto EXIT;
/* Cancel autorelock on display off */
switch( display_state_next ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
/* display states that use normal ui */
break;
default:
/* display powered off, showing lpm, etc */
if( autorelock_trigger != AUTORELOCK_NO_TRIGGERS ) {
mce_log(LL_DEBUG, "autorelock canceled: display off");
autorelock_trigger = AUTORELOCK_NO_TRIGGERS;
}
break;
}
tklock_autolock_on_devlock_prime();
tklock_autolock_rethink();
tklock_proxlock_rethink();
tklock_lpmui_pre_transition_actions();
tklock_ui_notify_schdule();
EXIT:
return;
}
/** Timer id for delayed proximity uncovering */
static guint tklock_datapipe_proximity_uncover_id = 0;
/** Re-evaluate proximity sensor debugging led pattern state
*/
static void tklock_datapipe_proximity_eval_led(void)
{
typedef enum {
PROXIMITY_LED_STATE_UNDEFINED = 0,
PROXIMITY_LED_STATE_COVERED = 1,
PROXIMITY_LED_STATE_UNCOVERING = 2,
PROXIMITY_LED_STATE_UNCOVERED = 3,
} proximity_led_state_t;
static proximity_led_state_t prev = PROXIMITY_LED_STATE_UNDEFINED;
/* Evaluate what led pattern should be active */
proximity_led_state_t curr = PROXIMITY_LED_STATE_UNDEFINED;
if( proximity_sensor_effective == COVER_OPEN )
curr = PROXIMITY_LED_STATE_UNCOVERED;
else if( proximity_sensor_actual == COVER_OPEN )
curr = PROXIMITY_LED_STATE_UNCOVERING;
else if( proximity_sensor_actual == COVER_CLOSED )
curr = PROXIMITY_LED_STATE_COVERED;
if( prev == curr )
goto EXIT;
/* Activate new pattern 1st, then deactivate old pattern
* to avoid transition via no active pattern.
*/
switch( curr )
{
case PROXIMITY_LED_STATE_UNCOVERED:
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_PROXIMITY_UNCOVERED);
break;
case PROXIMITY_LED_STATE_UNCOVERING:
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_PROXIMITY_UNCOVERING);
break;
case PROXIMITY_LED_STATE_COVERED:
datapipe_exec_full(&led_pattern_activate_pipe,
MCE_LED_PATTERN_PROXIMITY_COVERED);
break;
default:
break;
}
switch( prev )
{
case PROXIMITY_LED_STATE_UNCOVERED:
datapipe_exec_full(&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_PROXIMITY_UNCOVERED);
break;
case PROXIMITY_LED_STATE_UNCOVERING:
datapipe_exec_full(&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_PROXIMITY_UNCOVERING);
break;
case PROXIMITY_LED_STATE_COVERED:
datapipe_exec_full(&led_pattern_deactivate_pipe,
MCE_LED_PATTERN_PROXIMITY_COVERED);
break;
default:
break;
}
prev = curr;
EXIT:
return;
}
/** Set effective proximity state from current sensor state
*/
static void tklock_datapipe_proximity_update(void)
{
if( proximity_sensor_effective == proximity_sensor_actual )
goto EXIT;
mce_log(LL_DEBUG, "proximity_sensor_effective = %s -> %s",
proximity_state_repr(proximity_sensor_effective),
proximity_state_repr(proximity_sensor_actual));
proximity_sensor_effective = proximity_sensor_actual;
datapipe_exec_full(&proximity_sensor_effective_pipe,
GINT_TO_POINTER(proximity_sensor_effective));
tklock_datapipe_proximity_eval_led();
tklock_uiexception_rethink();
tklock_proxlock_rethink();
tklock_evctrl_rethink();
/* consider moving to lpm ui */
tklock_lpmui_rethink();
EXIT:
return;
}
/** Timer callback for handling delayed proximity uncover
*/
static gboolean tklock_datapipe_proximity_uncover_cb(gpointer data)
{
(void)data;
if( !tklock_datapipe_proximity_uncover_id )
goto EXIT;
tklock_datapipe_proximity_uncover_id = 0;
tklock_datapipe_proximity_update();
wakelock_unlock("mce_proximity_stm");
EXIT:
return FALSE;
}
/** Cancel delayed proximity uncovering
*/
static void tklock_datapipe_proximity_uncover_cancel(void)
{
if( tklock_datapipe_proximity_uncover_id ) {
g_source_remove(tklock_datapipe_proximity_uncover_id),
tklock_datapipe_proximity_uncover_id = 0;
wakelock_unlock("mce_proximity_stm");
}
}
/** Schedule delayed proximity uncovering
*/
static void tklock_datapipe_proximity_uncover_schedule(void)
{
if( tklock_datapipe_proximity_uncover_id )
g_source_remove(tklock_datapipe_proximity_uncover_id);
else
wakelock_lock("mce_proximity_stm", -1);
int delay = tklock_proximity_delay_default;
if( call_state == CALL_STATE_ACTIVE )
delay = tklock_proximity_delay_incall;
if( delay < MCE_MINIMUM_TK_PROXIMITY_DELAY )
delay = MCE_MINIMUM_TK_PROXIMITY_DELAY;
else if( delay > MCE_MAXIMUM_TK_PROXIMITY_DELAY )
delay = MCE_MAXIMUM_TK_PROXIMITY_DELAY;
tklock_datapipe_proximity_uncover_id =
g_timeout_add(delay, tklock_datapipe_proximity_uncover_cb, 0);
}
/** Change notifications for proximity_sensor_actual
*/
static void tklock_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));
tklock_datapipe_proximity_eval_led();
/* update lpm ui proximity history using raw data */
tklock_lpmui_update_history(proximity_sensor_actual);
if( proximity_sensor_actual == COVER_OPEN ) {
tklock_datapipe_proximity_uncover_schedule();
}
else {
tklock_datapipe_proximity_uncover_cancel();
tklock_datapipe_proximity_update();
}
EXIT:
return;
}
/** Change notifications for call_state
*/
static void tklock_datapipe_call_state_cb(gconstpointer data)
{
/* Default to using shorter outgoing call linger time */
static bool incoming = false;
call_state_t prev = call_state;
call_state = GPOINTER_TO_INT(data);
if( call_state == CALL_STATE_INVALID )
call_state = CALL_STATE_NONE;
if( call_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "call_state = %s -> %s",
call_state_repr(prev),
call_state_repr(call_state));
switch( call_state ) {
case CALL_STATE_RINGING:
/* Switch to using longer incoming call linger time */
incoming = true;
/* Fall through */
case CALL_STATE_ACTIVE:
tklock_uiexception_begin(UIEXCEPTION_TYPE_CALL, 0);
break;
default:
tklock_uiexception_end(UIEXCEPTION_TYPE_CALL, incoming ?
exception_length_call_in :
exception_length_call_out);
/* Restore linger time to default again */
incoming = false;
break;
}
// display on/off policy
tklock_uiexception_rethink();
// volume keys during call
tklock_evctrl_rethink();
EXIT:
return;
}
/** Music playback state; assume not playing */
static bool music_playback_ongoing = false;
/** Change notifications for music_playback_ongoing
*/
static void tklock_datapipe_music_playback_ongoing_cb(gconstpointer data)
{
bool prev = music_playback_ongoing;
music_playback_ongoing = GPOINTER_TO_INT(data);
if( music_playback_ongoing == prev )
goto EXIT;
mce_log(LL_DEBUG, "music_playback_ongoing = %d -> %d",
prev, music_playback_ongoing);
// volume keys during playback
tklock_evctrl_rethink();
EXIT:
return;
}
/** Alarm state; assume no active alarms */
static alarm_ui_state_t alarm_ui_state = MCE_ALARM_UI_OFF_INT32;
/** Change notifications for alarm_ui_state
*/
static void tklock_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 == MCE_ALARM_UI_INVALID_INT32 )
alarm_ui_state = MCE_ALARM_UI_OFF_INT32;
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));
switch( alarm_ui_state ) {
case MCE_ALARM_UI_RINGING_INT32:
case MCE_ALARM_UI_VISIBLE_INT32:
tklock_uiexception_begin(UIEXCEPTION_TYPE_ALARM, 0);
break;
default:
tklock_uiexception_end(UIEXCEPTION_TYPE_ALARM, exception_length_alarm);
break;
}
tklock_uiexception_rethink();
EXIT:
return;
}
/** Charger state; assume not charging */
static charger_state_t charger_state = CHARGER_STATE_UNDEF;
/** Change notifications for charger_state
*/
static void tklock_datapipe_charger_state_cb(gconstpointer data)
{
charger_state_t prev = charger_state;
charger_state = GPOINTER_TO_INT(data);
if( charger_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "charger_state = %s -> %s",
charger_state_repr(prev),
charger_state_repr(charger_state));
/* No exception on mce startup */
if( prev == CHARGER_STATE_UNDEF )
goto EXIT;
/* Notification expected when charging starts */
if( charger_state == CHARGER_STATE_ON )
mce_tklock_begin_notification(0, "mce_charger_state",
exception_length_charger, -1);
EXIT:
return;
}
/** Battery status; not known initially, can't assume anything */
static battery_status_t battery_status = BATTERY_STATUS_UNDEF;
/** Change notifications for battery_status
*/
static void tklock_datapipe_battery_status_cb(gconstpointer data)
{
battery_status_t prev = battery_status;
battery_status = GPOINTER_TO_INT(data);
if( battery_status == prev )
goto EXIT;
mce_log(LL_DEBUG, "battery_status = %s -> %s",
battery_status_repr(prev),
battery_status_repr(battery_status));
if( battery_status == BATTERY_STATUS_FULL ) {
mce_tklock_begin_notification(0, "mce_battery_full",
exception_length_battery, -1);
}
EXIT:
return;
}
/** USB cable status; assume disconnected */
static usb_cable_state_t usb_cable_state = USB_CABLE_UNDEF;
/** Change notifications for usb_cable_state
*/
static void tklock_datapipe_usb_cable_state_cb(gconstpointer data)
{
usb_cable_state_t prev = usb_cable_state;
usb_cable_state = GPOINTER_TO_INT(data);
if( prev == usb_cable_state )
goto EXIT;
mce_log(LL_DEBUG, "usb_cable_state = %s -> %s",
usb_cable_state_repr(prev),
usb_cable_state_repr(usb_cable_state));
/* No exception on mce startup */
if( prev == USB_CABLE_UNDEF )
goto EXIT;
switch( usb_cable_state ) {
case USB_CABLE_DISCONNECTED:
mce_tklock_end_notification(0, "mce_usb_connect", 0);
mce_tklock_end_notification(0, "mce_usb_dialog", 0);
break;
case USB_CABLE_CONNECTED:
mce_tklock_begin_notification(0, "mce_usb_connect",
exception_length_usb_connect, -1);
break;
case USB_CABLE_ASK_USER:
mce_tklock_begin_notification(0, "mce_usb_dialog",
exception_length_usb_dialog, -1);
break;
default:
goto EXIT;
}
EXIT:
return;
}
/** Audio jack state; assume not known yet */
static cover_state_t jack_sense_state = COVER_UNDEF;
/** Change notifications for jack_sense_state
*/
static void tklock_datapipe_jack_sense_state_cb(gconstpointer data)
{
cover_state_t prev = jack_sense_state;
jack_sense_state = GPOINTER_TO_INT(data);
if( prev == jack_sense_state )
goto EXIT;
mce_log(LL_DEBUG, "jack_sense_state = %s -> %s",
cover_state_repr(prev),
cover_state_repr(jack_sense_state));
if( prev == COVER_UNDEF )
goto EXIT;
int64_t length = -1;
switch( jack_sense_state ) {
case COVER_CLOSED:
length = exception_length_jack_in;
break;
case COVER_OPEN:
length = exception_length_jack_out;
break;
default:
break;
}
mce_tklock_begin_notification(0, "mce_jack_sense", length, -1);
EXIT:
return;
}
/** Change notifications for camera_button
*/
static void tklock_datapipe_camera_button_state_cb(gconstpointer const data)
{
/* TODO: This might make no sense, need to check on HW that has
* dedicated camera button ... */
(void)data;
mce_tklock_begin_notification(0, "mce_camera_button",
exception_length_camera, -1);
}
/** Change notifications for keypress
*/
static void tklock_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;
// ignore non-key events
if( ev->type != EV_KEY )
goto EXIT;
// ignore key up events
if( ev->value == 0 )
goto EXIT;
switch( ev->code ) {
case KEY_POWER:
// power key events are handled in powerkey.c
break;
case KEY_CAMERA:
mce_log(LL_DEBUG, "camera key");
mce_tklock_begin_notification(0, "mce_camera_key",
exception_length_camera, -1);
break;
case KEY_VOLUMEDOWN:
case KEY_VOLUMEUP:
if( datapipe_get_gint(keypad_grab_wanted_pipe) ) {
mce_log(LL_DEVEL, "volume key ignored");
break;
}
mce_log(LL_DEBUG, "volume key");
mce_tklock_begin_notification(0, "mce_volume_key",
exception_length_volume, -1);
break;
default:
break;
}
EXIT:
return;
}
/** UI exception state; initialized to none */
static uiexception_type_t uiexception_type = UIEXCEPTION_TYPE_NONE;
/** Change notifications for uiexception_type
*/
static void tklock_datapipe_uiexception_type_cb(gconstpointer data)
{
uiexception_type_t prev = uiexception_type;
uiexception_type = GPOINTER_TO_INT(data);
if( uiexception_type == prev )
goto EXIT;
mce_log(LL_CRUCIAL, "uiexception_type = %s -> %s",
uiexception_type_repr(prev),
uiexception_type_repr(uiexception_type));
/* Cancel autorelock if there is a call or alarm */
if( (uiexception_type & (UIEXCEPTION_TYPE_CALL | UIEXCEPTION_TYPE_ALARM)) &&
autorelock_trigger != AUTORELOCK_NO_TRIGGERS ) {
mce_log(LL_DEBUG, "autorelock canceled: handling call/alarm");
autorelock_trigger = AUTORELOCK_NO_TRIGGERS;
}
/* Forget lpm ui triggering history
* whenever exception state changes */
tklock_lpmui_reset_history();
tklock_autolock_rethink();
tklock_proxlock_rethink();
/* Broadcast blanking policy change */
tklock_dbus_send_display_blanking_policy(0);
EXIT:
return;
}
/** Audio routing state; assume handset */
static audio_route_t audio_route = AUDIO_ROUTE_HANDSET;
/** Change notifications for audio_route
*/
static void tklock_datapipe_audio_route_cb(gconstpointer data)
{
audio_route_t prev = audio_route;
audio_route = GPOINTER_TO_INT(data);
if( audio_route == AUDIO_ROUTE_UNDEF )
audio_route = AUDIO_ROUTE_HANDSET;
if( audio_route == prev )
goto EXIT;
mce_log(LL_DEBUG, "audio_route = %s -> %s",
audio_route_repr(prev), audio_route_repr(audio_route));
tklock_uiexception_rethink();
EXIT:
return;
}
/** Change notifications for tklock_request_pipe
*
* Handles tklock requests from outside this module
*/
static void tklock_datapipe_tklock_request_cb(gconstpointer data)
{
tklock_request_t tklock_request = GPOINTER_TO_INT(data);
mce_log(LL_DEBUG, "tklock_request = %s",
tklock_request_repr(tklock_request));
bool enable = tklock_ui_is_enabled();
switch( tklock_request ) {
case TKLOCK_REQUEST_UNDEF:
case TKLOCK_REQUEST_OFF:
case TKLOCK_REQUEST_OFF_DELAYED:
enable = false;
break;
default:
case TKLOCK_REQUEST_OFF_PROXIMITY:
case TKLOCK_REQUEST_ON:
case TKLOCK_REQUEST_ON_DIMMED:
case TKLOCK_REQUEST_ON_PROXIMITY:
case TKLOCK_REQUEST_ON_DELAYED:
enable = true;
break;
case TKLOCK_REQUEST_TOGGLE:
enable = !enable;
}
tklock_ui_set_enabled(enable);
}
/** Interaction expected; assume false */
static bool interaction_expected = false;
/** Interaction expected; unfiltered info from compositor */
static bool interaction_expected_raw = false;
/** Change notifications for interaction_expected_pipe
*/
static void tklock_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);
/* All changes must be ignored when handling exceptional things
* like calls and alarms that are shown on top of lockscreen ui.
*/
if( uiexception_type & (UIEXCEPTION_TYPE_CALL | UIEXCEPTION_TYPE_ALARM) )
goto EXIT;
/* Edge triggered action: When interaction becomes expected
* while lockscreen is still active (e.g. display has been
* unblanked to show notification on the lockscreen and
* user has swiped from plain lockscreen view to device unlock
* code entry view) the display state restore should be disabled.
*/
if( display_state_next == MCE_DISPLAY_ON &&
tklock_ui_is_enabled() && interaction_expected ) {
tklock_uiexception_deny_state_restore(true, "interaction expected");
}
EXIT:
return;
}
/** Re-evaluate effective interaction_expected value
*
* The notifications from compositor side do not always make
* sense from mce point of view.
*
* This function:
* - Normalizes the interaction_expected value by filtering out
* obviously impossible situations such as having interacation
* expected while display is powered off.
* - Should be called whenever state variables used in the
* filtering have changed.
*/
static void tklock_datapipe_rethink_interaction_expected(void)
{
bool use = interaction_expected_raw;
switch( display_state_curr ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
/* Display is in state that allows interaction */
if( submode & MCE_SUBMODE_TKLOCK ) {
/* Lockscreen active
* -> use reported state
*/
}
else if( topmost_window_pid == -1 ) {
/* Home screen active
* -> use reported state
*/
}
else {
/* Application active
* -> ignore reported state
*/
use = true;
}
break;
default:
/* Display is not in state allowing interaction
* -> ignore reported state */
use = false;
break;
}
if( interaction_expected != use )
datapipe_exec_full(&interaction_expected_pipe, GINT_TO_POINTER(use));
}
/** Update raw interaction expected state
*
* Updates cached raw value and re-calculates effective value.
*
* @param expected state as reported by compositor
*/
static void tklock_datapipe_update_interaction_expected(bool expected)
{
if( interaction_expected_raw == expected )
goto EXIT;
mce_log(LL_DEBUG, "interaction_expected_raw: %d -> %d",
interaction_expected_raw, expected);
interaction_expected_raw = expected;
tklock_datapipe_rethink_interaction_expected();
EXIT:
return;
}
/** Filter tklock submode changes
*
* All tklock submode changes are subjected to policy implemented
* at tklock_ui_xxx() functions.
*
* Basically this ensures tklock_datapipe_submode_cb() will never
* see submode values where tklock would not agree with policy.
*/
static gpointer tklock_datapipe_submode_filter_cb(gpointer data)
{
submode_t input = GPOINTER_TO_INT(data);
submode_t output = input;
tklock_ui_set_enabled(input & MCE_SUBMODE_TKLOCK);
if( tklock_ui_is_enabled() )
output |= MCE_SUBMODE_TKLOCK;
else
output &= ~MCE_SUBMODE_TKLOCK;
if( input != output )
mce_log(LL_DEBUG, "submode filter: %s", submode_change_repr(input, output));
return GINT_TO_POINTER(output);
}
/** Change notifications for submode
*/
static void tklock_datapipe_submode_cb(gconstpointer data)
{
submode_t prev = submode;
submode = GPOINTER_TO_INT(data);
if( submode == prev )
goto EXIT;
/* Note: Due to filtering at tklock_datapipe_submode_filter_cb()
* the submode value seen here is always in sync with policy
* implemented at tklock_ui_xxx() functions.
*/
mce_log(LL_DEBUG, "submode = %s", submode_change_repr(prev, submode));
// out of sync tklock state blocks state restore
tklock_uiexception_rethink();
// block tklock removal while autolock rules apply
tklock_autolock_rethink();
tklock_proxlock_rethink();
tklock_evctrl_rethink();
// skip the rest if tklock did not change
if( !((prev ^ submode) & MCE_SUBMODE_TKLOCK) )
goto EXIT;
tklock_datapipe_rethink_interaction_expected();
if( submode & MCE_SUBMODE_TKLOCK ) {
// tklock added
}
else {
// tklock removed
switch( display_state_next ) {
case MCE_DISPLAY_LPM_ON:
case MCE_DISPLAY_LPM_OFF:
/* We're currently in or transitioning to lpm display state
* and tklock just got removed. Normally this should not
* happen, so emit error message to journal. */
mce_log(LL_ERR, "tklock submode was removed in lpm state");
/* Nevertheless, removal of tklock means there is something
* happening at the ui side - and probably the best course of
* action is to cancel lpm state by turning on the display. */
mce_datapipe_request_display_state(MCE_DISPLAY_ON);
break;
default:
case MCE_DISPLAY_UNDEF:
case MCE_DISPLAY_OFF:
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_ON:
// nop
break;
}
}
EXIT:
return;
}
/** Query the touchscreen/keypad lock status
*
* @return TRUE if the touchscreen/keypad lock is enabled,
* FALSE if the touchscreen/keypad lock is disabled
*/
static bool tklock_datapipe_in_tklock_submode(void)
{
return (submode & MCE_SUBMODE_TKLOCK) != 0;
}
static void tklock_datapipe_set_tklock_submode(bool lock)
{
/* This function should be called only via:
*
* tklock_ui_set_enabled()
* tklock_ui_sync_cb()
* tklock_datapipe_set_tklock_submode()
*/
mce_log(LL_DEBUG, "tklock submode request: %s",
lock ? "LOCK" : "UNLOCK");
if( lock )
mce_add_submode_int32(MCE_SUBMODE_TKLOCK);
else
mce_rem_submode_int32(MCE_SUBMODE_TKLOCK);
}
/** Change notifications for lockkey_state_pipe
*/
static void tklock_datapipe_lockkey_state_cb(gconstpointer const data)
{
/* TODO: IIRC lock key is N900 hw feature, I have not had a chance
* to test if this actually works ... */
key_state_t key_state = GPOINTER_TO_INT(data);
mce_log(LL_DEBUG, "lockkey: %s", key_state_repr(key_state));
/* Ignore release events */
if( key_state != KEY_STATE_PRESSED )
goto EXIT;
/* Try to give it the same treatment as power key would get.
* Copy pasted from generic_powerkey_handler() @ powerkey.c */
switch( display_state_next ) {
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_POWER_UP:
mce_log(LL_DEBUG, "display -> off + lock");
/* Do the locking before turning display off.
*
* The tklock requests get ignored in act dead
* etc, so we can just blindly request it.
*/
mce_datapipe_request_tklock(TKLOCK_REQUEST_ON);
mce_datapipe_request_display_state(MCE_DISPLAY_OFF);
break;
default:
case MCE_DISPLAY_UNDEF:
case MCE_DISPLAY_OFF:
case MCE_DISPLAY_LPM_OFF:
case MCE_DISPLAY_LPM_ON:
case MCE_DISPLAY_POWER_DOWN:
mce_log(LL_DEBUG, "display -> on");
mce_datapipe_request_display_state(MCE_DISPLAY_ON);
break;
}
EXIT:
return;
}
/** Change notifications for heartbeat_event_pipe
*/
static void tklock_datapipe_heartbeat_event_cb(gconstpointer data)
{
(void)data;
mce_log(LL_DEBUG, "heartbeat");
tklock_dtcalib_from_heartbeat();
}
/** Keypad slide input state; assume closed */
static cover_state_t keyboard_slide_input_state = COVER_CLOSED;
/** Change notifications from keyboard_slide_state_pipe
*/
static void tklock_datapipe_keyboard_slide_input_state_cb(gconstpointer const data)
{
cover_state_t prev = keyboard_slide_input_state;
keyboard_slide_input_state = GPOINTER_TO_INT(data);
if( keyboard_slide_input_state == COVER_UNDEF )
keyboard_slide_input_state = COVER_CLOSED;
if( keyboard_slide_input_state == prev )
goto EXIT;
mce_log(LL_DEVEL, "keyboard_slide_input_state = %s -> %s",
cover_state_repr(prev),
cover_state_repr(keyboard_slide_input_state));
tklock_keyboard_slide_rethink();
EXIT:
return;
}
/** Keypad slide output state; assume unknown */
static cover_state_t keyboard_slide_output_state = COVER_UNDEF;
static void
tklock_datapipe_keyboard_slide_output_state_cb(gconstpointer const data)
{
cover_state_t prev = keyboard_slide_output_state;
keyboard_slide_output_state = GPOINTER_TO_INT(data);
if( keyboard_slide_output_state == prev )
goto EXIT;
mce_log(LL_DEVEL, "keyboard_slide_output_state = %s -> %s",
cover_state_repr(prev),
cover_state_repr(keyboard_slide_output_state));
tklock_dbus_send_keyboard_slide_state(0);
EXIT:
return;
}
/** Keypad available output state; assume unknown */
static cover_state_t keyboard_available_state = COVER_UNDEF;
static void
tklock_datapipe_keyboard_available_state_cb(gconstpointer const data)
{
cover_state_t prev = keyboard_available_state;
keyboard_available_state = GPOINTER_TO_INT(data);
if( keyboard_available_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "keyboard_available_state = %s -> %s",
cover_state_repr(prev),
cover_state_repr(keyboard_available_state));
tklock_dbus_send_keyboard_available_state(0);
EXIT:
return;
}
/** Mouse available output state; assume unknown */
static cover_state_t mouse_available_state = COVER_UNDEF;
static void
tklock_datapipe_mouse_available_state_cb(gconstpointer const data)
{
cover_state_t prev = mouse_available_state;
mouse_available_state = GPOINTER_TO_INT(data);
if( mouse_available_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "mouse_available_state = %s -> %s",
cover_state_repr(prev),
cover_state_repr(mouse_available_state));
tklock_dbus_send_mouse_available_state(0);
EXIT:
return;
}
/** Cached als poll state; tracked via tklock_datapipe_light_sensor_poll_request_cb() */
static gboolean light_sensor_polling = FALSE;
/** Ambient Light Sensor filter for temporary sensor enable
*
* @param data Polling enabled/disabled bool (as void pointer)
*/
static void
tklock_datapipe_light_sensor_poll_request_cb(gconstpointer const data)
{
gboolean prev = light_sensor_polling;
light_sensor_polling = GPOINTER_TO_INT(data) ? TRUE : FALSE;
mce_log(LL_DEBUG, "light_sensor_polling: %s -> %s",
prev ? "true" : "false",
light_sensor_polling ? "true" : "false");
/* Check without comparing to previous state. The poll
* request can be denied by datapipe filter at the als
* plugin - in which case we see a false->false transition
* here at datapipe output trigger callback. */
tklock_lidfilter_rethink_als_poll();
}
/** Change notifications for topmost_window_pid_pipe
*/
static void
tklock_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);
tklock_datapipe_rethink_interaction_expected();
EXIT:
return;
}
/** Raw ambient light sensor state; assume unknown */
static int light_sensor_actual = -1;
/** Change notifications from light_sensor_actual_pipe
*/
static void tklock_datapipe_light_sensor_actual_cb(gconstpointer data)
{
cover_state_t prev = light_sensor_actual;
light_sensor_actual = GPOINTER_TO_INT(data);
if( light_sensor_actual == prev )
goto EXIT;
mce_log(LL_DEBUG, "light_sensor_actual = %d -> %d",
prev, light_sensor_actual);
tklock_lidfilter_rethink_als_state();
EXIT:
return;
}
/** Assume lid sensor is broken until we have seen closed->open transition
*
* If the lid sensor is used for display blanking, a faulty sensor can
* cause a lot of problems.
*
* To avoid this mce tracks persistently whether the sensor on the device
* has been seen to function on previous mce invocations.
*
* This cached state must be recovered before the datapipe callbacks
* that depend on it are hooked up.
*/
static bool tklock_lid_sensor_is_working = false;
/** Path to the flag file for persistent tklock_lid_sensor_is_working */
#define LID_SENSOR_IS_WORKING_FLAG_FILE "/var/lib/mce/lid_sensor_is_working"
/** Keep flag file in sync with lid_sensor_is_working_pipe status
*/
static void tklock_datapipe_lid_sensor_is_working_cb(gconstpointer data)
{
bool prev = tklock_lid_sensor_is_working;
tklock_lid_sensor_is_working = GPOINTER_TO_INT(data);
if( tklock_lid_sensor_is_working == prev )
goto EXIT;
mce_log(LL_DEVEL, "lid_sensor_is_working = %s -> %s",
prev ? "true" : "false",
tklock_lid_sensor_is_working ? "true" : "false");
if( tklock_lid_sensor_is_working ) {
/* Create flag file */
int fd = open(LID_SENSOR_IS_WORKING_FLAG_FILE, O_WRONLY|O_CREAT, 0644);
if( fd == -1 )
mce_log(LL_WARN, "%s: could not create flag file: %m",
LID_SENSOR_IS_WORKING_FLAG_FILE);
else
close(fd);
tklock_lidpolicy_rethink();
}
else {
/* Remove flag file */
if( unlink(LID_SENSOR_IS_WORKING_FLAG_FILE) == -1 && errno != ENOENT )
mce_log(LL_WARN, "%s: could not remove flag file: %m",
LID_SENSOR_IS_WORKING_FLAG_FILE);
/* Invalidate sensor data */
datapipe_exec_full(&lid_sensor_actual_pipe,
GINT_TO_POINTER(COVER_UNDEF));
}
EXIT:
return;
}
/** Change notifications from lid_sensor_actual_pipe
*/
static void tklock_datapipe_lid_sensor_actual_cb(gconstpointer data)
{
cover_state_t prev = lid_sensor_actual;
lid_sensor_actual = GPOINTER_TO_INT(data);
if( lid_sensor_actual == prev )
goto EXIT;
if( prev == COVER_CLOSED && lid_sensor_actual == COVER_OPEN ) {
/* We have seen the sensor flip from closed to open position,
* so we can stop assuming it stays closed forever */
datapipe_exec_full(&lid_sensor_is_working_pipe,
GINT_TO_POINTER(true));
}
mce_log(LL_DEVEL, "lid_sensor_actual = %s -> %s",
cover_state_repr(prev),
cover_state_repr(lid_sensor_actual));
tklock_lidfilter_rethink_lid_state();
EXIT:
return;
}
/** Change notifications from lid_sensor_filtered_pipe
*/
static void tklock_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_DEVEL, "lid_sensor_filtered = %s -> %s",
cover_state_repr(prev),
cover_state_repr(lid_sensor_filtered));
/* TODO: On devices that have means to detect physically covered
* display, it might be desirable to also power off:
* - proximity sensor
* - notification led
* - double tap detection
*
* Note: Logic for volume key control exists, but is not used atm */
/* Re-evaluate need for touch blocking */
tklock_evctrl_rethink();
EXIT:
return;
}
/** Camera lens cover state; assume closed */
static cover_state_t lens_cover_state = COVER_CLOSED;
/** Change notifications from lens_cover_state_pipe
*/
static void tklock_datapipe_lens_cover_state_cb(gconstpointer data)
{
cover_state_t prev = lens_cover_state;
lens_cover_state = GPOINTER_TO_INT(data);
if( lens_cover_state == COVER_UNDEF )
lens_cover_state = COVER_CLOSED;
if( lens_cover_state == prev )
goto EXIT;
mce_log(LL_DEBUG, "lens_cover_state = %s -> %s",
cover_state_repr(prev),
cover_state_repr(lens_cover_state));
// TODO: COVER_OPEN -> display on, unlock, reason=AUTORELOCK_KBD_SLIDE
// TODO: COVER_CLOSE -> display off, lock if reason==AUTORELOCK_KBD_SLIDE
EXIT:
return;
}
/** Check if event relates to ongoing user activity on screen
*
* Detect touch screen events that signify finger on screen
* situation.
*
* To make things work in SDK do mouse click detection too.
*/
static bool tklock_touch_activity_event_p(const struct input_event *ev)
{
bool activity = false;
switch( ev->type ) {
case EV_KEY:
switch( ev->code ) {
case BTN_MOUSE:
case BTN_TOUCH:
activity = (ev->value != 0);
break;
default:
break;
}
break;
case EV_ABS:
switch( ev->code ) {
case ABS_MT_POSITION_X:
case ABS_MT_POSITION_Y:
activity = true;
break;
case ABS_MT_PRESSURE:
case ABS_MT_TOUCH_MAJOR:
case ABS_MT_WIDTH_MAJOR:
activity = (ev->value > 0);
break;
case ABS_MT_TRACKING_ID:
activity = (ev->value != -1);
break;
default:
break;
}
break;
default:
break;
}
return activity;
}
/** Handle user_activity_event_pipe notifications
*
* @param data input_event as void pointer
*/
static void tklock_datapipe_user_activity_event_cb(gconstpointer data)
{
static int64_t last_time = 0;
const struct input_event *ev = data;
if( !ev )
goto EXIT;
/* We are only interested in touch activity */
if( !tklock_touch_activity_event_p(ev) )
goto EXIT;
/* Deal with autorelock cancellation 1st */
if( autorelock_trigger != AUTORELOCK_NO_TRIGGERS ) {
mce_log(LL_DEBUG, "autorelock canceled: touch activity");
autorelock_trigger = AUTORELOCK_NO_TRIGGERS;
}
/* Touch events relevant unly when handling notification & linger */
if( !(uiexception_type & (UIEXCEPTION_TYPE_NOTIF | UIEXCEPTION_TYPE_LINGER)) )
goto EXIT;
int64_t now = mce_lib_get_boot_tick();
if( last_time + 200 > now )
goto EXIT;
last_time = now;
mce_log(LL_DEBUG, "type: %s, code: %s, value: %d",
evdev_get_event_type_name(ev->type),
evdev_get_event_code_name(ev->type, ev->code),
ev->value);
/* N.B. the uiexception_type is bitmask, but only bit at time is
* visible in the uiexception_type datapipe */
switch( uiexception_type ) {
case UIEXCEPTION_TYPE_LINGER:
/* touch events during linger -> do not restore display state */
tklock_uiexception_deny_state_restore(true,
"touch event during linger");
break;
case UIEXCEPTION_TYPE_NOTIF:
/* touch events while device is not locked -> do not restore display state */
if( tklock_uiexception_deny_state_restore(false,
"touch event during notification") ) {
break;
}
/* touchscreen activity makes notification exceptions to last longer */
mce_log(LL_DEBUG, "touch event; lengthen notification exception");
tklock_notif_extend_by_renew();
break;
default:
break;
}
EXIT:
return;
}
/** Change notifications for init_done
*/
static void tklock_datapipe_init_done_cb(gconstpointer data)
{
tristate_t prev = init_done;
init_done = GPOINTER_TO_INT(data);
if( init_done == prev )
goto EXIT;
mce_log(LL_DEBUG, "init_done = %s -> %s",
tristate_repr(prev),
tristate_repr(init_done));
/* No direct actions, but restoring display state
* after notifications etc is disabled until init
* done is reached. See tklock_uiexception_begin().
*/
EXIT:
return;
}
/** Array of datapipe handlers */
static datapipe_handler_t tklock_datapipe_handlers[] =
{
// input filters
{
.datapipe = &submode_pipe,
.filter_cb = tklock_datapipe_submode_filter_cb,
},
// output triggers
{
.datapipe = &resume_detected_event_pipe,
.output_cb = tklock_datapipe_resume_detected_event_cb,
},
{
.datapipe = &lipstick_service_state_pipe,
.output_cb = tklock_datapipe_lipstick_service_state_cb,
},
{
.datapipe = &devicelock_service_state_pipe,
.output_cb = tklock_datapipe_devicelock_service_state_cb,
},
{
.datapipe = &osupdate_running_pipe,
.output_cb = tklock_datapipe_osupdate_running_cb,
},
{
.datapipe = &shutting_down_pipe,
.output_cb = tklock_datapipe_shutting_down_cb,
},
{
.datapipe = &devicelock_state_pipe,
.output_cb = tklock_datapipe_devicelock_state_cb,
},
{
.datapipe = &display_state_curr_pipe,
.output_cb = tklock_datapipe_display_state_curr_cb,
},
{
.datapipe = &display_state_next_pipe,
.output_cb = tklock_datapipe_display_state_next_cb,
},
{
.datapipe = &interaction_expected_pipe,
.output_cb = tklock_datapipe_interaction_expected_cb,
},
{
.datapipe = &proximity_sensor_actual_pipe,
.output_cb = tklock_datapipe_proximity_sensor_actual_cb,
},
{
.datapipe = &call_state_pipe,
.output_cb = tklock_datapipe_call_state_cb,
},
{
.datapipe = &music_playback_ongoing_pipe,
.output_cb = tklock_datapipe_music_playback_ongoing_cb,
},
{
.datapipe = &alarm_ui_state_pipe,
.output_cb = tklock_datapipe_alarm_ui_state_cb,
},
{
.datapipe = &charger_state_pipe,
.output_cb = tklock_datapipe_charger_state_cb,
},
{
.datapipe = &battery_status_pipe,
.output_cb = tklock_datapipe_battery_status_cb,
},
{
.datapipe = &uiexception_type_pipe,
.output_cb = tklock_datapipe_uiexception_type_cb,
},
{
.datapipe = &audio_route_pipe,
.output_cb = tklock_datapipe_audio_route_cb,
},
{
.datapipe = &system_state_pipe,
.output_cb = tklock_datapipe_system_state_cb,
},
{
.datapipe = &usb_cable_state_pipe,
.output_cb = tklock_datapipe_usb_cable_state_cb,
},
{
.datapipe = &jack_sense_state_pipe,
.output_cb = tklock_datapipe_jack_sense_state_cb,
},
{
.datapipe = &heartbeat_event_pipe,
.output_cb = tklock_datapipe_heartbeat_event_cb,
},
{
.datapipe = &submode_pipe,
.output_cb = tklock_datapipe_submode_cb,
},
{
.datapipe = &light_sensor_actual_pipe,
.output_cb = tklock_datapipe_light_sensor_actual_cb,
},
{
.datapipe = &lid_sensor_is_working_pipe,
.output_cb = tklock_datapipe_lid_sensor_is_working_cb,
},
{
.datapipe = &lid_sensor_actual_pipe,
.output_cb = tklock_datapipe_lid_sensor_actual_cb,
},
{
.datapipe = &lid_sensor_filtered_pipe,
.output_cb = tklock_datapipe_lid_sensor_filtered_cb,
},
{
.datapipe = &lens_cover_state_pipe,
.output_cb = tklock_datapipe_lens_cover_state_cb,
},
{
.datapipe = &user_activity_event_pipe,
.output_cb = tklock_datapipe_user_activity_event_cb,
},
{
.datapipe = &init_done_pipe,
.output_cb = tklock_datapipe_init_done_cb,
},
{
/* Note: Keybaord slide state signaling must reflect
* the actual state -> uses output triggering
* unlike the display state logic that is bound
* to datapipe input. */
.datapipe = &keyboard_slide_state_pipe,
.output_cb = tklock_datapipe_keyboard_slide_output_state_cb,
},
{
.datapipe = &keyboard_available_state_pipe,
.output_cb = tklock_datapipe_keyboard_available_state_cb,
},
{
.datapipe = &mouse_available_state_pipe,
.output_cb = tklock_datapipe_mouse_available_state_cb,
},
{
.datapipe = &light_sensor_poll_request_pipe,
.output_cb = tklock_datapipe_light_sensor_poll_request_cb,
},
{
.datapipe = &topmost_window_pid_pipe,
.output_cb = tklock_datapipe_topmost_window_pid_cb,
},
// input triggers
{
.datapipe = &tklock_request_pipe,
.input_cb = tklock_datapipe_tklock_request_cb,
},
{
.datapipe = &keypress_event_pipe,
.input_cb = tklock_datapipe_keypress_event_cb,
},
{
.datapipe = &lockkey_state_pipe,
.input_cb = tklock_datapipe_lockkey_state_cb,
},
{
.datapipe = &camera_button_state_pipe,
.input_cb = tklock_datapipe_camera_button_state_cb,
},
{
/* Note: Logically we should use output trigger for keyboard slide,
* but input triggering is used to avoid turning display
* on if mce happens to restart while keyboard is open.
* As long as the slide input is not filtered, there is
* no harm in this. */
.datapipe = &keyboard_slide_state_pipe,
.input_cb = tklock_datapipe_keyboard_slide_input_state_cb,
},
// sentinel
{
.datapipe = 0,
}
};
static datapipe_bindings_t tklock_datapipe_bindings =
{
.module = MODULE_NAME,
.handlers = tklock_datapipe_handlers,
};
/** Append triggers/filters to datapipes
*/
static void tklock_datapipe_init(void)
{
mce_datapipe_init_bindings(&tklock_datapipe_bindings);
}
/** Remove triggers/filters from datapipes
*/
static void tklock_datapipe_quit(void)
{
mce_datapipe_quit_bindings(&tklock_datapipe_bindings);
}
/* ========================================================================= *
* AUTOLOCK AFTER DEVICELOCK STATE MACHINE
* ========================================================================= */
/** Time limit for triggering autolock after display on */
static int64_t tklock_autolock_on_devlock_limit_trigger = 0;
/** Time limit for blocking autolock after lipstick startup */
static int64_t tklock_autolock_on_devlock_limit_block = 0;
/** Set autolock blocking limit after lipstick startup
*/
static void tklock_autolock_on_devlock_block(int duration_ms)
{
tklock_autolock_on_devlock_limit_block =
mce_lib_get_boot_tick() + duration_ms;
}
static void tklock_autolock_on_devlock_prime(void)
{
/* While we want to trap only device lock that happens immediately
* after unblanking the display, scheduling etc makes it difficult
* to specify some exact figure for "immediately".
*
* Since devicelock timeouts have granularity of 1 minute, assume
* that device locking that happens less than 60 seconds after
* unblanking was related to what happened during display off time. */
const int autolock_limit = 60 * 1000;
/* Do nothing during startup */
if( display_state_curr == MCE_DISPLAY_UNDEF )
goto EXIT;
/* Unprime if we are going to powered off state */
switch( display_state_next ) {
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_ON:
break;
default:
if( tklock_autolock_on_devlock_limit_trigger )
mce_log(LL_DEBUG, "autolock after devicelock: unprimed");
tklock_autolock_on_devlock_limit_trigger = 0;
goto EXIT;
}
/* Prime if we are coming from powered off state */
switch( display_state_curr ) {
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_ON:
break;
default:
if( !tklock_autolock_on_devlock_limit_trigger )
mce_log(LL_DEBUG, "autolock after devicelock: primed");
tklock_autolock_on_devlock_limit_trigger =
mce_lib_get_boot_tick() + autolock_limit;
break;
}
EXIT:
return;
}
static void tklock_autolock_on_devlock_trigger(void)
{
/* Device lock must be active */
if( devicelock_state != DEVICELOCK_STATE_LOCKED )
goto EXIT;
/* Not while handling calls or alarms */
switch( uiexception_type ) {
case UIEXCEPTION_TYPE_CALL:
case UIEXCEPTION_TYPE_ALARM:
goto EXIT;
default:
break;
}
/* Autolock time limit must be set and not reached yet */
if( !tklock_autolock_on_devlock_limit_trigger )
goto EXIT;
int64_t now = mce_lib_get_boot_tick();
if( now >= tklock_autolock_on_devlock_limit_trigger )
goto EXIT;
/* Autolock must not be blocked by recent lipstick restart */
if( now < tklock_autolock_on_devlock_limit_block )
goto EXIT;
/* We get here if: Device lock got applied right after
* display was powered up.
*
* Most likely the device lock should have been applied
* already when the display was off, but the devicelock
* timer did not trigger while the device was suspended.
*
* It is also possible that the last used application
* is still visible and active.
*
* Setting the tklock moves the application to background
* and lockscreen / devicelock is shown instead.
*/
mce_log(LL_DEBUG, "autolock after devicelock: triggered");
mce_datapipe_request_tklock(TKLOCK_REQUEST_ON);
EXIT:
return;
}
/* ========================================================================= *
* LID_SENSOR
* ========================================================================= */
/** Predicate for: Lid sensor is enabled
*
* It is assumed that any lid sensors present on the device are always
* enabled by default. The mce setting just makes mce either ignore or
* act on the change events that might or might not be coming in.
*
* @return true if lid state changes should be reacted to, false otherwise
*/
static bool tklock_lidsensor_is_enabled(void)
{
return lid_sensor_enabled;
}
/** Initialize lid sensor tracking
*
* Note: This must be called before installing datapipe callbacks.
*/
static void tklock_lidsensor_init(void)
{
/* Initialize state based on flag file presense */
tklock_lid_sensor_is_working =
(access(LID_SENSOR_IS_WORKING_FLAG_FILE, F_OK) == 0);
mce_log(LL_DEVEL, "lid_sensor_is_working = %s",
tklock_lid_sensor_is_working ? "true" : "false");
/* Broadcast initial state */
datapipe_exec_full(&lid_sensor_is_working_pipe,
GINT_TO_POINTER(tklock_lid_sensor_is_working));
}
/* ========================================================================= *
* LID_LIGHT
* ========================================================================= */
/** Convert lid light state to human readable string
*
* @param state lid state value
*
* @return human readable name of the state
*/
static const char *tklock_lidlight_repr(tklock_lidlight_t state)
{
const char *repr = "UNKNOWN";
switch( state ) {
case TKLOCK_LIDLIGHT_NA: repr = "NA"; break;
case TKLOCK_LIDLIGHT_LO: repr = "LO"; break;
case TKLOCK_LIDLIGHT_HI: repr = "HI"; break;
default: break;
}
return repr;
}
/** Convert lux value to lid light state
*
* @param lux lux value from light sensor
*
* @return corresponding lid state value
*/
static tklock_lidlight_t tklock_lidlight_from_lux(int lux)
{
/* Sensor is off? */
if( lux < 0 )
return TKLOCK_LIDLIGHT_NA;
/* Sensor does not see light? */
if( lux <= filter_lid_als_limit )
return TKLOCK_LIDLIGHT_LO;
/* It is not completely dark */
return TKLOCK_LIDLIGHT_HI;
}
/* ========================================================================= *
* LID_FILTER
* ========================================================================= */
/** Convert last seen lux value to lid light state
*
* @return lid state value corresponding with the latest reported lux value
*/
static tklock_lidlight_t tklock_lidfilter_map_als_state(void)
{
return tklock_lidlight_from_lux(light_sensor_actual);
}
/** Predicate for: ALS data is used for filtering Lid sensor state
*
* @return true if filtering should be done, false otherwise
*/
static bool tklock_lidfilter_is_enabled(void)
{
return tklock_lidsensor_is_enabled() && als_enabled && filter_lid_with_als;
}
/** Flag for: lid=closed + lux=low -> blank display */
static bool tklock_lidfilter_allow_close = false;
/** Allow/deny blanking if lid is closed in low light situation
*/
static void tklock_lidfilter_set_allow_close(bool allow)
{
if( tklock_lidfilter_allow_close != allow ) {
mce_log(LL_DEBUG, "allow_close: %s -> %s",
tklock_lidfilter_allow_close ? "true" : "false",
allow ? "true" : "false");
tklock_lidfilter_allow_close = allow;
}
}
/** Cached light sensor state */
static tklock_lidlight_t tklock_lidfilter_als_state = TKLOCK_LIDLIGHT_NA;
/** Set light sensor state
*
* @param state TKLOCK_LIDLIGHT_LO/HI/NA
*/
static void tklock_lidfilter_set_als_state(tklock_lidlight_t state)
{
if( tklock_lidfilter_als_state != state ) {
mce_log(LL_DEBUG, "als_state: %s -> %s",
tklock_lidlight_repr(tklock_lidfilter_als_state),
tklock_lidlight_repr(state));
tklock_lidfilter_als_state = state;
/* Check if futre lid close should be ignored or acted on */
tklock_lidfilter_rethink_allow_close();
}
/* If we know we have lo/hi light, stop waiting for als data */
if( tklock_lidfilter_als_state != TKLOCK_LIDLIGHT_NA )
tklock_lidfilter_set_wait_for_light(false);
/* If we know we have hi light, stop waiting for darkness*/
if( tklock_lidfilter_als_state == TKLOCK_LIDLIGHT_LO )
tklock_lidfilter_set_wait_for_dark(false);
}
/** Timer ID for: Stop waiting for lid close event */
static guint tklock_lidfilter_wait_for_close_id = 0;
/** Timer Callback for: Stop waiting for lid close event */
static gboolean tklock_lidfilter_wait_for_close_cb(gpointer aptr)
{
(void)aptr;
if( !tklock_lidfilter_wait_for_close_id )
goto EXIT;
mce_log(LL_DEBUG, "wait_close: timeout");
tklock_lidfilter_wait_for_close_id = 0;
tklock_lidfilter_set_als_state(TKLOCK_LIDLIGHT_NA);
tklock_lidfilter_set_allow_close(false);
/* Invalidate sensor data */
datapipe_exec_full(&lid_sensor_actual_pipe,
GINT_TO_POINTER(COVER_UNDEF));
EXIT:
return FALSE;
}
/** Predicate for: Waiting to see lid close event
*/
static bool tklock_lidfilter_get_wait_for_close(void)
{
return tklock_lidfilter_wait_for_close_id != 0;
}
/** Start/stop waiting for lid close event
*
* @param state true when expecting lid close, false otherwise
*
* Used when als drop is noticed while lid is not closed.
*
* If lid closes soon after, blank screen.
*
* Otherwise disable blanking until some light is seen.
*/
static void tklock_lidfilter_set_wait_for_close(bool state)
{
if( lid_sensor_actual != COVER_OPEN )
state = false;
if( display_state_next != MCE_DISPLAY_ON &&
display_state_next != MCE_DISPLAY_DIM )
state = false;
if( state == tklock_lidfilter_get_wait_for_close() )
goto EXIT;
mce_log(LL_DEBUG, "wait_close: %s", state ? "start" : "cancel");
if( state ) {
tklock_lidfilter_wait_for_close_id =
g_timeout_add(TKLOCK_LIDFILTER_SET_WAIT_FOR_CLOSE_DELAY,
tklock_lidfilter_wait_for_close_cb, 0);
}
else {
g_source_remove(tklock_lidfilter_wait_for_close_id),
tklock_lidfilter_wait_for_close_id = 0;
}
EXIT:
return;
}
/** Timer ID for: Stop waiting for ALS drop */
static guint tklock_lidfilter_wait_for_dark_id = 0;
/** Timer Callback for: Stop waiting for ALS drop
*/
static gboolean tklock_lidfilter_wait_for_dark_cb(gpointer aptr)
{
(void)aptr;
if( !tklock_lidfilter_wait_for_dark_id )
goto EXIT;
mce_log(LL_DEBUG, "wait_dark: timeout");
tklock_lidfilter_wait_for_dark_id = 0;
tklock_lidfilter_set_als_state(TKLOCK_LIDLIGHT_NA);
/* Invalidate sensor data */
datapipe_exec_full(&lid_sensor_actual_pipe,
GINT_TO_POINTER(COVER_UNDEF));
EXIT:
return FALSE;
}
/** Predicate for: Waiting for ALS drop to occur
*/
static bool tklock_lidfilter_get_wait_for_dark(void)
{
return tklock_lidfilter_wait_for_dark_id != 0;
}
/** Start/stop waiting for als drop event
*
* @param state true when expecting als drop, false otherwise
*
* Used when lid is closed in non-dark environment.
*
* If als level drops soon after, blank screen.
*
* Otherwise ignore lid state until it changes again.
*/
static void tklock_lidfilter_set_wait_for_dark(bool state)
{
if( state == tklock_lidfilter_get_wait_for_dark() )
goto EXIT;
mce_log(LL_DEBUG, "wait_dark: %s", state ? "start" : "cancel");
if( state ) {
tklock_lidfilter_wait_for_dark_id =
g_timeout_add(TKLOCK_LIDFILTER_SET_WAIT_FOR_DARK_DELAY,
tklock_lidfilter_wait_for_dark_cb, 0);
}
else {
g_source_remove(tklock_lidfilter_wait_for_dark_id),
tklock_lidfilter_wait_for_dark_id = 0;
}
EXIT:
return;
}
/** Timer ID for: Stop waiting for ALS data */
static guint tklock_lidfilter_wait_for_light_id = 0;
/** Timer callback for: Stop waiting for ALS data
*/
static gboolean tklock_lidfilter_wait_for_light_cb(gpointer aptr)
{
(void)aptr;
if( !tklock_lidfilter_wait_for_light_id )
goto EXIT;
mce_log(LL_DEBUG, "wait_light: timeout");
tklock_lidfilter_wait_for_light_id = 0;
tklock_lidfilter_set_als_state(tklock_lidfilter_map_als_state());
tklock_lidpolicy_rethink();
EXIT:
return FALSE;
}
/** Predicate for: Waiting for ALS data
*/
static bool tklock_lidfilter_get_wait_for_light(void)
{
return tklock_lidfilter_wait_for_light_id != 0;
}
/** Start/stop waiting for als change event
*
* @param state true when expecting als change, false otherwise
*
* Used when lid is opened and we need to wait for als powerup.
*
* If als reports light soon after, unblank screen.
*
* Otherwise leave display state as it were.
*/
static void tklock_lidfilter_set_wait_for_light(bool state)
{
if( state == tklock_lidfilter_get_wait_for_light() )
goto EXIT;
mce_log(LL_DEBUG, "wait_light: %s", state ? "start" : "cancel");
if( state ) {
tklock_lidfilter_wait_for_light_id =
g_timeout_add(TKLOCK_LIDFILTER_SET_WAIT_FOR_LIGHT_DELAY,
tklock_lidfilter_wait_for_light_cb, 0);
tklock_lidfilter_set_als_state(TKLOCK_LIDLIGHT_NA);
tklock_lidpolicy_rethink();
}
else {
g_source_remove(tklock_lidfilter_wait_for_light_id),
tklock_lidfilter_wait_for_light_id = 0;
}
EXIT:
return;
}
/** React to end of temporary als poll periods
*/
static void tklock_lidfilter_rethink_als_poll(void)
{
// when als polling stops, we must stop waiting for light level
if( !light_sensor_polling ) {
tklock_lidfilter_set_wait_for_light(false);
tklock_lidfilter_rethink_als_state();
}
}
/** Update allow-close flag based on display state and logical als state
*/
static void tklock_lidfilter_rethink_allow_close(void)
{
switch( display_state_curr ) {
case MCE_DISPLAY_POWER_UP:
/* After display power cycling we need to see a high lux value
* before lid close can be used for display blanking again. */
tklock_lidfilter_set_allow_close(false);
/* Display power up while sensor is in closed state. Assume this
* is due to user pressing power key and ignore the lid sensor
* state until further changes are received. */
if( lid_sensor_actual == COVER_CLOSED ) {
mce_log(LL_DEVEL, "unblank while lid closed; ignore lid");
datapipe_exec_full(&lid_sensor_actual_pipe,
GINT_TO_POINTER(COVER_UNDEF));
}
break;
case MCE_DISPLAY_ON:
case MCE_DISPLAY_DIM:
case MCE_DISPLAY_LPM_ON:
if( tklock_lidfilter_als_state == TKLOCK_LIDLIGHT_HI )
tklock_lidfilter_set_allow_close(true);
break;
default:
break;
}
}
/** Re-evaluate reaction to lid sensor state
*/
static void tklock_lidfilter_rethink_lid_state(void)
{
if( !tklock_lidfilter_is_enabled() ) {
tklock_lidfilter_set_wait_for_dark(false);
tklock_lidfilter_set_wait_for_light(false);
tklock_lidfilter_set_wait_for_close(false);
goto EXIT;
}
/* Keep ALS powered up for a while after lid state change */
if( lid_sensor_actual != COVER_UNDEF ) {
datapipe_exec_full(&light_sensor_poll_request_pipe,
GINT_TO_POINTER(TRUE));
}
switch( lid_sensor_actual ) {
case COVER_OPEN:
tklock_lidfilter_set_wait_for_dark(false);
tklock_lidfilter_set_wait_for_light(true);
break;
case COVER_CLOSED:
tklock_lidfilter_set_wait_for_light(false);
if( tklock_lidfilter_get_wait_for_close() )
tklock_lidfilter_set_wait_for_close(false);