power: add device_power_change_state() 32/275732/5 accepted/tizen/unified/20220601.141615 submit/tizen/20220531.085748
authorYoungjae Cho <y0.cho@samsung.com>
Tue, 31 May 2022 05:03:30 +0000 (14:03 +0900)
committerYoungjae Cho <y0.cho@samsung.com>
Tue, 31 May 2022 08:30:10 +0000 (17:30 +0900)
The API, device_power_change_state(), send a request to deviced for
changing power state. It simply sends request and receives result of
the request. If the changed state is sleep/poweroff/reboot, then the
ongoing async callback might be ceased. To ensure the whole execution
of subroutine, use device_power_add_change_state_wait_callback().

Change-Id: Id7479a08d56505a0bc6bfba3e9971cb635e1276e
Signed-off-by: Youngjae Cho <y0.cho@samsung.com>
include/power-internal.h
src/power-internal.c

index 0393310..fac6b59 100644 (file)
@@ -82,7 +82,9 @@ struct device_change_state_info {
 typedef void (*change_state_wait_callback) (const struct device_change_state_info *info, void *user_data);
 
 /**
- * Add callback for states to be transitioned.
+ * Add callback for states to be transitioned. The action caused by the transition,
+ *   e.g., wake unlock for sleep or shutdown for reboot, would be defered until calling
+ *   device_power_change_state_wait_done().
  *
  * uint64_t state_bits: bitwise-ORing of interesting state.
  * change_state_wait_callback cb: For every state transition to the state_bit, the cb will be invoked.
@@ -102,6 +104,31 @@ int device_power_change_state_wait_done(uint64_t id);
  */
 void device_power_remove_change_state_wait_callback(uint64_t state_bits);
 
+/**
+ * Async callback of device_power_change_state()
+ *
+ * uint64_t state: which state it has transitioned.
+ * int retval: result of the method call.
+ * void *user_data: user data for the callback.
+ */
+typedef void (*change_state_callback) (uint64_t state, int retval, void *user_data);
+
+/**
+ * Send a request to the deviced for changing power state. It doesn't ensure the whole execution
+ *   of a callback subroutine - think of a request for sleep/poweroff/reboot state. To ensure
+ *   the whole execution of a callback, use device_power_add_change_state_wait_callback() additionally.
+ *   If both change_state_wait_callback and change_state_callback has registered to a state,
+ *   it is unpredictable which callback will be invoked first.
+ *
+ * uint64_t state: which state to transition to
+ * int timeout_sec: maximum timeout of async method call. if it expires, then the second parameter of
+ *   a callback function, int retval, gives -ETIMEDOUT error. If it is 0 or negative, it is set to
+ *   the default, 10 seconds.
+ * change_state_callback cb: async callback function, nullable
+ * void *user_data: user data for the callback.
+ */
+int device_power_change_state(uint64_t state, int timeout_sec, change_state_callback cb, void *user_data);
+
 #ifdef __cplusplus
 }
 #endif
index 8cb5029..7244368 100644 (file)
@@ -8,18 +8,24 @@
 
 #define DBUS_METHOD_SYNC_CALL_TIMEOUT_MS    10000 /* 10 second */
 
