4 * Copyright (c) 2022 Samsung Electronics Co., Ltd.
6 * Licensed under the Apache License, Version 2.0 (the License);
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
24 #include <libsyscommon/libgdbus.h>
25 #include <libsyscommon/list.h>
26 #include <libsyscommon/file.h>
27 #include <device/power-internal.h>
29 #include "shared/devices.h"
30 #include "shared/log.h"
31 #include "shared/device-notifier.h"
33 #include "power/power-control.h"
34 #include "power-state-manager.h"
35 #include "power-state-wait.h"
37 #ifndef PROCESS_CHECK_TIMEOUT
38 #define PROCESS_CHECK_TIMEOUT 600000 /* milisecond, 10 minute */
41 static GList *lock_list;
51 static void print_lock_node(void)
54 struct lock_node *node;
56 _D("List of remaining CPU locks");
57 SYS_G_LIST_FOREACH(lock_list, elem, node)
58 _D(" pid=%d(%s), locktime=%s, timeout=%dms",
59 node->pid, node->comm, node->locktime, node->timeout);
62 static void remove_lock_node(struct lock_node *node)
68 g_source_remove(node->timer_id);
72 SYS_G_LIST_REMOVE(lock_list, node);
75 /* Check the lock_list is empty after removing the node.
76 * If it is, then request wake unlock */
77 if (SYS_G_LIST_LENGTH(lock_list) == 0)
78 power_release_wakelock();
83 static gboolean lock_expired_cb(void *data)
85 struct lock_node *this = (struct lock_node *) data;
87 _D("Powerlock of pid=%d(%s) for %dms is expired", this->pid, this->comm, this->timeout);
88 remove_lock_node(this);
90 return G_SOURCE_REMOVE;
93 /* check existance of process for every PROCESS_CHECK_TIMEOUT
94 * if the process has requested infinite CPU lock */
95 static gboolean process_check_cb(void *data)
97 struct lock_node *this = (struct lock_node *) data;
99 if (kill(this->pid, 0) != 0) {
100 _D("pid=%d(%s) is not found, deviced release the CPU lock", this->pid, this->comm);
101 remove_lock_node(this);
102 return G_SOURCE_REMOVE;
105 return G_SOURCE_CONTINUE;
108 static GVariant *dbus_power_lock_cpu(GDBusConnection *conn,
109 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
110 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
116 struct lock_node *node;
119 struct tm current_tm;
122 /* lock timeout in milisecond */
123 g_variant_get(param, "(i)", &timeout);
129 pid = gdbus_connection_get_sender_pid(conn, sender);
130 if (pid == -1 || kill(pid, 0) == -1) {
131 _E("%d process does not exist, dbus ignored.", pid);
136 SYS_G_LIST_FOREACH(lock_list, elem, node) {
137 if (node->pid == pid)
146 node = calloc(1, sizeof(struct lock_node));
153 snprintf(commpath, sizeof(commpath), "/proc/%d/comm", pid);
154 retval = sys_get_str(commpath, node->comm, sizeof(node->comm));
156 snprintf(node->comm, sizeof(node->comm), "Unknown");
158 /* remove whitesapce */
159 sep = strpbrk(node->comm, " \n\t\r");
163 SYS_G_LIST_APPEND(lock_list, node);
166 /* set current time for logging */
167 current = time(NULL);
168 rettm = localtime_r(¤t, ¤t_tm);
170 snprintf(node->locktime, sizeof(node->locktime), "Unknown");
172 strftime(node->locktime, sizeof(node->locktime), "%Y-%m-%d %H:%M:%S", ¤t_tm);
174 node->timeout = timeout;
176 if (node->timer_id) {
177 g_source_remove(node->timer_id);
181 _D("pid=%d(%s) request CPU lock for %dms", pid, node->comm, timeout);
182 power_acquire_wakelock();
185 node->timer_id = g_timeout_add(timeout, lock_expired_cb, node);
186 else if (timeout == 0) /* endless CPU lock: add checker that monitors the process*/
187 node->timer_id = g_timeout_add(PROCESS_CHECK_TIMEOUT, process_check_cb, node);
190 return g_variant_new("(i)", ret);
193 static GVariant *dbus_power_unlock_cpu(GDBusConnection *conn,
194 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
195 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
200 struct lock_node *node;
202 pid = gdbus_connection_get_sender_pid(conn, sender);
203 if (pid == -1 || kill(pid, 0) == -1) {
204 _E("%d process does not exist, dbus ignored.", pid);
209 SYS_G_LIST_FOREACH(lock_list, elem, node) {
210 if (node->pid == pid)
215 _D("pid=%d(%s) release CPU lock", node->pid, node->comm);
216 remove_lock_node(node);
220 return g_variant_new("(i)", ret);
223 static GVariant *dbus_power_add_change_state_wait(GDBusConnection *conn,
224 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
225 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
231 pid = gdbus_connection_get_sender_pid(conn, sender);
232 if (pid == -1 || kill(pid, 0) == -1) {
233 _E("%d process does not exist, dbus ignored.", pid);
238 g_variant_get(param, "(t)", &state);
240 ret = add_change_state_wait(pid, state);
243 return g_variant_new("(i)", ret);
246 static GVariant *dbus_power_remove_change_state_wait(GDBusConnection *conn,
247 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
248 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
253 pid = gdbus_connection_get_sender_pid(conn, sender);
254 if (pid == -1 || kill(pid, 0) == -1) {
255 _E("%d process does not exist, dbus ignored.", pid);
259 g_variant_get(param, "(t)", &state);
261 remove_change_state_wait(pid, state);
264 return gdbus_new_g_variant_tuple();
267 static GVariant *dbus_power_confirm_change_state_wait(GDBusConnection *conn,
268 const gchar *sender, const gchar *path, const gchar *iface, const gchar *name,
269 GVariant *param, GDBusMethodInvocation *invocation, gpointer user_data)
275 pid = gdbus_connection_get_sender_pid(conn, sender);
276 if (pid == -1 || kill(pid, 0) == -1) {
277 _E("%d process does not exist, dbus ignored.", pid);
282 g_variant_get(param, "(t)", &sleep_id);
284 ret = confirm_change_state_wait(pid, sleep_id);
287 return g_variant_new("(i)", ret);
290 static const dbus_method_s dbus_methods[] = {
291 { "LockCpu", "i", "i", dbus_power_lock_cpu },
292 { "UnlockCpu", NULL, "i", dbus_power_unlock_cpu },
293 { "AddChangeStateWait", "t", "i", dbus_power_add_change_state_wait },
294 { "RemoveChangeStateWait", "t", NULL, dbus_power_remove_change_state_wait },
295 { "ConfirmChangeStateWait", "t", "i", dbus_power_confirm_change_state_wait },
296 /* Add methods here */
299 static const dbus_interface_u dbus_interface = {
301 .name = DEVICED_INTERFACE_POWER,
302 .methods = dbus_methods,
303 .nr_methods = ARRAY_SIZE(dbus_methods),
306 void power_plugin_dbus_init(void *data)
310 retval = gdbus_add_object(NULL, DEVICED_PATH_POWER, &dbus_interface);
312 _E("Failed to init dbus method.");