#include "shared/plugin.h"
#define DISPLAY_CONF_FILE "/etc/deviced/display.conf"
+#define POWERLOCK_CONF_FILE "/etc/deviced/powerlock.conf"
#ifndef VCONFKEY_HOMESCREEN_TUTORIAL_OOBE_ENABLED
#define VCONFKEY_HOMESCREEN_TUTORIAL_OOBE_ENABLED "db/private/com.samsung.w-home/tutorial_oobe_enabled"
(b.tv_sec * 1000000 + b.tv_usec)) \
/ 1000)
+#define KILLABLE_DAEMON_LOCK_LIMIT 1800 /* seconds, 30min */
+#define FORCE_RELEASE_LOCK_INTERVAL 5 /* seconds */
+
+static void get_comm(pid_t pid, char *comm);
+static bool is_killable_daemon(pid_t pid);
+static gboolean pmlock_terminate_daemon_to_release_lock(gpointer data);
+static int pmlock_check(void *data);
+static int powerlock_load_config(struct parse_result *result, void *user_data);
+static void free_killable_daemon_list(void);
+
+static dd_list *display_lock_killable_daemon;
+static bool initialized_killable_daemon_list;
+
static struct display_config display_conf = {
.lock_wait_time = LOCK_SCREEN_WATING_TIME,
.longpress_interval = LONG_PRESS_INTERVAL,
.timeout_enable = true,
.input_support = true,
.lockcheck_timeout = 600,
+ .pmlock_check = pmlock_check,
.aod_enter_level = 40,
.aod_tsp = true,
.touch_wakeup = false,
return UNKNOWN_STR;
}
+static void get_comm(pid_t pid, char *comm)
+{
+ char buf[PATH_MAX];
+ int fd, r;
+
+ if (pid >= INTERNAL_LOCK_BASE)
+ snprintf(buf, PATH_MAX, "/proc/%d/comm", getpid());
+ else
+ snprintf(buf, PATH_MAX, "/proc/%d/comm", pid);
+
+ fd = open(buf, O_RDONLY);
+ if (fd < 0) {
+ comm[0] = '\0';
+ _E("Process(%d) does not exist now(may be dead without unlock).", pid);
+ return;
+ }
+
+ r = read(fd, comm, PATH_MAX);
+ if ((r > 0) && (r < PATH_MAX)) {
+ if (comm[r - 1] == '\n')
+ comm[r - 1] = '\0';
+ else
+ comm[r] = '\0';
+ } else
+ comm[0] = '\0';
+
+ close(fd);
+}
+
+static bool is_killable_daemon(pid_t pid)
+{
+ char pname[PATH_MAX] = {0, };
+ const char *blacklist;
+ dd_list *l;
+
+ get_comm(pid, pname);
+ if (!(*pname))
+ return false;
+
+ DD_LIST_FOREACH(display_lock_killable_daemon, l, blacklist) {
+ if (MATCH(pname, blacklist))
+ return true;
+ }
+
+ return false;
+}
+
+static gboolean pmlock_terminate_daemon_to_release_lock(gpointer data)
+{
+ pid_t pid;
+ PmLockNode *node;
+ enum state_t state;
+ int ret;
+ bool pid_exist;
+ char pname[PATH_MAX] = {0, };
+
+ if (!data) {
+ _E("Invalid parameter.");
+ return G_SOURCE_REMOVE;
+ }
+
+ node = (PmLockNode *) data;
+ state = node->state;
+ pid = node->pid;
+
+ if (pid <= 0) {
+ _E("Invalid lock pid.");
+ del_node(state, node);
+ return G_SOURCE_REMOVE;
+ }
+
+ if (!node->killable_daemon) {
+ _E("Incorrect checker, this is not a killable daemon. Stop checking lock.");
+ return G_SOURCE_REMOVE;
+ }
+
+ pid_exist = (kill(pid, 0) == 0);
+ if (pid_exist)
+ get_comm(pid, pname);
+
+ if (node->force_release == false) {
+ /* Stop checking lock if process had been terminated */
+ if (!pid_exist) {
+ del_node(state, node);
+ _I("Process %d not found. Stop checking lock.", pid);
+ return G_SOURCE_REMOVE;
+ }
+
+ /* KILLABLE_DAEMON_LOCK_LIMIT is expired. Kill the daemon */
+ CRITICAL_LOG("%s(%d) holds %s lock for %ds. kill SIGTERM.",
+ *pname ? pname : "Unknown", pid, states[state].name, KILLABLE_DAEMON_LOCK_LIMIT);
+ ret = kill(pid, SIGTERM);
+ if (ret < 0)
+ CRITICAL_LOG("Failed to send SIGTERM to process %s(%d), %d.",
+ *pname ? pname : "Unknown", pid, errno);
+
+ node->force_release = true;
+ g_timeout_add_seconds(FORCE_RELEASE_LOCK_INTERVAL,
+ pmlock_terminate_daemon_to_release_lock, (gpointer)node);
+ } else if (node->force_release == true) {
+ /* kill confirmation */
+ if (pid_exist) {
+ CRITICAL_LOG("%s(%d) is still alive, kill SIGKILL.",
+ *pname ? pname : "Unknown", pid);
+
+ ret = kill(pid, SIGKILL);
+ if (ret < 0)
+ CRITICAL_LOG("Failed to kill process %s(%d), %d.",
+ *pname ? pname : "Unknown", pid, errno);
+ }
+
+ /* release lock */
+ CRITICAL_LOG("Release %s lock occupied by PID %d.", states[state].name, pid);
+ del_node(state, node);
+ set_unlock_time(pid, state);
+
+ if (!timeout_src_id)
+ states[get_pm_cur_state()].trans(EVENT_TIMEOUT);
+ }
+
+ return G_SOURCE_REMOVE;
+}
+
+static int pmlock_check(void *data)
+{
+ PmLockNode *node;
+ int ret;
+
+ assert(data);
+ node = (PmLockNode *) data;
+
+ if (!initialized_killable_daemon_list) {
+ ret = config_parse(POWERLOCK_CONF_FILE, powerlock_load_config, NULL);
+ /* config file may not exist */
+ if (ret < 0 && ret != -ENOENT)
+ _W("Failed to load %s, %d.", POWERLOCK_CONF_FILE, ret);
+
+ if (status == DEVICE_OPS_STATUS_UNINIT) {
+ _W("Lock request before display init. Preloaded killable list.");
+ initialized_killable_daemon_list = true;
+ } else if (status == DEVICE_OPS_STATUS_STOP) {
+ _W("Lock request after display stop. Loaded list will be freed immediately.");
+ node->killable_daemon = is_killable_daemon(node->pid);
+ free_killable_daemon_list();
+ goto killable_marked;
+ }
+ }
+
+ node->killable_daemon = is_killable_daemon(node->pid);
+
+killable_marked:
+ /* use default lock checker */
+ if (!node->killable_daemon)
+ return 0;
+
+ return g_timeout_add_seconds(KILLABLE_DAEMON_LOCK_LIMIT,
+ pmlock_terminate_daemon_to_release_lock, (gpointer)node);
+}
+
+static void free_killable_daemon_list(void)
+{
+ dd_list *l, *l_next;
+ char *blacklist;
+
+ if (!display_lock_killable_daemon)
+ return;
+
+ DD_LIST_FOREACH_SAFE(display_lock_killable_daemon, l, l_next, blacklist) {
+ DD_LIST_REMOVE(display_lock_killable_daemon, blacklist);
+ free(blacklist);
+ }
+ display_lock_killable_daemon = NULL;
+ initialized_killable_daemon_list = false;
+}
+
static unsigned long get_lcd_on_flags(void)
{
unsigned long flags = NORMAL_MODE;
holdkey_block = GET_COND_FLAG(data->cond) & PM_FLAG_BLOCK_HOLDKEY;
tmp = find_node(state, pid);
- if (!tmp)
- add_node(state, pid, cond_timeout_id, holdkey_block);
- else {
+ if (!tmp) {
+ tmp = add_node(state, pid, cond_timeout_id, holdkey_block);
+ if (!tmp) {
+ _E("Failed to acquire lock, state: %d, pid: %d.", state, pid);
+ return;
+ }
+ } else {
update_lock_timer(data, tmp, cond_timeout_id);
tmp->holdkey_block = holdkey_block;
}
}
}
- _SD("be requested LOCK info pname(%s), holdkeyblock(%d) flags(%d)",
- pname, holdkey_block, flags);
+ _SD("be requested LOCK info pname(%s), holdkeyblock(%d) flags(%d) killable_daemon(%d)",
+ pname, holdkey_block, flags, tmp->killable_daemon);
set_lock_time(pid, pname, state);
device_notify(DEVICE_NOTIFIER_DISPLAY_LOCK, (void *)&value);
return 0;
}
+static int powerlock_load_config(struct parse_result *result, void *user_data)
+{
+ char *name = NULL;
+
+ _D("powerlock_load_config: section=%s name=%s value=%s", result->section, result->name, result->value);
+
+ if (MATCH(result->section, "KillableDaemon") && MATCH(result->name, "KillableList")) {
+ name = strndup(result->value, PATH_MAX - 1);
+ if (!name) {
+ _E("Not enough memory.");
+ return -ENOMEM;
+ }
+
+ CRITICAL_LOG("Add %s to killable daemon list.", name);
+ DD_LIST_APPEND(display_lock_killable_daemon, name);
+ }
+
+ return 0;
+}
+
static gboolean delayed_init_dpms(gpointer data)
{
int timeout;
_W("Failed to load '%s', use default value: %d",
DISPLAY_CONF_FILE, ret);
+ ret = config_parse(POWERLOCK_CONF_FILE, powerlock_load_config, NULL);
+ /* config file may not exist */
+ if (ret < 0 && ret != -ENOENT)
+ _W("Failed to load %s, %d.", POWERLOCK_CONF_FILE, ret);
+ initialized_killable_daemon_list = true;
+
register_kernel_uevent_control(&lcd_uevent_ops);
register_kernel_uevent_control(&sec_dsim_uevent_ops);
exit_lcd_operation();
free_lock_info_list();
+ free_killable_daemon_list();
/* free display service */
display_service_free();
static dd_list *cond_head[S_END];
static int trans_condition;
+static struct display_config *display_conf;
+
bool check_lock_state(int state)
{
dd_list *elem;
trans_condition |= MASK_OFF;
}
-static void pmlock_check_cb(GVariant *var, void *user_data, GError *err)
+static void default_pmlock_check_cb(GVariant *var, void *user_data, GError *err)
{
pid_t pid = 0;
int ret, detected = 0;
DEVICED_PATH_DISPLAY,
DEVICED_INTERFACE_DISPLAY,
"pmlock_expired",
- g_variant_new("(i)", pid));
+ g_variant_new("(ii)", pid, (int)diff));
if (ret < 0)
_E("Failed to send dbus pmlock_expired");
g_variant_unref(var);
}
-static gboolean pmlock_check(void *data)
+static gboolean default_pmlock_check(void *data)
{
const char *arr[2];
char chr_pid[PID_MAX];
PmLockNode *node;
- GVariant *v;
enum state_t state;
pid_t pid;
int ret;
return G_SOURCE_REMOVE;
}
- v = (GVariant*)data;
- g_variant_get(v, "(ii)", &state, &pid);
+ node = (PmLockNode *) data;
+ state = node->state;
+ pid = node->pid;
if (state == S_LCDOFF && backlight_ops->get_lcd_power() == DPMS_ON) {
_D("Lcd state is PM_LCD_POWER_ON");
break;
default:
_E("Invalid state.");
- g_variant_unref(v);
return G_SOURCE_REMOVE;
}
RESOURCED_PATH_PROCESS,
RESOURCED_INTERFACE_PROCESS,
METHOD_APP_STATUS,
- "is", arr, pmlock_check_cb, -1, (void *)(intptr_t)state);
+ "is", arr, default_pmlock_check_cb, -1, (void *)(intptr_t)state);
if (ret < 0)
_E("Failed to call dbus method");
PmLockNode *add_node(enum state_t s_index, pid_t pid, guint timeout_id,
bool holdkey_block)
{
- guint warning_id = 0;
PmLockNode *n;
- GVariant *v = NULL;
time_t now;
- struct display_config *display_conf = get_display_config();
- if (!display_conf) {
- _E("Failed to get display configuration.");
- return NULL;
- }
- n = (PmLockNode *) malloc(sizeof(PmLockNode));
+ assert(display_conf);
+
+ n = (PmLockNode *) calloc(1, sizeof(PmLockNode));
if (n == NULL) {
- _E("Not enough memory, add cond. fail");
+ _E("Not enough memory, failed to alloc lock node.");
return NULL;
}
- if (pid < INTERNAL_LOCK_BASE) {
- v = g_variant_new("(ii)", s_index, pid);
- if (v) {
- warning_id = g_timeout_add_seconds(display_conf->lockcheck_timeout,
- pmlock_check, (void *)v);
- } else {
- _E("Failed to make GVariant.");
- }
- }
-
time(&now);
+ n->state = s_index;
n->pid = pid;
n->timeout_id = timeout_id;
- n->warning_id = warning_id;
- n->warning_param = v;
n->time = now;
n->holdkey_block = holdkey_block;
n->background = false;
n->broadcast_warning = true;
+
+ if (pid < INTERNAL_LOCK_BASE) {
+ /* check if this lock node needs custom-defined lock checker.
+ * n->warning_id would be 0 if fails to register the checker,
+ * or there is no need to use that checker */
+ if (display_conf->pmlock_check)
+ n->warning_id = display_conf->pmlock_check(n);
+
+ /* use default lock checker */
+ if (!n->warning_id)
+ n->warning_id = g_timeout_add_seconds(display_conf->lockcheck_timeout,
+ default_pmlock_check, (gpointer)n);
+ }
+
DD_LIST_APPEND(cond_head[s_index], n);
refresh_app_cond();
backlight_ops = get_backlight_ops();
if (!backlight_ops)
_E("Failed to get backlight operator.");
+
+ display_conf = get_display_config();
+ if (!display_conf) {
+ _E("Failed to get display config.");
+ }
}