-struct userdata {
+struct change_state_wait_handler {
        change_state_wait_callback callback;
-       void *data;
+       void *user_data;
+};
+
+struct change_state_handler {
+       change_state_callback callback;
+       void *user_data;
+       uint64_t state;
 };
 
 static int change_state_signal_id[POWER_STATE_MAX_INDEX];
 
 static void signal_unsubscribed_callback(void *data)
 {
-       struct userdata *ud = (struct userdata *) data;
+       struct change_state_wait_handler *h = (struct change_state_wait_handler *) data;
 
-       free(ud);
+       free(h);
 }
 
 static void signal_callback(GDBusConnection *connection,
@@ -31,16 +37,16 @@ static void signal_callback(GDBusConnection *connection,
        gpointer user_data)
 {
        struct device_change_state_info info;
-       struct userdata *ud;
+       struct change_state_wait_handler *h;
 
-       ud = (struct userdata *) user_data;
+       h = (struct change_state_wait_handler *) user_data;
 
-       if (!ud || !ud->callback)
+       if (!h || !h->callback)
                return;
 
        g_variant_get(parameters, "(ttti)", &info.prev_state, &info.next_state, &info.id, &info.reason);
 
-       ud->callback(&info, ud->data);
+       h->callback(&info, h->user_data);
 }
 
 static int __device_power_add_change_state_wait_callback(GDBusConnection *connection,
@@ -54,19 +60,19 @@ static int __device_power_add_change_state_wait_callback(GDBusConnection *connec
                [POWER_STATE_REBOOT_INDEX] = SIGNAME_CHANGE_STATE_TO_REBOOT,
                [POWER_STATE_EXIT_INDEX] = SIGNAME_CHANGE_STATE_TO_EXIT,
        };
-       struct userdata *ud;
+       struct change_state_wait_handler *h;
 
        if (change_state_signal_id[index] > 0)
                return DEVICE_ERROR_ALREADY_IN_PROGRESS;
 
-       ud = malloc(sizeof(struct userdata));
-       if (!ud) {
+       h = malloc(sizeof(struct change_state_wait_handler));
+       if (!h) {
                _E("Failed to alloc user data");
                return DEVICE_ERROR_OPERATION_FAILED;
        }
 
-       ud->callback = cb;
-       ud->data = data;
+       h->callback = cb;
+       h->user_data = data;
 
        change_state_signal_id[index] = g_dbus_connection_signal_subscribe(connection,
                DEVICED_BUS_NAME,
@@ -76,7 +82,7 @@ static int __device_power_add_change_state_wait_callback(GDBusConnection *connec
                NULL,
                G_DBUS_SIGNAL_FLAGS_NONE,
                signal_callback,
-               ud,
+               h,
                signal_unsubscribed_callback);
 
        return DEVICE_ERROR_NONE;
@@ -205,3 +211,84 @@ void device_power_remove_change_state_wait_callback(uint64_t state_bits)
                NULL,
                NULL);
 }
+
+static void change_state_async_callback(GObject *source_object, GAsyncResult *res, gpointer user_data)
+{
+       GError *err = NULL;
+       GDBusConnection *connection;
+       GVariant *retgv;
+       int retval;
+       struct change_state_handler *h;
+
+       h = (struct change_state_handler *) user_data;
+       if (!h)
+               return;
+
+       if (!h->callback)
+               goto cleanup;
+
+       connection = (GDBusConnection *) source_object;
+       retgv = g_dbus_connection_call_finish(connection, res, &err);
+       if (err) {
+               retval = -ECOMM;
+               if (err->code == G_IO_ERROR_TIMED_OUT)
+                       retval = -ETIMEDOUT;
+
+               _E("Failed to finish async call, %s(%d)", err->message, err->code);
+               h->callback(h->state, retval, h->user_data);
+
+               goto cleanup;
+       }
+
+       g_variant_get(retgv, "(i)", &retval);
+       h->callback(h->state, retval, h->user_data);
+
+cleanup:
+       if (err)
+               g_clear_error(&err);
+       free(h);
+}
+
+int device_power_change_state(uint64_t state, int timeout_sec, change_state_callback cb, void *user_data)
+{
+       GError *err = NULL;
+       GDBusConnection *connection;
+       struct change_state_handler *h = NULL;
+
+       if (cb) {
+               h = (struct change_state_handler *) calloc(1, sizeof(struct change_state_handler));
+               if (!h) {
+                       _E("Failed to alloc change_state_handler.");
+                       return DEVICE_ERROR_OPERATION_FAILED;
+               }
+
+               h->state = state;
+               h->callback = cb;
+               h->user_data = user_data;
+       }
+
+       if (timeout_sec <= 0)
+               timeout_sec = 10;
+
+       connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
+       if (!connection) {
+               _E("Failed to get dbus connection, %s", err->message);
+               g_clear_error(&err);
+               return DEVICE_ERROR_OPERATION_FAILED;
+       }
+
+       g_dbus_connection_call(connection,
+               DEVICED_BUS_NAME,
+               DEVICED_PATH_POWER,
+               DEVICED_INTERFACE_POWER,
+               "PowerChangeState",
+               g_variant_new("(t)", state),
+               NULL,
+               G_DBUS_CALL_FLAGS_NONE,
+               timeout_sec * 1000,
+               NULL,
+               change_state_async_callback,
+               h);
+
+       return DEVICE_ERROR_NONE;
+}