0121f50a52e61a1c1b707ffaee59876b91ff981a
[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, 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 static void on_name_vanished(GDBusConnection *connection,
155                 const gchar *name,
156                 gpointer user_data)
157 {
158         GList *elem;
159         GList *next;
160         struct proxy_node *node;
161
162         pthread_mutex_lock(&dmutex);
163         for (elem = proxy_pool, next = g_list_next(elem); elem;
164                 elem = next, next = g_list_next(elem)) {
165                 node = elem->data;
166                 if (!node)
167                         continue;
168                 proxy_pool = g_list_delete_link(proxy_pool, elem);
169                 g_object_unref(node->proxy);
170                 free(node->dest);
171                 free(node->path);
172                 free(node->interface);
173                 free(node);
174         }
175         pthread_mutex_unlock(&dmutex);
176 }
177
178 static GDBusConnection *get_dbus_connection(void)
179 {
180         GError *err = NULL;
181         static GDBusConnection *conn;
182
183         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, &err);
184         if (!conn) {
185                 if (err)
186                         _D("Fail to get dbus connection: %s", err->message);
187                 else
188                         _D("Fail to get dbus connection");
189                 return NULL;
190         }
191
192         return conn;
193 }
194
195 static GDBusProxy *get_proxy_from_proxy_pool(const char *dest,
196                 const char *path,
197                 const char *interface,
198                 GError **err)
199 {
200         GDBusConnection *conn;
201         GDBusProxy *proxy;
202         struct proxy_node *node;
203
204         if (!dest || !path || !interface) {
205                 if (err)
206                         g_set_error(err, G_IO_ERROR,
207                                         G_IO_ERROR_INVALID_ARGUMENT,
208                                         "Cannot determine destination address");
209                 return NULL;
210         }
211
212         /* find matched proxy node in proxy pool */
213         node = find_matched_proxy_node(dest, path, interface);
214         if (node)
215                 return node->proxy;
216
217         conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, err);
218         if (!conn)
219                 return NULL;
220
221         if (!bus_init) {
222                 bus_init++;
223                 g_bus_watch_name_on_connection(conn,
224                         DEVICED_BUS_NAME,
225                         G_BUS_NAME_WATCHER_FLAGS_NONE,
226                         NULL,
227                         on_name_vanished,
228                         NULL,
229                         NULL);
230         }
231
232         proxy = g_dbus_proxy_new_sync(conn,
233                         G_DBUS_PROXY_FLAGS_NONE,
234                         NULL,      /* GDBusinterfaceinfo */
235                         dest,      /* bus name */
236                         path,      /* object path */
237                         interface, /* interface name */
238                         NULL,      /* GCancellable */
239                         err);
240         if (!proxy)
241                 return NULL;
242
243         node = malloc(sizeof(struct proxy_node));
244         if (!node) {
245                 g_object_unref(proxy);
246                 if (err)
247                         g_set_error(err, G_IO_ERROR,
248                                         G_IO_ERROR_FAILED,
249                                         "Cannot allocate proxy_node memory");
250                 return NULL;
251         }
252
253         node->proxy = proxy;
254         node->dest = strdup(dest);
255         node->path = strdup(path);
256         node->interface = strdup(interface);
257
258         proxy_pool = g_list_append(proxy_pool, node);
259
260         return proxy;
261 }
262
263 int dbus_method_sync(const char *dest, const char *path,
264                 const char *interface, const char *method,
265                 const char *sig, char *param[])
266 {
267         GDBusProxy *proxy;
268         GError *err = NULL;
269         GVariant *output;
270         int result;
271
272 #if !GLIB_CHECK_VERSION(2, 35, 0)
273         g_type_init();
274 #endif
275
276         pthread_mutex_lock(&dmutex);
277         proxy = get_proxy_from_proxy_pool(dest, path, interface, &err);
278         if (!proxy) {
279                 pthread_mutex_unlock(&dmutex);
280                 _E("fail to get proxy from proxy pool : %s-%s (%d-%s)",
281                                 interface, method, err->code, err->message);
282                 result = g_dbus_error_to_errno(err->code);
283                 g_clear_error(&err);
284                 return result;
285         }
286
287         output = g_dbus_proxy_call_sync(proxy,
288                         method,                       /* method name */
289                         append_g_variant(sig, param), /* parameters */
290                         G_DBUS_CALL_FLAGS_NONE,
291                         DBUS_REPLY_TIMEOUT,           /* timeout */
292                         NULL,                         /* GCancellable */
293                         &err);
294         pthread_mutex_unlock(&dmutex);
295
296         if (!output) {
297                 if (err) {
298                         _E("g_dbus_proxy_call_sync error : %s-%s (%d-%s)",
299                                         interface, method, err->code, err->message);
300                         result = g_dbus_error_to_errno(err->code);
301                         g_clear_error(&err);
302                 } else {
303                         _E("g_dbus_proxy_call_sync error : %s-%s",
304                                         interface, method);
305                         result = -ECOMM;
306                 }
307                 return result;
308         }
309
310         /* get output value */
311         g_variant_get(output, "(i)", &result);
312
313         g_variant_unref(output);
314
315         return result;
316 }
317
318 static void feedback_signal_callback(GDBusConnection *conn,
319                 const gchar *sender,
320                 const gchar *path,
321                 const gchar *iface,
322                 const gchar *signal,
323                 GVariant *params,
324                 gpointer user_data)
325 {
326         size_t iface_len, signal_len;
327         struct feedback_restart_callback *callback;
328         dd_list *elem;
329         int ret;
330
331         if (!params || !sender || !path || !iface || !signal)
332                 return;
333
334         iface_len = strlen(iface) + 1;
335         signal_len = strlen(signal) + 1;
336
337         if (strncmp(iface, VIBRATOR_INTERFACE_HAPTIC, iface_len))
338                 return;
339
340         if (strncmp(signal, SIGNAL_VIBRATOR_INITIATED, signal_len))
341                 return;
342
343         DD_LIST_FOREACH(callback_list, elem, callback) {
344                 if (!callback->func)
345                         continue;
346                 ret = callback->func();
347                 if (ret < 0)
348                         _E("Failed to call restart callback");
349         }
350 }
351
352 int register_signal_handler(feedback_restart_cb func)
353 {
354         GDBusConnection *conn;
355         guint feedback_id = 0;
356         struct feedback_restart_callback *callback;
357         dd_list *elem;
358
359         DD_LIST_FOREACH(callback_list, elem, callback) {
360                 if (callback->func != func)
361                         continue;
362                 if (callback->feedback_id == 0)
363                         continue;
364
365                 return -EEXIST;
366         }
367
368         callback = (struct feedback_restart_callback *)malloc(sizeof(struct feedback_restart_callback));
369         if (!callback) {
370 //LCOV_EXCL_START System Error
371                 _E("malloc() failed");
372                 return -ENOMEM;
373 //LCOV_EXCL_STOP
374         }
375
376         conn = get_dbus_connection();
377         if (!conn) {
378 //LCOV_EXCL_START System Error
379                 free(callback);
380                 _E("Failed to get dbus connection");
381                 return -EPERM;
382 //LCOV_EXCL_STOP
383         }
384
385         feedback_id = g_dbus_connection_signal_subscribe(conn,
386                         NULL,
387                         VIBRATOR_INTERFACE_HAPTIC,
388                         NULL,
389                         NULL,
390                         NULL,
391                         G_DBUS_SIGNAL_FLAGS_NONE,
392                         feedback_signal_callback,
393                         NULL,
394                         NULL);
395         if (feedback_id == 0) {
396                 free(callback);
397                 _E("Failed to subscrive bus signal");
398                 return -EPERM;
399         }
400
401         callback->func = func;
402         callback->feedback_id = feedback_id;
403
404         DD_LIST_APPEND(callback_list, callback);
405
406         return 0;
407 }
408
409 int unregister_signal_handler(feedback_restart_cb func)
410 {
411         GDBusConnection *conn;
412         struct feedback_restart_callback *callback;
413         dd_list *elem;
414
415         if (!func)
416                 return -EINVAL;
417
418         conn = get_dbus_connection();
419         if (!conn) {
420 //LCOV_EXCL_START System Error
421                 free(callback);
422                 _E("Failed to get dbus connection");
423                 return -EPERM;
424 //LCOV_EXCL_STOP
425         }
426
427         DD_LIST_FOREACH(callback_list, elem, callback) {
428                 if (callback->func != func)
429                         continue;
430                 if (callback->feedback_id > 0)
431                         g_dbus_connection_signal_unsubscribe(conn, callback->feedback_id);
432                 DD_LIST_REMOVE(callback_list, callback);
433                 free(callback);
434         }
435
436         return 0;
437 }