Name: deviced
Summary: Deviced
-Version: 10.0.0
+Version: 10.0.1
Release: 1
Group: System/Management
License: Apache-2.0
INCLUDE(FindPkgConfig)
pkg_check_modules(libpkgs REQUIRED
+ glib-2.0
+ capi-system-device
libsyscommon)
FOREACH(flag ${libpkgs_CFLAGS})
* limitations under the License.
*/
+#include <string.h>
+#include <glib.h>
#include <shared/common.h>
#include <shared/devices.h>
#include <libsyscommon/libgdbus.h>
+#include <device/power.h>
-#include "core/log.h"
+#include "shared/log-macro.h"
+#include "power/power-lock.h"
static GVariant *dbus_lockstate(GDBusConnection *conn,
const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
{
- return g_variant_new("(i)", 0);
+ char *state_str;
+ char *option1_str;
+ char *option2_str;
+ int timeout;
+ pid_t pid;
+ int ret = 0;
+
+ g_variant_get(param, "(sssi)", &state_str, &option1_str, &option2_str, &timeout);
+
+ if (!state_str || timeout < 0) {
+ _E("message is invalid!");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!strncmp(state_str, "privilege check", sizeof("privilege check")))
+ goto out;
+
+ pid = gdbus_connection_get_sender_pid(conn, sender);
+ if (pid == -1 || kill(pid, 0) == -1) {
+ _E("%d process does not exist, dbus ignored!", pid);
+ ret = -ESRCH;
+ goto out;
+ }
+
+ if (strncmp(state_str, "lcdoff", sizeof("lcdoff"))) {
+ _E("Headless doesn't support lock other than lcdoff");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /**
+ * Delegate the lock request to power module instead of display module
+ * when it comes to a headless profile.
+ */
+ ret = power_lock_acquire_lock(pid, timeout);
+
+out:
+ g_free(state_str);
+ g_free(option1_str);
+ g_free(option2_str);
+ return g_variant_new("(i)", ret);
}
static GVariant *dbus_unlockstate(GDBusConnection *conn,
const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
{
- return g_variant_new("(i)", 0);
+ char *state_str;
+ char *option_str;
+ pid_t pid;
+ int ret = 0;
+
+ g_variant_get(param, "(ss)", &state_str, &option_str);
+
+ if (!state_str) {
+ _E("message is invalid!");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ if (!strncmp(state_str, "privilege check", sizeof("privilege check")))
+ goto out;
+
+ pid = gdbus_connection_get_sender_pid(conn, sender);
+ if (pid == -1 || kill(pid, 0) == -1) {
+ _E("%d process does not exist, dbus ignored!", pid);
+ ret = -ESRCH;
+ goto out;
+ }
+
+ if (strncmp(state_str, "lcdoff", sizeof("lcdoff"))) {
+ _E("Headless doesn't support lock other than lcdoff");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /**
+ * Delegate the unlock request to power module instead of display module
+ * when it comes to a headless profile.
+ */
+ ret = power_lock_release_lock(pid);
+out:
+ g_free(state_str);
+ g_free(option_str);
+ return g_variant_new("(i)", ret);
+}
+
+static GVariant *dbus_pmlockgetlockcount(GDBusConnection *conn,
+ const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
+ GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
+{
+ power_lock_e power_lock_type;
+ int ret;
+
+ g_variant_get(param, "(i)", &power_lock_type);
+
+ if (power_lock_type != POWER_LOCK_CPU) {
+ _W("Invalid parameter power lock type=(%d)", power_lock_type);
+ ret = -EINVAL;
+ goto out;
+ }
+
+ power_lock_get_lock_count(&ret);
+
+out:
+ return g_variant_new("(i)", ret);
}
static const dbus_method_s dbus_methods[] = {
- { "lockstate", "sssi", "i", dbus_lockstate },
- { "unlockstate", "ss", "i", dbus_unlockstate }
+ { "lockstate", "sssi", "i", dbus_lockstate },
+ { "unlockstate", "ss", "i", dbus_unlockstate },
+ { "PmlockGetLockCount", "i", "i", dbus_pmlockgetlockcount},
};
static const dbus_interface_u dbus_interface = {
#include "display-lock.h"
#include "shared/log-macro.h"
#include "shared/apps.h"
+#include "power/power-lock.h"
#define METHOD_APP_STATUS "CheckAppStatus"
#define PID_MAX 6
num_of_pmlock = g_list_length(cond_head[state]);
/* broadcast on every change of lock count */
- gdbus_signal_emit(NULL,
- DEVICED_PATH_DISPLAY,
- DEVICED_INTERFACE_DISPLAY,
- DEVICED_SIGNAL_POWER_LOCK_COUNT_CHANGED,
- g_variant_new("(ii)", power_lock_type, num_of_pmlock));
+ power_lock_broadcast_lock_count(power_lock_type, num_of_pmlock);
if (num_of_pmlock > 1)
return;
#include "shared/device-notifier.h"
#include "power-suspend.h"
+#include "power-lock.h"
#include "power.h"
-#ifndef PROCESS_CHECK_TIMEOUT
-#define PROCESS_CHECK_TIMEOUT 600000 /* milisecond, 10 minute */
-#endif
-
-static GList *lock_list;
-
-struct lock_node {
- pid_t pid;
- char comm[64];
- int timer_id;
- int timeout;
- char locktime[64];
-};
-
static uint64_t convert_device_state_to_deviced(device_power_state_e state)
{
if (state == DEVICE_POWER_STATE_START)
return deviced_bitmap;
}
-static void print_lock_node(void)
-{
- GList *elem;
- struct lock_node *node;
-
- _D("List of remaining CPU locks");
- SYS_G_LIST_FOREACH(lock_list, elem, node)
- _D(" pid=%d(%s), locktime=%s, timeout=%dms",
- node->pid, node->comm, node->locktime, node->timeout);
-}
-
-static void remove_lock_node(struct lock_node *node)
-{
- if (!node)
- return;
-
- if (node->timer_id) {
- g_source_remove(node->timer_id);
- node->timer_id = 0;
- }
-
- SYS_G_LIST_REMOVE(lock_list, node);
- free(node);
-
- /* Check the lock_list is empty after removing the node.
- * If it is, then request wake unlock */
- if (SYS_G_LIST_LENGTH(lock_list) == 0)
- power_release_wakelock();
- else
- print_lock_node();
-}
-
-static gboolean lock_expired_cb(void *data)
-{
- struct lock_node *this = (struct lock_node *) data;
-
- _D("Powerlock of pid=%d(%s) for %dms is expired", this->pid, this->comm, this->timeout);
- remove_lock_node(this);
-
- return G_SOURCE_REMOVE;
-}
-
-/* check existance of process for every PROCESS_CHECK_TIMEOUT
- * if the process has requested infinite CPU lock */
-static gboolean process_check_cb(void *data)
-{
- struct lock_node *this = (struct lock_node *) data;
-
- if (kill(this->pid, 0) != 0) {
- _D("pid=%d(%s) is not found, deviced release the CPU lock", this->pid, this->comm);
- remove_lock_node(this);
- return G_SOURCE_REMOVE;
- }
-
- return G_SOURCE_CONTINUE;
-}
-
static GVariant *dbus_power_lock_cpu(GDBusConnection *conn,
const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
int timeout;
int ret = 0;
pid_t pid;
- GList *elem;
- struct lock_node *node;
-
- time_t current;
- struct tm current_tm;
- struct tm *rettm;
/* lock timeout in milisecond */
g_variant_get(param, "(i)", &timeout);
goto out;
}
- SYS_G_LIST_FOREACH(lock_list, elem, node) {
- if (node->pid == pid)
- break;
- }
-
- if (!node) {
- int retval;
- char commpath[128];
- char *sep;
-
- node = calloc(1, sizeof(struct lock_node));
- if (!node) {
- ret = -ENOMEM;
- goto out;
- }
-
- node->pid = pid;
- snprintf(commpath, sizeof(commpath), "/proc/%d/comm", pid);
- retval = sys_get_str(commpath, node->comm, sizeof(node->comm));
- if (retval != 0)
- snprintf(node->comm, sizeof(node->comm), "Unknown");
-
- /* remove whitesapce */
- sep = strpbrk(node->comm, " \n\t\r");
- if (sep)
- *sep = '\0';
-
- SYS_G_LIST_APPEND(lock_list, node);
- }
-
- /* set current time for logging */
- current = time(NULL);
- rettm = localtime_r(¤t, ¤t_tm);
- if (!rettm)
- snprintf(node->locktime, sizeof(node->locktime), "Unknown");
- else
- strftime(node->locktime, sizeof(node->locktime), "%Y-%m-%d %H:%M:%S", ¤t_tm);
-
- node->timeout = timeout;
-
- if (node->timer_id) {
- g_source_remove(node->timer_id);
- node->timer_id = 0;
- }
-
- _D("pid=%d(%s) request CPU lock for %dms", pid, node->comm, timeout);
- power_request_change_state(DEVICED_POWER_STATE_NORMAL, HAL_DEVICE_POWER_TRANSITION_REASON_POWER_LOCK);
-
- if (timeout > 0)
- node->timer_id = g_timeout_add(timeout, lock_expired_cb, node);
- else if (timeout == 0) /* endless CPU lock: add checker that monitors the process*/
- node->timer_id = g_timeout_add(PROCESS_CHECK_TIMEOUT, process_check_cb, node);
+ ret = power_lock_acquire_lock(pid, timeout);
out:
return g_variant_new("(i)", ret);
{
int ret = 0;
pid_t pid;
- GList *elem;
- struct lock_node *node;
pid = gdbus_connection_get_sender_pid(conn, sender);
if (pid == -1 || kill(pid, 0) == -1) {
goto out;
}
- SYS_G_LIST_FOREACH(lock_list, elem, node) {
- if (node->pid == pid)
- break;
- }
-
- if (node) {
- _D("pid=%d(%s) release CPU lock", node->pid, node->comm);
- remove_lock_node(node);
- }
+ ret = power_lock_release_lock(pid);
out:
return g_variant_new("(i)", ret);
--- /dev/null
+/*
+ * deviced
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include <stdio.h>
+#include <glib.h>
+#include <system_info.h>
+#include <libsyscommon/libgdbus.h>
+#include <libsyscommon/list.h>
+#include <libsyscommon/file.h>
+#include <device/power.h>
+#include <hal/device/hal-device-power.h>
+
+#include "power.h"
+#include "power-suspend.h"
+#include "power-lock.h"
+#include "shared/log-macro.h"
+
+#ifndef PROCESS_CHECK_TIMEOUT
+#define PROCESS_CHECK_TIMEOUT 600000 /* milisecond, 10 minute */
+#endif
+
+static GList *lock_list;
+
+struct lock_node {
+ pid_t pid;
+ char comm[64];
+ int timer_id;
+ int timeout;
+ char locktime[64];
+};
+
+static int is_headed_profile(bool *profile)
+{
+ static int initialized = false;
+ static bool is_headed = false;
+ int ret;
+
+ if (!profile)
+ return -EINVAL;
+
+ if (initialized) {
+ *profile = is_headed;
+ return 0;
+ }
+
+ ret = system_info_get_platform_bool("http://tizen.org/feature/display", &is_headed);
+ if (ret != SYSTEM_INFO_ERROR_NONE)
+ return -EINVAL;
+
+ initialized = true;
+
+ return 0;
+}
+
+static void print_lock_node(void)
+{
+ GList *elem;
+ struct lock_node *node;
+
+ _D("List of remaining CPU locks");
+ SYS_G_LIST_FOREACH(lock_list, elem, node)
+ _D(" pid=%d(%s), locktime=%s, timeout=%dms",
+ node->pid, node->comm, node->locktime, node->timeout);
+}
+
+static void remove_lock_node(struct lock_node *node)
+{
+ if (!node)
+ return;
+
+ if (node->timer_id) {
+ g_source_remove(node->timer_id);
+ node->timer_id = 0;
+ }
+
+ SYS_G_LIST_REMOVE(lock_list, node);
+ free(node);
+
+ power_lock_broadcast_lock_count(POWER_LOCK_CPU, SYS_G_LIST_LENGTH(lock_list));
+
+ /* Check the lock_list is empty after removing the node.
+ * If it is, then request wake unlock */
+ if (SYS_G_LIST_LENGTH(lock_list) == 0)
+ power_release_wakelock();
+ else
+ print_lock_node();
+}
+
+static gboolean lock_expired_cb(void *data)
+{
+ struct lock_node *this = (struct lock_node *) data;
+
+ _D("Powerlock of pid=%d(%s) for %dms is expired", this->pid, this->comm, this->timeout);
+ remove_lock_node(this);
+
+ return G_SOURCE_REMOVE;
+}
+
+/* check existance of process for every PROCESS_CHECK_TIMEOUT
+ * if the process has requested infinite CPU lock */
+static gboolean process_check_cb(void *data)
+{
+ struct lock_node *this = (struct lock_node *) data;
+
+ if (kill(this->pid, 0) != 0) {
+ _D("pid=%d(%s) is not found, deviced release the CPU lock", this->pid, this->comm);
+ remove_lock_node(this);
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+int power_lock_broadcast_lock_count(int power_lock_type, int count)
+{
+ int ret;
+ bool is_headed = false;
+
+ if (power_lock_type < POWER_LOCK_CPU || power_lock_type > POWER_LOCK_DISPLAY_DIM)
+ return -EINVAL;
+
+ if (count < 0)
+ return -EINVAL;
+
+ ret = is_headed_profile(&is_headed);
+ if (ret < 0) {
+ _E("Failed to find profile, headed or headless. Suppress lock count signal broadcasting.");
+ return -EINVAL;
+ }
+
+ if (is_headed && power_lock_type != POWER_LOCK_CPU)
+ _W("Lock type other than POWER_LOCK_CPU has no effect on headless profile. But broadcast it anyway.");
+
+ gdbus_signal_emit(NULL,
+ DEVICED_PATH_DISPLAY,
+ DEVICED_INTERFACE_DISPLAY,
+ DEVICED_SIGNAL_POWER_LOCK_COUNT_CHANGED,
+ g_variant_new("(ii)", power_lock_type, count));
+
+ return 0;
+}
+
+/**
+ * FIXME: It is strongly discoraged to use this function
+ * at the profile other than headless.
+ */
+int power_lock_acquire_lock(pid_t pid, int timeout)
+{
+ GList *elem;
+ struct lock_node *node;
+ time_t current;
+ struct tm current_tm;
+ struct tm *rettm;
+
+ SYS_G_LIST_FOREACH(lock_list, elem, node) {
+ if (node->pid == pid)
+ break;
+ }
+
+ if (!node) {
+ int retval;
+ char commpath[128];
+ char *sep;
+
+ node = calloc(1, sizeof(struct lock_node));
+ if (!node)
+ return -ENOMEM;
+
+ node->pid = pid;
+ snprintf(commpath, sizeof(commpath), "/proc/%d/comm", pid);
+ retval = sys_get_str(commpath, node->comm, sizeof(node->comm));
+ if (retval != 0)
+ snprintf(node->comm, sizeof(node->comm), "Unknown");
+
+ /* remove whitesapce */
+ sep = strpbrk(node->comm, " \n\t\r");
+ if (sep)
+ *sep = '\0';
+
+ SYS_G_LIST_APPEND(lock_list, node);
+ power_lock_broadcast_lock_count(POWER_LOCK_CPU, SYS_G_LIST_LENGTH(lock_list));
+ }
+
+ /* set current time for logging */
+ current = time(NULL);
+ rettm = localtime_r(¤t, ¤t_tm);
+ if (!rettm)
+ snprintf(node->locktime, sizeof(node->locktime), "Unknown");
+ else
+ strftime(node->locktime, sizeof(node->locktime), "%Y-%m-%d %H:%M:%S", ¤t_tm);
+
+ node->timeout = timeout;
+
+ if (node->timer_id) {
+ g_source_remove(node->timer_id);
+ node->timer_id = 0;
+ }
+
+ _D("pid=%d(%s) request CPU lock for %dms", pid, node->comm, timeout);
+ power_request_change_state(DEVICED_POWER_STATE_NORMAL, HAL_DEVICE_POWER_TRANSITION_REASON_POWER_LOCK);
+
+ if (timeout > 0)
+ node->timer_id = g_timeout_add(timeout, lock_expired_cb, node);
+ else if (timeout == 0) /* endless CPU lock: add checker that monitors the process*/
+ node->timer_id = g_timeout_add(PROCESS_CHECK_TIMEOUT, process_check_cb, node);
+
+ return 0;
+}
+
+/**
+ * FIXME: It is strongly discoraged to use this function
+ * at the profile other than headless.
+ */
+int power_lock_release_lock(pid_t pid)
+{
+ GList *elem;
+ struct lock_node *node;
+
+ SYS_G_LIST_FOREACH(lock_list, elem, node) {
+ if (node->pid == pid)
+ break;
+ }
+
+ if (node) {
+ _D("pid=%d(%s) release CPU lock", node->pid, node->comm);
+ remove_lock_node(node);
+ }
+
+ return 0;
+}
+
+/**
+ * FIXME: It is strongly discoraged to use this function
+ * at the profile other than headless.
+ */
+int power_lock_get_lock_count(int *count)
+{
+ if (!count)
+ return -EINVAL;
+
+ *count = SYS_G_LIST_LENGTH(lock_list);
+
+ return 0;
+}
--- /dev/null
+/*
+ * deviced
+ *
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __POWER_LOCK_H__
+#define __POWER_LOCK_H__
+
+#include <unistd.h>
+#include <sys/types.h>
+
+int power_lock_broadcast_lock_count(int power_lock_type, int count);
+
+/**
+ * FIXME: IT IS STRONGLY DISCOURAGED TO USE BELOW FUNCTIONS AT THE
+ * PROFILE OTHER THAN HEADLESS.
+ *
+ * These functions are implemented only for the headless profile.
+ * The headless profile has no display module so it is impossible
+ * to control lock via display, which is the way how the current deviced's
+ * locking mechanism has been working. Therefore it was necessary to
+ * give another way for healdess profile to use lock, so these functions
+ * have been newly introduced. However, it has not been fully taken into
+ * consideration what side effect would be when it operates with headed,
+ * so you might NOT use these functions at the profile other than headless.
+ *
+ * Instead, the profiles other than headless can use display lock functions
+ * below to get an equivalent effect.
+ * • pm_lock_internal()
+ * • pm_unlock_internal()
+ * • pmlock_get_lock_count()
+ */
+int power_lock_acquire_lock(pid_t pid, int timeout);
+int power_lock_release_lock(pid_t pid);
+int power_lock_get_lock_count(int *count);
+
+#endif /* __POWER_LOCK_H__ */
+