Code Merge [Tizen3.0]: Applied tizen.org patches
[platform/core/connectivity/bluetooth-frwk.git] / bt-core / bt_core.c
1 /*
2  * bluetooth-frwk
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 <string.h>
23 #include <vconf.h>
24 #include <vconf-keys.h>
25
26 #include <dbus/dbus-glib.h>
27 #include <dbus/dbus.h>
28 #include <glib.h>
29
30 #include "bt_core.h"
31 #include "bt-internal-types.h"
32
33 static GMainLoop *main_loop = NULL;
34 static DBusGConnection *conn = NULL;
35
36 #ifdef __TIZEN_MOBILE__
37 typedef enum {
38         BT_DEACTIVATED,
39         BT_ACTIVATED,
40         BT_ACTIVATING,
41         BT_DEACTIVATING,
42 } bt_status_t;
43
44 static bt_status_t adapter_status = BT_DEACTIVATED;
45
46 static void __bt_core_set_status(bt_status_t status)
47 {
48         adapter_status = status;
49 }
50
51 static bt_status_t __bt_core_get_status(void)
52 {
53         return adapter_status;
54 }
55 #endif
56
57 static void __bt_core_terminate(void)
58 {
59         if (main_loop) {
60                 g_main_loop_quit(main_loop);
61         } else {
62                 BT_DBG("Terminating bt-core daemon");
63                 exit(0);
64         }
65 }
66
67 static gboolean bt_core_enable_adapter(BtCore *agent,
68                                                 DBusGMethodInvocation *context);
69
70 static gboolean bt_core_disable_adapter(BtCore *agent,
71                                                 DBusGMethodInvocation *context);
72
73 static gboolean bt_core_reset_adapter(BtCore *agent,
74                                                 DBusGMethodInvocation *context);
75
76 #include "bt_core_glue.h"
77
78 GType bt_core_get_type (void);
79
80
81 G_DEFINE_TYPE(BtCore, bt_core, G_TYPE_OBJECT);
82
83 /*This is part of platform provided code skeleton for client server model*/
84 static void bt_core_class_init (BtCoreClass *bt_core_class)
85 {
86         dbus_g_object_type_install_info(G_TYPE_FROM_CLASS(bt_core_class),
87                                         &dbus_glib_bt_core_object_info);
88 }
89
90 /*This is part of platform provided code skeleton for client server model*/
91 static void bt_core_init (BtCore *core)
92 {
93 }
94
95 typedef enum {
96         BT_CORE_ERROR_REJECT,
97         BT_CORE_ERROR_CANCEL,
98         BT_CORE_ERROR_TIMEOUT,
99 } BtCoreError;
100
101 #define BT_CORE_ERROR (bt_core_error_quark())
102
103 static GQuark bt_core_error_quark(void)
104 {
105         static GQuark quark = 0;
106         if (!quark)
107                 quark = g_quark_from_static_string("BtCore");
108
109         return quark;
110 }
111
112 #define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
113
114 static GError *bt_core_error(BtCoreError error, const char *err_msg)
115 {
116         return g_error_new(BT_CORE_ERROR, error, err_msg, NULL);
117 }
118
119 static DBusGProxy *_bt_get_connman_proxy(void)
120 {
121         DBusGProxy *proxy;
122
123         if (conn == NULL) {
124                 conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, NULL);
125                 retv_if(conn == NULL, NULL);
126         }
127
128         proxy = dbus_g_proxy_new_for_name(conn,
129                         CONNMAN_DBUS_NAME,
130                         CONNMAN_BLUETOOTH_TECHNOLOGY_PATH,
131                         CONNMAN_BLUETOTOH_TECHNOLOGY_INTERFACE);
132         retv_if(proxy == NULL, NULL);
133
134         return proxy;
135 }
136
137 static int _bt_power_adapter(gboolean powered)
138 {
139         GValue state = { 0 };
140         GError *error = NULL;
141         DBusGProxy *proxy;
142
143         proxy = _bt_get_connman_proxy();
144         retv_if(proxy == NULL, BLUETOOTH_ERROR_INTERNAL);
145
146         g_value_init(&state, G_TYPE_BOOLEAN);
147         g_value_set_boolean(&state, powered);
148
149         BT_DBG("set power property state: %d to connman", powered);
150
151         dbus_g_proxy_call(proxy, "SetProperty", &error,
152                                 G_TYPE_STRING, "Powered",
153                                 G_TYPE_VALUE, &state,
154                                 G_TYPE_INVALID, G_TYPE_INVALID);
155
156         if (error != NULL) {
157                 BT_ERR("Powered set err:[%s]", error->message);
158                 g_error_free(error);
159                 g_value_unset(&state);
160                 return BLUETOOTH_ERROR_INTERNAL;
161         }
162         return BLUETOOTH_ERROR_NONE;
163 }
164
165 static int __bt_enable_adapter(void)
166 {
167         BT_DBG("");
168 #ifdef __TIZEN_MOBILE__
169         int ret;
170
171         __bt_core_set_status(BT_ACTIVATING);
172
173         ret = system("/usr/etc/bluetooth/bt-stack-up.sh &");
174         if (ret < 0) {
175                 BT_DBG("running script failed");
176                 ret = system("/usr/etc/bluetooth/bt-dev-end.sh &");
177                 __bt_core_set_status(BT_DEACTIVATED);
178                 return -1;
179         }
180 #else
181         _bt_power_adapter(TRUE);
182 #endif
183         return 0;
184 }
185
186 static int __bt_disable_adapter(void)
187 {
188         BT_DBG("");
189
190 #ifdef __TIZEN_MOBILE__
191         __bt_core_set_status(BT_DEACTIVATING);
192
193         if (system("/usr/etc/bluetooth/bt-stack-down.sh &") < 0) {
194                 BT_DBG("running script failed");
195                 __bt_core_set_status(BT_ACTIVATED);
196                 return -1;
197         }
198 #else
199         _bt_power_adapter(FALSE);
200 #endif
201         __bt_core_terminate();
202         return 0;
203 }
204
205 static gboolean bt_core_enable_adapter(BtCore *agent,
206                                                 DBusGMethodInvocation *context)
207 {
208         char *sender = dbus_g_method_get_sender(context);
209         int ret;
210
211         if (sender == NULL)
212                 return FALSE;
213
214         ret = __bt_enable_adapter();
215         if (ret < 0) {
216                 GError *error = bt_core_error(BT_CORE_ERROR_REJECT,
217                                                         "Activation failed");
218                 dbus_g_method_return_error(context, error);
219                 g_error_free(error);
220                 g_free(sender);
221                 return FALSE;
222         } else {
223                 dbus_g_method_return(context);
224         }
225
226         g_free(sender);
227         return TRUE;
228 }
229
230 static gboolean bt_core_disable_adapter(BtCore *agent,
231                                                 DBusGMethodInvocation *context)
232 {
233         char *sender = dbus_g_method_get_sender(context);
234         int ret;
235
236         if (sender == NULL)
237                 return FALSE;
238
239         ret = __bt_disable_adapter();
240         if (ret < 0) {
241                 GError *error = bt_core_error(BT_CORE_ERROR_REJECT,
242                                                         "Deactivation failed");
243                 dbus_g_method_return_error(context, error);
244                 g_error_free(error);
245                 g_free(sender);
246                 return FALSE;
247         } else {
248                 dbus_g_method_return(context);
249         }
250
251         g_free(sender);
252         return TRUE;
253 }
254
255 static int __bt_reset_adapter(void)
256 {
257         /* Forcely terminate */
258         if (system("/usr/etc/bluetooth/bt-reset-env.sh &") < 0) {
259                 BT_DBG("running script failed");
260         }
261         __bt_core_terminate();
262         return 0;
263 }
264
265 static gboolean bt_core_reset_adapter(BtCore *agent,
266                                                 DBusGMethodInvocation *context)
267 {
268         char *sender = dbus_g_method_get_sender(context);
269         int ret;
270
271         if (sender == NULL)
272                 return FALSE;
273
274         ret = __bt_reset_adapter();
275         if (ret < 0) {
276                 GError *error = bt_core_error(BT_CORE_ERROR_REJECT,
277                                                         "Deactivation failed");
278                 dbus_g_method_return_error(context, error);
279                 g_error_free(error);
280                 g_free(sender);
281                 return FALSE;
282         } else {
283                 dbus_g_method_return(context);
284         }
285
286         g_free(sender);
287         return TRUE;
288 }
289
290 static int __bt_core_get_object_path(DBusMessage *msg, char **path)
291 {
292         DBusMessageIter item_iter;
293
294         dbus_message_iter_init(msg, &item_iter);
295
296         if (dbus_message_iter_get_arg_type(&item_iter)
297                                         != DBUS_TYPE_OBJECT_PATH) {
298                 BT_ERR("This is bad format dbus\n");
299                 return BLUETOOTH_ERROR_INTERNAL;
300         }
301
302         dbus_message_iter_get_basic(&item_iter, path);
303
304         if (*path == NULL)
305                 return BLUETOOTH_ERROR_INTERNAL;
306
307         return BLUETOOTH_ERROR_NONE;
308 }
309
310 static int __bt_core_get_owner_info(DBusMessage *msg, char **name,
311                                 char **previous, char **current)
312 {
313         DBusMessageIter item_iter;
314
315         dbus_message_iter_init(msg, &item_iter);
316
317         if (dbus_message_iter_get_arg_type(&item_iter)
318                                         != DBUS_TYPE_STRING) {
319                 BT_ERR("This is bad format dbus\n");
320                 return BLUETOOTH_ERROR_INTERNAL;
321         }
322
323         dbus_message_iter_get_basic(&item_iter, name);
324
325         if (*name == NULL)
326                 return BLUETOOTH_ERROR_INTERNAL;
327
328         dbus_message_iter_next(&item_iter);
329
330         if (dbus_message_iter_get_arg_type(&item_iter)
331                                         != DBUS_TYPE_STRING) {
332                 BT_ERR("This is bad format dbus\n");
333                 return BLUETOOTH_ERROR_INTERNAL;
334         }
335
336         dbus_message_iter_get_basic(&item_iter, previous);
337
338         if (*previous == NULL)
339                 return BLUETOOTH_ERROR_INTERNAL;
340
341         dbus_message_iter_next(&item_iter);
342
343         if (dbus_message_iter_get_arg_type(&item_iter)
344                                         != DBUS_TYPE_STRING) {
345                 BT_ERR("This is bad format dbus\n");
346                 return BLUETOOTH_ERROR_INTERNAL;
347         }
348
349         dbus_message_iter_get_basic(&item_iter, current);
350
351         if (*current == NULL)
352                 return BLUETOOTH_ERROR_INTERNAL;
353
354         return BLUETOOTH_ERROR_NONE;
355 }
356
357 static DBusHandlerResult __bt_core_event_filter(DBusConnection *conn,
358                                            DBusMessage *msg, void *data)
359 {
360         char *object_path = NULL;
361         const char *member = dbus_message_get_member(msg);
362
363         if (dbus_message_get_type(msg) != DBUS_MESSAGE_TYPE_SIGNAL)
364                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
365
366         if (member == NULL)
367                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
368
369         if (strcasecmp(member, "InterfacesAdded") == 0) {
370                 if (__bt_core_get_object_path(msg, &object_path)) {
371                         BT_ERR("Fail to get the path");
372                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
373                 }
374 #ifdef __TIZEN_MOBILE__
375                 if (strcasecmp(object_path, "/org/bluez/hci0") == 0) {
376                         __bt_core_set_status(BT_ACTIVATED);
377                 }
378 #endif
379         } else if (strcasecmp(member, "InterfacesRemoved") == 0) {
380                 if (__bt_core_get_object_path(msg, &object_path)) {
381                         BT_ERR("Fail to get the path");
382                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
383                 }
384
385                 if (strcasecmp(object_path, "/org/bluez/hci0") == 0) {
386 #ifdef __TIZEN_MOBILE__
387                         __bt_core_set_status(BT_DEACTIVATED);
388 #endif
389                         __bt_core_terminate();
390                 }
391         } else if (strcasecmp(member, "NameOwnerChanged") == 0) {
392                 char *name = NULL;
393                 char *previous = NULL;
394                 char *current = NULL;
395
396                 if (__bt_core_get_owner_info(msg, &name, &previous, &current)) {
397                         BT_ERR("Fail to get the owner info");
398                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
399                 }
400
401                 if (*current != '\0')
402                         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
403
404                 if (strcasecmp(name, "org.bluez") == 0) {
405                         BT_DBG("Bluetoothd is terminated");
406                         __bt_disable_adapter();
407                         __bt_core_terminate();
408                 } else if (strcasecmp(name, "org.projectx.bt") == 0) {
409                         BT_DBG("bt-service is terminated abnormally");
410                         __bt_disable_adapter();
411                 }
412         }
413
414         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
415 }
416
417 static DBusGProxy *__bt_core_register_event_filter(DBusGConnection *g_conn,
418                                                 BtCore *bt_core)
419 {
420         DBusError dbus_error;
421         DBusConnection *conn;
422         DBusGProxy *proxy;
423         GError *err = NULL;
424         guint result = 0;
425
426         if (g_conn == NULL)
427                 return NULL;
428
429         conn = dbus_g_connection_get_connection(g_conn);
430         if (conn == NULL)
431                 return NULL;
432
433         proxy = dbus_g_proxy_new_for_name(g_conn, DBUS_SERVICE_DBUS,
434                                 DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS);
435         if (proxy == NULL) {
436                 BT_ERR("proxy is NULL");
437                 return NULL;
438         }
439
440         if (!dbus_g_proxy_call(proxy, "RequestName", &err, G_TYPE_STRING,
441                         BT_CORE_NAME, G_TYPE_UINT, 0, G_TYPE_INVALID,
442                         G_TYPE_UINT, &result, G_TYPE_INVALID)) {
443                 if (err != NULL) {
444                         BT_ERR("RequestName RPC failed[%s]\n", err->message);
445                         g_error_free(err);
446                 }
447                 g_object_unref(proxy);
448                 return NULL;
449         }
450
451         if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
452                 BT_ERR("Failed to get the primary well-known name.\n");
453                 g_object_unref(proxy);
454                 return NULL;
455         }
456
457         if (!dbus_connection_add_filter(conn, __bt_core_event_filter,
458                                         NULL, NULL)) {
459                 BT_ERR("Fail to add filter");
460                 g_object_unref(proxy);
461                 return NULL;
462         }
463
464         dbus_error_init(&dbus_error);
465
466         dbus_bus_add_match(conn,
467                         "type='signal',interface='org.freedesktop.DBus'"
468                         ",member='NameOwnerChanged'",
469                         &dbus_error);
470
471         if (dbus_error_is_set(&dbus_error)) {
472                 BT_ERR("Fail to add match: %s\n", dbus_error.message);
473                 dbus_error_free(&dbus_error);
474                 g_object_unref(proxy);
475                 return NULL;
476         }
477
478         dbus_bus_add_match(conn,
479                         "type='signal',interface='org.freedesktop.DBus.ObjectManager'"
480                         ",member='InterfacesAdded'",
481                         &dbus_error);
482
483         if (dbus_error_is_set(&dbus_error)) {
484                 BT_ERR("Fail to add match: %s\n", dbus_error.message);
485                 dbus_error_free(&dbus_error);
486                 g_object_unref(proxy);
487                 return NULL;
488         }
489
490         dbus_bus_add_match(conn,
491                         "type='signal',interface='org.freedesktop.DBus.ObjectManager'"
492                         ",member='InterfacesRemoved'",
493                         &dbus_error);
494
495         if (dbus_error_is_set(&dbus_error)) {
496                 BT_ERR("Fail to add match: %s\n", dbus_error.message);
497                 dbus_error_free(&dbus_error);
498                 g_object_unref(proxy);
499                 return NULL;
500         }
501
502         dbus_g_connection_register_g_object(g_conn, BT_CORE_PATH,
503                                         G_OBJECT(bt_core));
504
505         return proxy;
506 }
507
508 static void __bt_unregister_event_filter(DBusGConnection *g_conn,
509                                         BtCore *bt_core,
510                                         DBusGProxy *dbus_proxy)
511 {
512         DBusConnection *conn;
513
514         if (g_conn == NULL ||
515              bt_core == NULL ||
516               dbus_proxy == NULL) {
517                 BT_ERR("Invalid parameter");
518                 return;
519         }
520
521         conn = dbus_g_connection_get_connection(g_conn);
522
523         dbus_connection_remove_filter(conn, __bt_core_event_filter, NULL);
524
525         dbus_g_connection_unregister_g_object(g_conn, G_OBJECT(bt_core));
526
527         g_object_unref(bt_core);
528         g_object_unref(dbus_proxy);
529 }
530
531 static void __bt_core_sigterm_handler(int signo)
532 {
533         BT_DBG("Got the signal: %d", signo);
534
535         __bt_core_terminate();
536 }
537
538 int main(void)
539 {
540         GError *error = NULL;
541         BtCore *bt_core;
542         DBusGProxy *dbus_proxy = NULL;
543         struct sigaction sa;
544
545         BT_DBG("+");
546
547         conn = dbus_g_bus_get(DBUS_BUS_SYSTEM, &error);
548         if (error != NULL) {
549                 BT_ERR("ERROR: Can't get on system bus [%s]", error->message);
550                 g_error_free(error);
551                 return FALSE;
552         }
553
554         bt_core = g_object_new(BT_CORE_TYPE, NULL);
555         if (bt_core == NULL) {
556                 BT_ERR("bt_service is NULL");
557                 goto fail;
558         }
559
560         dbus_proxy = __bt_core_register_event_filter(conn, bt_core);
561         if (!dbus_proxy) {
562                 BT_ERR("__bt_core_register_event_filter failed");
563                 g_object_unref(bt_core);
564                 bt_core = NULL;
565                 goto fail;
566         }
567
568         main_loop = g_main_loop_new(NULL, FALSE);
569
570         memset(&sa, 0, sizeof(sa));
571         sa.sa_handler = __bt_core_sigterm_handler;
572         sigaction(SIGINT, &sa, NULL);
573         sigaction(SIGTERM, &sa, NULL);
574
575         g_main_loop_run(main_loop);
576
577 fail:
578         __bt_unregister_event_filter(conn, bt_core, dbus_proxy);
579
580         if (main_loop)
581                 g_main_loop_unref(main_loop);
582
583         dbus_g_connection_unref(conn);
584
585         BT_DBG("-");
586         return FALSE;
587 }
588