ff20c1b03696ba0cf596da80a83389512f32eab0
[platform/core/system/libsvi.git] / src / dbus.c
1 /*
2  * feedback
3  *
4  * Copyright (c) 2012 - 2013 Samsung Electronics Co., Ltd.
5  *
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
9  *
10  *       http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  */
18
19
20 #include <stdio.h>
21 #include <stdlib.h>
22 #include <stdint.h>
23 #include <stdbool.h>
24 #include <errno.h>
25
26 #include "common.h"
27 #include "log.h"
28 #include "dbus.h"
29
30 /* -1 is a default timeout value, it's converted to 25*1000 internally. */
31 #define DBUS_REPLY_TIMEOUT      (-1)
32
33 /** extract from dbus/dbus-protocol.h
34  * (GDbus use the same maximum value.)
35  * Max length in bytes of a bus name, interface, or member (not object
36  * path, paths are unlimited). This is limited because lots of stuff
37  * is O(n) in this number, plus it would be obnoxious to type in a
38  * paragraph-long method name so most likely something like that would
39  * be an exploit.
40  */
41 #define DBUS_MAXIMUM_NAME_LENGTH 255
42
43 #define SIGNAL_VIBRATOR_INITIATED "InitiateVibrator"
44
45 struct feedback_restart_callback {
46         feedback_restart_cb func;
47         guint feedback_id;
48 };
49
50 struct proxy_node {
51         GDBusProxy *proxy;
52         char *dest;
53         char *path;
54         char *interface;
55 };
56
57 static GList *proxy_pool;
58 static pthread_mutex_t dmutex = PTHREAD_MUTEX_INITIALIZER;
59 static int bus_init;
60
61 static dd_list *callback_list;
62
63 static int g_dbus_error_to_errno(int code)
64 {
65         /**
66          * if device is not supported,
67          * deviced does not register the method call of the device.
68          * in this case, dbus will return UNKNOWN_METHOD error.
69          */
70         /* refer to gio/gioenums.h */
71         if (code == G_DBUS_ERROR_ACCESS_DENIED)
72                 return -EACCES;
73         else if (code == G_DBUS_ERROR_UNKNOWN_METHOD)
74                 return -ENOTSUP;
75         return -ECOMM;
76 }
77
78 static GVariant *append_g_variant(const char *sig, const char *param[])
79 {
80         GVariantBuilder builder;
81         char *ch;
82         int i;
83         struct dbus_byte *bytes;
84
85         if (!sig || !param)
86                 return NULL;
87
88         g_variant_builder_init(&builder, G_VARIANT_TYPE_TUPLE);
89
90         for (ch = (char *)sig, i = 0; *ch != '\0'; ++i, ++ch) {
91                 switch (*ch) {
92                 case 'i':
93                         g_variant_builder_add(&builder, "i", atoi(param[i]));
94                         break;
95                 case 'u':
96                         g_variant_builder_add(&builder, "u", strtoul(param[i], NULL, 10));
97                         break;
98                 case 't':
99                         g_variant_builder_add(&builder, "t", atoll(param[i]));
100                         break;
101                 case 's':
102                         g_variant_builder_add(&builder, "s", param[i]);
103                         break;
104                 case 'a':
105                         ++ch;
106                         switch (*ch) {
107                         case 'y':
108                                 ++i;
109                                 bytes = (struct dbus_byte *)param[i];
110                                 g_variant_builder_add(&builder, "@ay",
111                                         g_variant_new_from_data(G_VARIANT_TYPE("ay"),
112                                         bytes->data, bytes->size, TRUE, NULL, NULL));
113                                 break;
114                         default:
115                                 break;
116                         }
117                         break;
118                 default:
119                         return NULL;
120                 }
121         }
122
123         return g_variant_builder_end(&builder);
124 }
125
126 static struct proxy_node *find_matched_proxy_node(const char *dest,
127                 const char *path,
128                 const char *interface)
129 {
130         GList *elem;
131         struct proxy_node *node;
132         int plen;
133
134         if (!dest || !path || !interface)
135                 return NULL;
136
137         plen = strlen(path) + 1;
138
139         /* find matched proxy object */
140         for (elem = proxy_pool; elem; elem = elem->next) {
141                 node = elem->data;
142                 if (!node)
143                         continue;
144                 if (!strncmp(node->dest, dest, DBUS_MAXIMUM_NAME_LENGTH) &&
145                     !strncmp(node->path, path, plen) &&
146                     !strncmp(node->interface, interface,
147                             DBUS_MAXIMUM_NAME_LENGTH))
148                         return node;
149         }
150
151         return NULL;
152 }
153
154 //LCOV_EXCL_START Not called Callback
155 static void on_name_vanished(GDBusConnection *connection,
156                 const gchar *name,
157                 gpointer user_data)
158 {
159         GList *elem;
160         GList *next;
161         struct proxy_node *node;
162
163         pthread_mutex_lock(&dmutex);
164         for (elem = proxy_pool, next = g_list_next(elem); elem;
165                 elem = next, next = g_list_next(elem)) {
166                 node = elem->data;
167                 if (!node)
168                         continue;
169                 proxy_pool = g_list_delete_link(proxy_pool, elem);
170                 g_object_unref(node->proxy);
171                 free(node->dest);
172                 free(node->path);
173                 free(node->interface);
174                 free(node);
175         }
176         pthread_mutex_unlock(&dmutex);
177 }
178 //LCOV_EXCL_STOP
179
180 static GDBusConnection *get_dbus_connection(void)
181 {
182         GError *err = NULL;
183         static GDBusConnection *conn;
184
185         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
186         if (!conn) {
187                 //LCOV_EXCL_START System Error
188                 if (err)
189                         _D("Fail to get dbus connection: %s", err->message);
190                 else
191                         _D("Fail to get dbus connection");
192                 return NULL;
193                 //LCOV_EXCL_STOP
194         }
195
196         return conn;
197 }
198
199 static GDBusProxy *get_proxy_from_proxy_pool(const char *dest,
200                 const char *path,
201                 const char *interface,
202                 GError **err)
203 {
204         GDBusConnection *conn;
205         GDBusProxy *proxy;
206         struct proxy_node *node;
207
208         if (!dest || !path || !interface) {
209                 if (err)
210                         g_set_error(err, G_IO_ERROR,
211                                         G_IO_ERROR_INVALID_ARGUMENT,
212                                         "Cannot determine destination address");
213                 return NULL;
214         }
215
216         /* find matched proxy node in proxy pool */
217         node = find_matched_proxy_node(dest, path, interface);
218         if (node)
219                 return node->proxy;
220
221         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, err);
222         if (!conn)
223                 return NULL;
224
225         if (!bus_init) {
226                 bus_init++;
227                 g_bus_watch_name_on_connection(conn,
228                         DEVICED_BUS_NAME,
229                         G_BUS_NAME_WATCHER_FLAGS_NONE,
230                         NULL,
231                         on_name_vanished,
232                         NULL,
233                         NULL);
234         }
235
236         proxy = g_dbus_proxy_new_sync(conn,
237                         G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
238                         NULL,      /* GDBusinterfaceinfo */
239                         dest,      /* bus name */
240                         path,      /* object path */
241                         interface, /* interface name */
242                         NULL,      /* GCancellable */
243                         err);
244         if (!proxy)
245                 return NULL;
246
247         node = malloc(sizeof(struct proxy_node));
248         if (!node) {
249                 //LCOV_EXCL_START System Error
250                 g_object_unref(proxy);
251                 if (err)
252                         g_set_error(err, G_IO_ERROR,
253                                         G_IO_ERROR_FAILED,
254                                         "Cannot allocate proxy_node memory");
255                 return NULL;
256                 //LCOV_EXCL_STOP
257         }
258
259         node->proxy = proxy;
260         node->dest = strdup(dest);
261         node->path = strdup(path);
262         node->interface = strdup(interface);
263
264         proxy_pool = g_list_append(proxy_pool, node);
265
266         return proxy;
267 }
268
269 int dbus_method_sync_var(const char *dest, const char *path,
270                 const char *interface, const char *method, GVariant *param)
271 {
272         GDBusProxy *proxy;
273         GError *err = NULL;
274         GVariant *output;
275         int result;
276
277 #if !GLIB_CHECK_VERSION(2, 35, 0)
278         g_type_init();
279 #endif
280
281         pthread_mutex_lock(&dmutex);
282         proxy = get_proxy_from_proxy_pool(dest, path, interface, &err);
283 //LCOV_EXCL_START System Error
284         if (!proxy) {
285                 pthread_mutex_unlock(&dmutex);
286                 _E("fail to get proxy from proxy pool : %s-%s (%d-%s)",
287                                 interface, method, err->code, err->message);
288                 result = g_dbus_error_to_errno(err->code);
289                 g_clear_error(&err);
290                 return result;
291         }
292 //LCOV_EXCL_STOP
293         output = g_dbus_proxy_call_sync(proxy,
294                         method,                       /* method name */
295                         param, /* parameters */
296                         G_DBUS_CALL_FLAGS_NONE,
297                         DBUS_REPLY_TIMEOUT,           /* timeout */
298                         NULL,                         /* GCancellable */
299                         &err);
300         pthread_mutex_unlock(&dmutex);
301
302 //LCOV_EXCL_START System Error
303         if (!output) {
304                 if (!err) {
305                         _E("g_dbus_proxy_call_sync error : %s-%s",
306                                         interface, method);
307                         return -EPERM;
308                 }
309                 _E("g_dbus_proxy_call_sync error : %s-%s (%d-%s)",
310                                 interface, method, err->code, err->message);
311                 result = g_dbus_error_to_errno(err->code);
312                 g_clear_error(&err);
313                 return result;
314 //LCOV_EXCL_STOP
315         }
316
317         /* get output value */
318         g_variant_get(output, "(i)", &result);
319
320         g_variant_unref(output);
321
322         return result;
323 }
324
325 int dbus_method_sync(const char *dest, const char *path,
326                 const char *interface, const char *method,
327                 const char *sig, const char *param[])
328 {
329         return dbus_method_sync_var(dest,
330                         path,
331                         interface,
332                         method,
333                         append_g_variant(sig, param));
334 }
335
336 //LCOV_EXCL_START Not called Callback
337 static void feedback_signal_callback(GDBusConnection *conn,
338                 const gchar *sender,
339                 const gchar *path,
340                 const gchar *iface,
341                 const gchar *signal,
342                 GVariant *params,
343                 gpointer user_data)
344 {
345         size_t iface_len, signal_len;
346         struct feedback_restart_callback *callback;
347         dd_list *elem;
348         int ret;
349
350         if (!params || !sender || !path || !iface || !signal)
351                 return;
352
353         iface_len = strlen(iface) + 1;
354         signal_len = strlen(signal) + 1;
355
356         if (strncmp(iface, VIBRATOR_INTERFACE_HAPTIC, iface_len))
357                 return;
358
359         if (strncmp(signal, SIGNAL_VIBRATOR_INITIATED, signal_len))
360                 return;
361
362         DD_LIST_FOREACH(callback_list, elem, callback) {
363                 if (!callback->func)
364                         continue;
365                 ret = callback->func();
366                 if (ret < 0)
367                         _E("Failed to call restart callback");
368         }
369 }
370 //LCOV_EXCL_STOP
371
372 int register_signal_handler(feedback_restart_cb func)
373 {
374         GDBusConnection *conn;
375         guint feedback_id = 0;
376         struct feedback_restart_callback *callback;
377         dd_list *elem;
378
379         DD_LIST_FOREACH(callback_list, elem, callback) {
380                 if (callback->func != func)
381                         continue;
382                 if (callback->feedback_id == 0)
383                         continue;
384
385                 return -EEXIST;
386         }
387
388         callback = (struct feedback_restart_callback *)malloc(sizeof(struct feedback_restart_callback));
389         if (!callback) {
390 //LCOV_EXCL_START System Error
391                 _E("malloc() failed");
392                 return -ENOMEM;
393 //LCOV_EXCL_STOP
394         }
395
396         conn = get_dbus_connection();
397         if (!conn) {
398 //LCOV_EXCL_START System Error
399                 free(callback);
400                 _E("Failed to get dbus connection");
401                 return -EPERM;
402 //LCOV_EXCL_STOP
403         }
404
405         feedback_id = g_dbus_connection_signal_subscribe(conn,
406                         NULL,
407                         VIBRATOR_INTERFACE_HAPTIC,
408                         NULL,
409                         NULL,
410                         NULL,
411                         G_DBUS_SIGNAL_FLAGS_NONE,
412                         feedback_signal_callback,
413                         NULL,
414                         NULL);
415         if (feedback_id == 0) {
416 //LCOV_EXCL_START System Error
417                 free(callback);
418                 _E("Failed to subscrive bus signal");
419                 return -EPERM;
420 //LCOV_EXCL_STOP
421         }
422
423         callback->func = func;
424         callback->feedback_id = feedback_id;
425
426         DD_LIST_APPEND(callback_list, callback);
427
428         return 0;
429 }
430
431 int unregister_signal_handler(feedback_restart_cb func)
432 {
433         GDBusConnection *conn;
434         struct feedback_restart_callback *callback;
435         dd_list *elem;
436
437         if (!func)
438                 return -EINVAL;
439
440         conn = get_dbus_connection();
441         if (!conn) {
442 //LCOV_EXCL_START System Error
443                 _E("Failed to get dbus connection");
444                 return -EPERM;
445 //LCOV_EXCL_STOP
446         }
447
448         DD_LIST_FOREACH(callback_list, elem, callback) {
449                 if (callback->func != func)
450                         continue;
451                 if (callback->feedback_id > 0)
452                         g_dbus_connection_signal_unsubscribe(conn, callback->feedback_id);
453                 DD_LIST_REMOVE(callback_list, callback);
454                 free(callback);
455         }
456
457         return 0;
458 }