+#include <unistd.h>
+#include <stdint.h>
+#include <libsyscommon/list.h>
+#include <device/power-internal.h>
+
+#include "shared/log.h"
+#include "shared/device-notifier.h"
+#include "shared/common.h"
+
+#include "power-state-wait.h"
+#include "power-state-manager.h"
+
+#define MAX_WAIT_SECOND 5 /* second */
+
+struct proc_info {
+ pid_t pid;
+ char comm[128];
+ guint64 state_bitmap;
+ int killed;
+};
+
+struct change_state_wait {
+ struct proc_info *pi;
+ guint64 id;
+ enum psm_state state;
+};
+
+static GList *proc_list;
+static GList *waiting_list;
+static int max_wait_timer;
+static void (*__change_state_wait_done) (void);
+static enum psm_state waiting_state;
+
+static void change_state_wait_done(void)
+{
+ _D("%s wait done", psm_name[waiting_state]);
+
+ if (max_wait_timer) {
+ g_source_remove(max_wait_timer);
+ max_wait_timer = 0;
+ }
+
+ if (__change_state_wait_done) {
+ __change_state_wait_done();
+ }
+}
+
+static gboolean max_wait_expired_cb(void *data)
+{
+ struct change_state_wait *csw;
+ struct proc_info *pi;
+ GList *elem, *elem_next;
+
+ max_wait_timer = 0;
+
+ SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
+ if (kill(csw->pi->pid, 0) != 0)
+ csw->pi->killed = 1;
+ else
+ _E("pid=%d(%s) hasn't confirmed id=%ld(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]);
+ waiting_list = g_list_remove_link(waiting_list, elem);
+ g_list_free(elem);
+ free(csw);
+ }
+
+ SYS_G_LIST_FOREACH_SAFE(proc_list, elem, elem_next, pi) {
+ if (pi->killed) {
+ _D("Clean up not existing pid=%d(%s)", pi->pid, pi->comm);
+ proc_list = g_list_remove_link(proc_list, elem);
+ g_list_free(elem);
+ free(pi);
+ }
+ }
+
+ change_state_wait_done();
+
+ return G_SOURCE_REMOVE;
+}
+
+int add_change_state_wait(pid_t pid, guint64 state)
+{
+ struct proc_info *pi;
+ GList *elem;
+
+ _D("pid=%d added csw for %#lx", pid, state);
+
+ SYS_G_LIST_FOREACH(proc_list, elem, pi) {
+ if (pi->pid == pid) {
+ pi->state_bitmap |= state;
+ return 0;
+ }
+ }
+
+ pi = calloc(1, sizeof(struct proc_info));
+ if (!pi)
+ return -ENOMEM;
+
+ pi->pid = pid;
+ pi->state_bitmap = state;
+ get_command(pid, pi->comm, sizeof(pi->comm));
+ SYS_G_LIST_APPEND(proc_list, pi);
+
+ return 0;
+}
+
+void remove_change_state_wait(pid_t pid, guint64 state)
+{
+ struct proc_info *pi;
+ GList *elem, *elem_next;
+
+ _D("pid=%d removed csw for %#lx", pid, state);
+
+ SYS_G_LIST_FOREACH_SAFE(proc_list, elem, elem_next, pi) {
+ if (pi->pid == pid) {
+ pi->state_bitmap &= ~state;
+ if (pi->state_bitmap == 0) {
+ proc_list = g_list_remove_link(proc_list, elem);
+ g_list_free(elem);
+ free(pi);
+ }
+ }
+ }
+}
+
+int confirm_change_state_wait(pid_t pid, guint64 id)
+{
+ struct change_state_wait *csw;
+ GList *elem, *elem_next;
+
+ SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
+ if (csw->pi->pid == pid && csw->id == id) {
+ _D("pid=%d(%s) confirmed id=%lu(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]);
+ waiting_list = g_list_remove_link(waiting_list, elem);
+ g_list_free(elem);
+ free(csw);
+
+ if (SYS_G_LIST_LENGTH(waiting_list) == 0) {
+ change_state_wait_done();
+ return 0;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int check_proc_requested_wait_for_state(struct proc_info *pi, enum psm_state state)
+{
+ if (!pi)
+ return 0;
+
+ if (state == PSM_NORMAL)
+ return (pi->state_bitmap & POWER_STATE_NORMAL);
+ if (state == PSM_SLEEP)
+ return (pi->state_bitmap & POWER_STATE_SLEEP);
+ if (state == PSM_POWEROFF)
+ return (pi->state_bitmap & POWER_STATE_POWEROFF);
+ if (state == PSM_REBOOT)
+ return (pi->state_bitmap & POWER_STATE_REBOOT);
+
+ return 0;
+}
+
+int update_change_state_wait(guint64 id, const struct trans_info *ti, change_state_wait_done_cb callback)
+{
+ struct proc_info *pi;
+ struct change_state_wait *csw;
+ GList *elem, *elem_next;
+ int n_waiting;
+
+ // initialize timer
+ if (max_wait_timer) {
+ g_source_remove(max_wait_timer);
+ max_wait_timer = 0;
+ }
+
+ // we are waiting for confirm of transition to the next state
+ waiting_state = ti->next;
+ _D("%s wait is triggered, id=%ld", psm_name[waiting_state], id);
+
+ // cancel all ongoing csw that is not a waiting for the next state
+ SYS_G_LIST_FOREACH_SAFE(waiting_list, elem, elem_next, csw) {
+ if (csw->state != waiting_state) {
+ waiting_list = g_list_remove_link(waiting_list, elem);
+ g_list_free(elem);
+ _D("Cancel waiting: pid=%d(%s) for id=%ld(%s)", csw->pi->pid, csw->pi->comm, csw->id, psm_name[csw->state]);
+ free(csw);
+ }
+ }
+
+ // add wait list
+ SYS_G_LIST_FOREACH(proc_list, elem, pi) {
+ if (check_proc_requested_wait_for_state(pi, waiting_state)) {
+ csw = calloc(1, sizeof(struct change_state_wait));
+ if (!csw)
+ continue;
+
+ csw->pi = pi;
+ csw->id = id;
+ csw->state = waiting_state;
+ SYS_G_LIST_APPEND(waiting_list, csw);
+ }
+ }
+
+ n_waiting = SYS_G_LIST_LENGTH(waiting_list);
+ if (n_waiting == 0) {
+ _D("There were no csw requests for %s, skip waiting", psm_name[waiting_state]);
+ __change_state_wait_done = NULL;
+ return 0;
+ }
+
+ __change_state_wait_done = callback;
+
+ _D("Expected number of wait confirm=%d", n_waiting);
+ max_wait_timer = g_timeout_add_seconds(MAX_WAIT_SECOND, max_wait_expired_cb, NULL);
+
+ return n_waiting;
+}