add things serivce APIs for timebase data
[apps/native/tizen-things-daemon.git] / lib / things-service / src / things-service.c
1 /*
2  * Copyright (c) 2017 Samsung Electronics Co., Ltd.
3  *
4  * Licensed under the Flora License, Version 1.1 (the License);
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an AS IS BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <sys/types.h>
18 #include <unistd.h>
19 #include <time.h>
20 #include <glib.h>
21 #include <gio/gio.h>
22 #include <gio/gunixsocketaddress.h>
23 #include <app_common.h>
24 #include "log-private.h"
25 #include "things-service.h"
26 #include "common-app-inf.h"
27 #include "common-util.h"
28
29 struct _ts_data {
30         char *app_id;
31         char *token;
32         GSocket *socket;
33         GSocketAddress *addr;
34         GSocket *cl_socket;
35         GDBusConnection *bus;
36         GDBusProxy *proxy;
37         GThread *accept_thread;
38         int accept_thread_running;
39 };
40
41 struct __timebased_int {
42         gint64 i_time;
43         gint value;
44 };
45
46 struct __timebased_double {
47         gint64 i_time;
48         gdouble value;
49 };
50
51 struct __timebased_data {
52         ts_value_type_e type;
53         unsigned int length;
54         GList *v_list;
55 };
56
57 static void __quit_n_join_accept_thread(ts_handle handle);
58 static int _create_accept_thread(ts_handle handle);
59
60 static void __timebase_value_free(gpointer data)
61 {
62         g_free(data);
63 }
64
65 static void
66 __add_timebased_int_to_json_object(gpointer data, gpointer user_data)
67 {
68         struct __timebased_int *tb_int = data;
69         struct json_object *a_obj = user_data;
70         struct json_object *obj = NULL;
71         struct json_object *value_o = NULL;
72         struct json_object *time_o = NULL;
73
74         ret_if(!tb_int);
75         ret_if(!a_obj);
76
77         obj = json_object_new_object();
78         retv_if(!obj);
79
80         time_o = json_object_new_int64(tb_int->time);
81         goto_if(!time_o, ERROR);
82         json_object_object_add(obj, "x", time_o);
83
84         value_o = json_object_new_int64(tb_int->value);
85         goto_if(!value_o, ERROR);
86         json_object_object_add(obj, "y", value_o);
87
88         json_object_array_add(a_obj, obj);
89         return;
90
91 ERROR:
92         json_object_put(obj);
93         return;
94 }
95
96 static void
97 __add_timebased_double_to_json_object(gpointer data, gpointer user_data)
98 {
99         struct __timebased_double *tb_double = data;
100         struct json_object *a_obj = user_data;
101         struct json_object *obj = NULL;
102         struct json_object *value_o = NULL;
103         struct json_object *time_o = NULL;
104
105         ret_if(!tb_int);
106         ret_if(!a_obj);
107
108         obj = json_object_new_object();
109         retv_if(!obj, NULL);
110
111         time_o = json_object_new_int64(tb_double->time);
112         goto_if(!time_o, ERROR);
113         json_object_object_add(obj, "x", time_o);
114
115         value_o = json_object_new_double(tb_double->value);
116         goto_if(!value_o, ERROR);
117         json_object_object_add(obj, "y", value_o);
118
119         json_object_array_add(a_obj, obj);
120
121         return;
122
123 ERROR:
124         json_object_put(obj);
125         return;
126 }
127
128 static char *__timebased_data_to_json(timebased_data *tb_data)
129 {
130         struct json_object *obj = NULL;
131         struct json_object *a_obj = NULL;
132         char *json_str = NULL;
133         int i = 0;
134         GList *temp_l = NULL;
135
136         retv_if(!tb_data, NULL);
137         retvm_if(!((tb_data->type == TS_VALUE_TYPE_INT) ||
138                         (tb_data->type == TS_VALUE_TYPE_DOUBLE)),
139                         NULL, "invalid type[%d]", tb_data->type);
140
141         obj = json_object_new_object();
142         retv_if(!obj, NULL);
143
144         a_obj = json_object_new_array();
145         goto_if(!a_obj, ERROR);
146         json_object_object_add(obj, "data", a_obj);
147
148         switch (tb_data->type) {
149         case TS_VALUE_TYPE_INT:
150                 g_list_foreach(tb_data->v_list,
151                         __add_timebased_int_to_json_object, a_obj);
152                 break;
153         case TS_VALUE_TYPE_DOUBLE:
154                 g_list_foreach(tb_data->v_list,
155                         __add_timebased_double_to_json_object, a_obj);
156                 break;
157         default:
158                 _E("invalid type [%d]", tb_data->type);
159                 goto ERROR:
160                 break;
161         }
162
163         json_str = g_strdup(json_object_to_json_string(obj));
164
165         if (obj)
166                 json_object_put(obj);
167
168         return json_str;
169
170 ERROR:
171         if (obj)
172                 json_object_put(obj);
173
174         return NULL;
175 }
176
177 static char *_get_socket_addr_name(const char *appID, const char *token)
178 {
179         return common_make_socket_addr_name(token, appID, (guint)getpid());
180 }
181
182 static char *_get_app_id(void)
183 {
184         char *app_id = NULL;
185         app_get_id(&app_id);
186
187         return app_id;
188 }
189
190 static char *_get_token(const char *appID)
191 {
192         char *checksum = NULL;
193         char *key = NULL;
194
195         key = g_strdup_printf("%s-%u-%lld",
196                         appID, getpid(), common_get_monotonic_coarse_time());
197         checksum = g_compute_checksum_for_string(G_CHECKSUM_MD5, key, -1);
198         g_free(key);
199
200         return checksum;
201 }
202
203 static gboolean
204 __on_disconnect(GIOChannel *ch, GIOCondition cond, gpointer data)
205 {
206         ts_handle handle = data;
207         gboolean connected = FALSE;
208
209         connected = g_socket_is_connected(handle->cl_socket);
210         if (!connected)
211                 _E("client socket is disconnected");
212
213         if (cond == G_IO_ERR)
214                 _E("connection has error");
215
216         if (cond == G_IO_HUP)
217                 _E("connection has been broken");
218
219         g_socket_shutdown(handle->cl_socket, FALSE, TRUE, NULL);
220         g_socket_close(handle->cl_socket, NULL);
221         g_object_unref(handle->cl_socket);
222
223         handle->cl_socket = NULL;
224
225         _create_accept_thread(handle);
226
227         return FALSE;
228 }
229
230 static void _ts_handle_free(ts_handle handle)
231 {
232         ret_if(!handle);
233
234         __quit_n_join_accept_thread(handle);
235
236         if (handle->app_id)
237                 g_free(handle->app_id);
238
239         if (handle->token)
240                 g_free(handle->token);
241
242         if (handle->socket) {
243                 g_socket_close(handle->socket, NULL);
244                 g_object_unref(handle->socket);
245         }
246
247         if (handle->addr)
248                 g_object_unref(handle->addr);
249
250         if (handle->bus)
251                 g_object_unref(handle->bus);
252
253         if (handle->proxy)
254                 g_object_unref(handle->proxy);
255
256         g_free(handle);
257 }
258
259 static void __quit_n_join_accept_thread(ts_handle handle)
260 {
261         if (handle->accept_thread) {
262                 g_atomic_int_set(&(handle->accept_thread_running), 0);
263                 g_thread_join(handle->accept_thread);
264                 handle->accept_thread = NULL;
265         }
266 }
267
268 static gpointer accept_thread(gpointer data)
269 {
270         ts_handle handle = data;
271         retv_if(!handle, NULL);
272
273         while (TRUE) {
274                 GSocket *new_socket = NULL;
275                 GIOChannel *new_ch = NULL;
276                 int fd = 0;
277                 GError *error = NULL;
278
279                 if (!g_atomic_int_get(&(handle->accept_thread_running)))
280                         break;
281
282                 new_socket = g_socket_accept(handle->socket, NULL, &error);
283                 if (!new_socket) {
284                         if (error) {
285                                 if (error->code !=  G_IO_ERROR_WOULD_BLOCK)
286                                         _E("err on g_socket_accept() - %s", error->message);
287
288                                 g_error_free(error);
289                         }
290                         continue;
291                 }
292
293                 _D("accepted new socket[%p]", new_socket);
294                 fd = g_socket_get_fd(new_socket);
295                 new_ch = g_io_channel_unix_new(fd);
296                 g_io_add_watch(new_ch, (GIOCondition) (G_IO_ERR | G_IO_HUP),
297                         __on_disconnect, handle);
298                 handle->cl_socket = new_socket;
299                 break; /* accept and exit thread */
300         }
301         _D("exiting accept thread");
302
303         return NULL;
304 }
305
306 static int _create_accept_thread(ts_handle handle)
307 {
308         GThread *new_thread = NULL;
309         GError *error = NULL;
310
311         retv_if(!handle, -1);
312
313         if (handle->accept_thread)
314                 __quit_n_join_accept_thread(handle);
315
316         g_atomic_int_set(&(handle->accept_thread_running), 1);
317         new_thread = g_thread_try_new(NULL, accept_thread, handle, &error);
318         if (!new_thread) {
319                 _E("failed to create accept thread - %s", error->message);
320                 g_error_free(error);
321                 return -1;
322         }
323
324         return 0;
325 }
326
327 static int __client_method_call(const char *method, ts_handle handle)
328 {
329         GVariant *response = NULL;
330         int ret = 0;
331         GError *error = NULL;
332
333         _D("call method [%s]", method);
334         response = g_dbus_proxy_call_sync(handle->proxy,
335                 method,
336                 g_variant_new("(ssu)", handle->token, handle->app_id, (guint)getpid()),
337                 G_DBUS_CALL_FLAGS_NONE,
338                 -1, /* The timeout in milliseconds or -1 to use the proxy default timeout. */
339                 NULL, /* cancellable */
340                 &error);
341         if (error) {
342                 _E("failed to g_dbus_proxy_call_sync() - %s", error->message);
343                 g_error_free(error);
344                 return -1;
345         }
346         g_variant_get(response, "(i)", ret);
347         _D("method[%s] get response ret[%d]", method, ret);
348         g_variant_unref(response);
349
350         return 0;
351 }
352
353 static int _client_call_register(ts_handle handle)
354 {
355         return __client_method_call(TTD_APP_INF_METHOD_REG, handle);
356 }
357
358 static int _client_call_unregister(ts_handle handle)
359 {
360         return __client_method_call(TTD_APP_INF_METHOD_UNREG, handle);
361 }
362
363 int things_service_init(ts_handle *handle)
364 {
365         ts_handle _handle = NULL;
366         GError *error = NULL;
367         char *socket_name = NULL;
368
369         retv_if(!handle, -1);
370
371         _handle = g_try_malloc0(sizeof(ts_handle));
372         _handle->app_id = _get_app_id();
373         if (!_handle->app_id) {
374                 _E("failed to get app id");
375                 _ts_handle_free(_handle);
376                 return -1;
377         }
378         _D("libthings_service with - %s", _handle->app_id);
379
380         _handle->token = _get_token(_handle->app_id);
381         if (!_handle->token) {
382                 _E("failed to get token");
383                 _ts_handle_free(_handle);
384                 return -1;
385         }
386         _D("get token - %s", _handle->token);
387         _handle->cl_socket = NULL;
388         _handle->socket = g_socket_new(G_SOCKET_FAMILY_UNIX,
389                                 G_SOCKET_TYPE_STREAM,
390                                 G_SOCKET_PROTOCOL_DEFAULT,
391                                 &error);
392         if (!_handle->socket) {
393                 _E("failed to create socket - %s", error->message);
394                 g_error_free(error);
395                 _ts_handle_free(_handle);
396                 return -1;
397         }
398         g_socket_set_blocking(_handle->socket, FALSE);
399
400         socket_name = _get_socket_addr_name(_handle->app_id, _handle->token);
401         if (!socket_name) {
402                 _E("failed to get socket_name");
403                 _ts_handle_free(_handle);
404                 return -1;
405         }
406         _handle->addr = g_unix_socket_address_new_with_type(
407                         socket_name, -1, G_UNIX_SOCKET_ADDRESS_ABSTRACT);
408         _D("got socket addr - [%s]", socket_name);
409         g_free(socket_name);
410
411         g_socket_bind(_handle->socket, _handle->addr, TRUE, &error);
412         if (error) {
413                 _E("failed to g_socket_bind() - %s", error->message);
414                 g_error_free(error);
415                 _ts_handle_free(_handle);
416                 return -1;
417         }
418
419         g_socket_listen(_handle->socket, &error);
420         if (error) {
421                 _E("failed to g_socket_listen() - %s", error->message);
422                 g_error_free(error);
423                 _ts_handle_free(_handle);
424                 return -1;
425         }
426
427         _handle->bus =
428                 g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &error);
429         if (!_handle->bus) {
430                 _E("failed to g_bus_get_sync() - %s", error->message);
431                 g_error_free(error);
432                 _ts_handle_free(_handle);
433                 return -1;
434         }
435
436         _handle->proxy = g_dbus_proxy_new_sync(_handle->bus,
437                 G_DBUS_PROXY_FLAGS_NONE,
438                 NULL, /* GDBusInterfaceInfo */
439                 TTD_APP_INF_BUS_NAME,
440                 TTD_APP_INF_OBJECT_PATH,
441                 TTD_APP_INF_BUS_INF,
442                 NULL, /* cancellable */
443                 &error);
444         if (!_handle->proxy) {
445                 _E("failed to g_dbus_proxy_new_sync() - %s",
446                         error ? error->message : "error not set");
447                 _ts_handle_free(_handle);
448                 return -1;
449         }
450
451         if (_create_accept_thread(_handle)) {
452                 _E("failed to _create_accept_thread()");
453                 _ts_handle_free(_handle);
454                 return -1;
455         }
456
457         if (_client_call_register(_handle)) {
458                 _E("failed to call register");
459                 _ts_handle_free(_handle);
460                 return -1;
461         }
462
463         *handle = _handle;
464
465         return 0;
466 }
467
468 int things_service_fini(ts_handle handle)
469 {
470         retv_if(!handle, -1);
471
472         _client_call_unregister(handle);
473         _ts_handle_free(handle);
474
475         return 0;
476 }
477
478 static int __ts_send_data(ts_handle handle, const char *json_data)
479 {
480         gboolean connected = FALSE;
481         char *msg = NULL;
482         GError *error = NULL;
483
484         retv_if(!handle, -1);
485         retv_if(!handle->socket, -1);
486         retv_if(!handle->cl_socket, -1);
487         retv_if(!json_data, -1);
488
489         connected = g_socket_is_connected(handle->cl_socket);
490         retv_if(!connected, -1);
491
492         msg = g_strdup_printf("%s %s", json_data, TTD_APP_MSG_DELIMITER);
493         g_socket_send(handle->cl_socket, msg, strlen(msg), NULL, &error);
494         if (error) {
495                 _E("failed to g_socket_send() - %s", error->message);
496                 g_error_free(error);
497                 return -1;
498         }
499         _D("msg is sent - %s", msg);
500         g_free(msg);
501
502         return 0;
503 }
504
505 int things_service_send_data(ts_handle handle, const char *json_data)
506 {
507         return __ts_send_data(handle, json_data);
508 }
509
510 timebased_data *thing_service_timebased_data_new(ts_value_type_e type)
511 {
512         timebased_data *t_data = NULL;
513
514         retvm_if(!((type == TS_VALUE_TYPE_INT) || (type == TS_VALUE_TYPE_DOUBLE)),
515                 NULL, "invalid type[%d]", type);
516
517         t_data = g_try_malloc0(sizeof(timebased_data));
518         if (!t_data)
519                 return NULL;
520
521         t_data->type = type;
522         t_data->length = 0;
523
524         return t_data;
525 }
526
527 void thing_service_timebased_data_free(timebased_data *tb_data)
528 {
529         ret_if(!tb_data);
530
531         t_data = g_try_malloc0(sizeof(timebased_data));
532         if (!t_data)
533                 return NULL;
534
535         if (t_data->v_list)
536                 g_list_free_full(tb_data->v_list, __timebase_value_free);
537
538         g_free(tb_data);
539 }
540
541 int things_service_timebased_data_get_length(timebased_data *tb_data)
542 {
543         retv_if(!tb_data, -1);
544
545         return tb_data->length;
546 }
547
548 int
549 things_service_timebased_data_append_int(timebased_data *tb_data, int ivalue)
550 {
551         struct __timebased_int *tb_int = NULL;
552
553         retv_if(!tb_data, -1);
554         retv_if(tb_data->type != TS_VALUE_TYPE_INT, -1);
555
556         tb_int = g_try_malloc0(sizeof(struct __timebased_int));
557         retv_if(!tb_int, -1);
558
559         tb_int->i_time = common_get_epoch_coarse_time();
560         tb_int->value = ivalue;
561
562         tb_data->v_list = g_list_append(tb_data->v_list, tb_int);
563         tb_data->length++;
564
565         return 0;
566 }
567
568 int things_service_timebased_data_append_double(
569         timebased_data *tb_data, double dvalue)
570 {
571         struct __timebased_double *tb_double = NULL;
572
573         retv_if(!tb_data, -1);
574         retv_if(tb_data->type != TS_VALUE_TYPE_DOUBLE, -1);
575
576         tb_double = g_try_malloc0(sizeof(struct __timebased_double));
577         retv_if(!tb_double, -1);
578
579         tb_double->i_time = common_get_epoch_coarse_time();
580         tb_double->value = dvalue;
581
582         tb_data->v_list = g_list_append(tb_data->v_list, tb_double);
583         tb_data->length++;
584
585         return 0;
586 }
587
588 int
589 things_service_send_timebased_data(ts_handle handle, timebased_data *tb_data)
590 {
591         int ret = 0;
592         char *json_data = NULL;
593
594         retv_if(!handle, -1);
595         retv_if(!tb_data, -1);
596
597         json_data = __timebased_data_to_json(tb_data);
598         ret  = __ts_send_data(handle, json_data);
599         g_free(json_data);
600
601         if (!ret) { /* if success to send data, remove all items in tb_data */
602                 g_list_free_full(tb_data->v_list, __timebase_value_free);
603                 tb_data->v_list = NULL;
604                 tb_data->length = 0;
605         }
606
607         return ret;
608 }