fix build failure
[apps/native/tizen-things-daemon.git] / lib / things-service / src / things-service.c
index 9623804..4835eb3 100644 (file)
 #include <gio/gio.h>
 #include <gio/gunixsocketaddress.h>
 #include <app_common.h>
+#include <json.h>
 #include "log-private.h"
 #include "things-service.h"
 #include "common-app-inf.h"
+#include "common-util.h"
 
 struct _ts_data {
-       int padding;
+       char *app_id;
+       char *token;
+       char *project;
+       GSocket *socket;
+       GSocketAddress *addr;
+       GSocket *cl_socket;
+       GDBusConnection *bus;
+       GDBusProxy *proxy;
+       GThread *accept_thread;
+       int accept_thread_running;
 };
 
-int things_service_init(ts_handle *handle)
+struct __timebased_int {
+       gint64 i_time;
+       gint value;
+};
+
+struct __timebased_double {
+       gint64 i_time;
+       gdouble value;
+};
+
+struct __timebased_data {
+       ts_value_type_e type;
+       unsigned int length;
+       GList *v_list;
+};
+
+static void __quit_n_join_accept_thread(ts_handle handle);
+static int _create_accept_thread(ts_handle handle);
+
+static void __timebase_value_free(gpointer data)
+{
+       g_free(data);
+}
+
+static void
+__add_timebased_int_to_json_object(gpointer data, gpointer user_data)
+{
+       struct __timebased_int *tb_int = data;
+       struct json_object *a_obj = user_data;
+       struct json_object *obj = NULL;
+       struct json_object *value_o = NULL;
+       struct json_object *time_o = NULL;
+
+       ret_if(!tb_int);
+       ret_if(!a_obj);
+
+       obj = json_object_new_object();
+       ret_if(!obj);
+
+       time_o = json_object_new_int64(tb_int->i_time);
+       goto_if(!time_o, ERROR);
+       json_object_object_add(obj, "x", time_o);
+
+       value_o = json_object_new_int64(tb_int->value);
+       goto_if(!value_o, ERROR);
+       json_object_object_add(obj, "y", value_o);
+
+       json_object_array_add(a_obj, obj);
+       return;
+
+ERROR:
+       json_object_put(obj);
+       return;
+}
+
+static void
+__add_timebased_double_to_json_object(gpointer data, gpointer user_data)
+{
+       struct __timebased_double *tb_double = data;
+       struct json_object *a_obj = user_data;
+       struct json_object *obj = NULL;
+       struct json_object *value_o = NULL;
+       struct json_object *time_o = NULL;
+
+       ret_if(!tb_double);
+       ret_if(!a_obj);
+
+       obj = json_object_new_object();
+       ret_if(!obj);
+
+       time_o = json_object_new_int64(tb_double->i_time);
+       goto_if(!time_o, ERROR);
+       json_object_object_add(obj, "x", time_o);
+
+       value_o = json_object_new_double(tb_double->value);
+       goto_if(!value_o, ERROR);
+       json_object_object_add(obj, "y", value_o);
+
+       json_object_array_add(a_obj, obj);
+
+       return;
+
+ERROR:
+       json_object_put(obj);
+       return;
+}
+
+static char *__timebased_data_to_json(timebased_data *tb_data)
+{
+       struct json_object *obj = NULL;
+       struct json_object *a_obj = NULL;
+       char *json_str = NULL;
+
+       retv_if(!tb_data, NULL);
+       retvm_if(!((tb_data->type == TS_VALUE_TYPE_INT) ||
+                       (tb_data->type == TS_VALUE_TYPE_DOUBLE)),
+                       NULL, "invalid type[%d]", tb_data->type);
+
+       obj = json_object_new_object();
+       retv_if(!obj, NULL);
+
+       a_obj = json_object_new_array();
+       goto_if(!a_obj, ERROR);
+       json_object_object_add(obj, "data", a_obj);
+
+       switch (tb_data->type) {
+       case TS_VALUE_TYPE_INT:
+               g_list_foreach(tb_data->v_list,
+                       __add_timebased_int_to_json_object, a_obj);
+               break;
+       case TS_VALUE_TYPE_DOUBLE:
+               g_list_foreach(tb_data->v_list,
+                       __add_timebased_double_to_json_object, a_obj);
+               break;
+       default:
+               _E("invalid type [%d]", tb_data->type);
+               goto ERROR;
+               break;
+       }
+
+       json_str = g_strdup(json_object_to_json_string(obj));
+
+       if (obj)
+               json_object_put(obj);
+
+       return json_str;
+
+ERROR:
+       if (obj)
+               json_object_put(obj);
+
+       return NULL;
+}
+
+static char *_get_socket_addr_name(const char *appID, const char *token)
+{
+       return common_make_socket_addr_name(token, appID, (guint)getpid());
+}
+
+static char *_get_app_id(void)
+{
+       char *app_id = NULL;
+       app_get_id(&app_id);
+
+       return app_id;
+}
+
+static char *_get_token(const char *appID)
+{
+       char *checksum = NULL;
+       char *key = NULL;
+
+       key = g_strdup_printf("%s-%u-%lld",
+                       appID, getpid(), common_get_monotonic_coarse_time());
+       checksum = g_compute_checksum_for_string(G_CHECKSUM_MD5, key, -1);
+       g_free(key);
+
+       return checksum;
+}
+
+static gboolean
+__on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
+{
+       ts_handle handle = data;
+       gboolean connected = FALSE;
+
+       connected = g_socket_is_connected(handle->cl_socket);
+       if (!connected)
+               _E("client socket is disconnected");
+
+       if (cond == G_IO_ERR)
+               _E("connection has error");
+
+       if (cond == G_IO_HUP)
+               _E("connection has been broken");
+
+       g_socket_shutdown(handle->cl_socket, FALSE, TRUE, NULL);
+       g_socket_close(handle->cl_socket, NULL);
+       g_object_unref(handle->cl_socket);
+
+       handle->cl_socket = NULL;
+
+       _create_accept_thread(handle);
+
+       return FALSE;
+}
+
+static void _ts_handle_free(ts_handle handle)
+{
+       ret_if(!handle);
+
+       __quit_n_join_accept_thread(handle);
+
+       if (handle->app_id)
+               g_free(handle->app_id);
+
+       if (handle->project)
+               g_free(handle->project);
+
+       if (handle->token)
+               g_free(handle->token);
+
+       if (handle->socket) {
+               g_socket_close(handle->socket, NULL);
+               g_object_unref(handle->socket);
+       }
+
+       if (handle->addr)
+               g_object_unref(handle->addr);
+
+       if (handle->bus)
+               g_object_unref(handle->bus);
+
+       if (handle->proxy)
+               g_object_unref(handle->proxy);
+
+       g_free(handle);
+}
+
+static void __quit_n_join_accept_thread(ts_handle handle)
+{
+       if (handle->accept_thread) {
+               g_atomic_int_set(&(handle->accept_thread_running), 0);
+               g_thread_join(handle->accept_thread);
+               handle->accept_thread = NULL;
+       }
+}
+
+static gpointer accept_thread(gpointer data)
+{
+       ts_handle handle = data;
+       retv_if(!handle, NULL);
+
+       while (TRUE) {
+               GSocket *new_socket = NULL;
+               GIOChannel *new_ch = NULL;
+               int fd = 0;
+               GError *error = NULL;
+
+               if (!g_atomic_int_get(&(handle->accept_thread_running)))
+                       break;
+
+               new_socket = g_socket_accept(handle->socket, NULL, &error);
+               if (!new_socket) {
+                       if (error) {
+                               if (error->code !=  G_IO_ERROR_WOULD_BLOCK)
+                                       _E("err on g_socket_accept() - %s", error->message);
+
+                               g_error_free(error);
+                       }
+                       continue;
+               }
+
+               _D("accepted new socket[%p]", new_socket);
+               fd = g_socket_get_fd(new_socket);
+               new_ch = g_io_channel_unix_new(fd);
+               g_io_add_watch(new_ch, (GIOCondition) (G_IO_ERR | G_IO_HUP),
+                       __on_disconnect, handle);
+               handle->cl_socket = new_socket;
+               break; /* accept and exit thread */
+       }
+       _D("exiting accept thread");
+
+       return NULL;
+}
+
+static int _create_accept_thread(ts_handle handle)
 {
+       GThread *new_thread = NULL;
+       GError *error = NULL;
+
+       retv_if(!handle, -1);
+
+       if (handle->accept_thread)
+               __quit_n_join_accept_thread(handle);
+
+       g_atomic_int_set(&(handle->accept_thread_running), 1);
+       new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
+       if (!new_thread) {
+               _E("failed to create accept thread - %s", error->message);
+               g_error_free(error);
+               return -1;
+       }
+
+       return 0;
+}
+
+static int __client_method_call(const char *method, ts_handle handle)
+{
+       GVariant *response = NULL;
+       int ret = 0;
+       const char *ret_msg = 0;
+       GError *error = NULL;
+
+       _D("call method [%s]", method);
+       response = g_dbus_proxy_call_sync(handle->proxy,
+               method,
+               g_variant_new("(sssu)",
+                       handle->project, handle->token, handle->app_id, (guint)getpid()),
+               G_DBUS_CALL_FLAGS_NONE,
+               -1, /* The timeout in milliseconds or -1 to use the proxy default timeout. */
+               NULL, /* cancellable */
+               &error);
+       if (error) {
+               _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
+               g_error_free(error);
+               return -1;
+       }
+       if (!response)
+               return -1;
+
+       g_variant_get(response, "(i&s)", ret, &ret_msg);
+       _D("method[%s] get response ret[%d] - %s", method, ret, ret_msg);
+       g_variant_unref(response);
+
+       return ret;
+}
+
+static int _client_call_register(ts_handle handle)
+{
+       return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
+}
+
+static int _client_call_unregister(ts_handle handle)
+{
+       return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
+}
+
+int things_service_init(ts_handle *handle, const char *project)
+{
+       ts_handle _handle = NULL;
+       GError *error = NULL;
+       char *socket_name = NULL;
+
+       retv_if(!handle, -1);
+
+       _handle = g_try_malloc0(sizeof(ts_handle));
+       _handle->project = g_strdup(project);
+       _handle->app_id = _get_app_id();
+       if (!_handle->app_id) {
+               _E("failed to get app id");
+               _ts_handle_free(_handle);
+               return -1;
+       }
+       _D("libthings_service with - %s", _handle->app_id);
+
+       _handle->token = _get_token(_handle->app_id);
+       if (!_handle->token) {
+               _E("failed to get token");
+               _ts_handle_free(_handle);
+               return -1;
+       }
+       _D("get token - %s", _handle->token);
+       _handle->cl_socket = NULL;
+       _handle->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
+                               G_SOCKET_TYPE_STREAM,
+                               G_SOCKET_PROTOCOL_DEFAULT,
+                               &error);
+       if (!_handle->socket) {
+               _E("failed to create socket - %s", error->message);
+               g_error_free(error);
+               _ts_handle_free(_handle);
+               return -1;
+       }
+       g_socket_set_blocking(_handle->socket, FALSE);
+
+       socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
+       if (!socket_name) {
+               _E("failed to get socket_name");
+               _ts_handle_free(_handle);
+               return -1;
+       }
+       _handle->addr = g_unix_socket_address_new_with_type(
+                       socket_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
+       _D("got socket addr - [%s]", socket_name);
+       g_free(socket_name);
+
+       g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
+       if (error) {
+               _E("failed to g_socket_bind() - %s", error->message);
+               g_error_free(error);
+               _ts_handle_free(_handle);
+               return -1;
+       }
+
+       g_socket_listen(_handle->socket, &error);
+       if (error) {
+               _E("failed to g_socket_listen() - %s", error->message);
+               g_error_free(error);
+               _ts_handle_free(_handle);
+               return -1;
+       }
+
+       _handle->bus =
+               g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
+       if (!_handle->bus) {
+               _E("failed to g_bus_get_sync() - %s", error->message);
+               g_error_free(error);
+               _ts_handle_free(_handle);
+               return -1;
+       }
+
+       _handle->proxy = g_dbus_proxy_new_sync(_handle->bus,
+               G_DBUS_PROXY_FLAGS_NONE,
+               NULL, /* GDBusInterfaceInfo */
+               TTD_APP_INF_BUS_NAME,
+               TTD_APP_INF_OBJECT_PATH,
+               TTD_APP_INF_BUS_INF,
+               NULL, /* cancellable */
+               &error);
+       if (!_handle->proxy) {
+               _E("failed to g_dbus_proxy_new_sync() - %s",
+                       error ? error->message : "error not set");
+               _ts_handle_free(_handle);
+               return -1;
+       }
+
+       if (_create_accept_thread(_handle)) {
+               _E("failed to _create_accept_thread()");
+               _ts_handle_free(_handle);
+               return -1;
+       }
+
+       if (_client_call_register(_handle)) {
+               _E("failed to call register");
+               _ts_handle_free(_handle);
+               return -1;
+       }
+
+       *handle = _handle;
+
        return 0;
 }
 
 int things_service_fini(ts_handle handle)
 {
+       retv_if(!handle, -1);
+
+       _client_call_unregister(handle);
+       _ts_handle_free(handle);
+
+       return 0;
+}
+
+static int __ts_send_data(ts_handle handle, const char *json_data)
+{
+       gboolean connected = FALSE;
+       char *msg = NULL;
+       GError *error = NULL;
+
+       retv_if(!handle, -1);
+       retv_if(!handle->socket, -1);
+       retv_if(!handle->cl_socket, -1);
+       retv_if(!json_data, -1);
+
+       connected = g_socket_is_connected(handle->cl_socket);
+       retv_if(!connected, -1);
+
+       msg = g_strdup_printf("%s %s", json_data, TTD_APP_MSG_DELIMITER);
+       g_socket_send(handle->cl_socket, msg, strlen(msg), NULL, &error);
+       if (error) {
+               _E("failed to g_socket_send() - %s", error->message);
+               g_error_free(error);
+               return -1;
+       }
+       _D("msg is sent - %s", msg);
+       g_free(msg);
+
        return 0;
 }
 
 int things_service_send_data(ts_handle handle, const char *json_data)
 {
+       return __ts_send_data(handle, json_data);
+}
+
+timebased_data *thing_service_timebased_data_new(ts_value_type_e type)
+{
+       timebased_data *t_data = NULL;
+
+       retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
+               NULL, "invalid type[%d]", type);
+
+       t_data = g_try_malloc0(sizeof(timebased_data));
+       if (!t_data)
+               return NULL;
+
+       t_data->type = type;
+       t_data->length = 0;
+
+       return t_data;
+}
+
+void thing_service_timebased_data_free(timebased_data *tb_data)
+{
+       ret_if(!tb_data);
+
+       if (tb_data->v_list)
+               g_list_free_full(tb_data->v_list, __timebase_value_free);
+
+       g_free(tb_data);
+}
+
+int things_service_timebased_data_get_length(timebased_data *tb_data)
+{
+       retv_if(!tb_data, -1);
+
+       return tb_data->length;
+}
+
+int
+things_service_timebased_data_append_int(timebased_data *tb_data, int ivalue)
+{
+       struct __timebased_int *tb_int = NULL;
+
+       retv_if(!tb_data, -1);
+       retv_if(tb_data->type != TS_VALUE_TYPE_INT, -1);
+
+       tb_int = g_try_malloc0(sizeof(struct __timebased_int));
+       retv_if(!tb_int, -1);
+
+       tb_int->i_time = common_get_epoch_coarse_time();
+       tb_int->value = ivalue;
+
+       tb_data->v_list = g_list_append(tb_data->v_list, tb_int);
+       tb_data->length++;
+
+       return 0;
+}
+
+int things_service_timebased_data_append_double(
+       timebased_data *tb_data, double dvalue)
+{
+       struct __timebased_double *tb_double = NULL;
+
+       retv_if(!tb_data, -1);
+       retv_if(tb_data->type != TS_VALUE_TYPE_DOUBLE, -1);
+
+       tb_double = g_try_malloc0(sizeof(struct __timebased_double));
+       retv_if(!tb_double, -1);
+
+       tb_double->i_time = common_get_epoch_coarse_time();
+       tb_double->value = dvalue;
+
+       tb_data->v_list = g_list_append(tb_data->v_list, tb_double);
+       tb_data->length++;
+
        return 0;
-}
\ No newline at end of file
+}
+
+int
+things_service_send_timebased_data(ts_handle handle, timebased_data *tb_data)
+{
+       int ret = 0;
+       char *json_data = NULL;
+
+       retv_if(!handle, -1);
+       retv_if(!tb_data, -1);
+
+       json_data = __timebased_data_to_json(tb_data);
+       ret  = __ts_send_data(handle, json_data);
+       g_free(json_data);
+
+       if (!ret) { /* if success to send data, remove all items in tb_data */
+               g_list_free_full(tb_data->v_list, __timebase_value_free);
+               tb_data->v_list = NULL;
+               tb_data->length = 0;
+       }
+
+       return ret;
+}