Ignore Bluetooth adapters that are currently down
[platform/upstream/connman.git] / plugins / bluetooth.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2009  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <netinet/ether.h>
31
32 #include <gdbus.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/device.h>
37 #include <connman/inet.h>
38 #include <connman/dbus.h>
39 #include <connman/log.h>
40
41 #define BLUEZ_SERVICE                   "org.bluez"
42 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
43 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
44 #define BLUEZ_DEVICE_INTERFACE          BLUEZ_SERVICE ".Device"
45 #define BLUEZ_NETWORK_INTERFACE         BLUEZ_SERVICE ".Network"
46
47 #define LIST_ADAPTERS                   "ListAdapters"
48 #define ADAPTER_ADDED                   "AdapterAdded"
49 #define ADAPTER_REMOVED                 "AdapterRemoved"
50
51 #define PROPERTY_CHANGED                "PropertyChanged"
52 #define GET_PROPERTIES                  "GetProperties"
53 #define SET_PROPERTY                    "SetProperty"
54
55 #define CONNECT                         "Connect"
56 #define DISCONNECT                      "Disconnect"
57
58 #define UUID_NAP        "00001116-0000-1000-8000-00805f9b34fb"
59
60 #define TIMEOUT 5000
61
62 static DBusConnection *connection;
63
64 static GHashTable *bluetooth_devices = NULL;
65
66 static int pan_probe(struct connman_network *network)
67 {
68         DBG("network %p", network);
69
70         return 0;
71 }
72
73 static void pan_remove(struct connman_network *network)
74 {
75         DBG("network %p", network);
76 }
77
78 static void connect_reply(DBusPendingCall *call, void *user_data)
79 {
80         struct connman_network *network = user_data;
81         DBusMessage *reply;
82         DBusError error;
83         const char *interface = NULL;
84         int index;
85
86         DBG("network %p", network);
87
88         reply = dbus_pending_call_steal_reply(call);
89         if (reply == NULL)
90                 return;
91
92         dbus_error_init(&error);
93
94         if (dbus_message_get_args(reply, &error,
95                                         DBUS_TYPE_STRING, &interface,
96                                                 DBUS_TYPE_INVALID) == FALSE) {
97                 if (dbus_error_is_set(&error) == TRUE) {
98                         connman_error("%s", error.message);
99                         dbus_error_free(&error);
100                 } else
101                         connman_error("Wrong arguments for connect");
102                 goto done;
103         }
104
105         if (interface == NULL)
106                 goto done;
107
108         DBG("interface %s", interface);
109
110         index = connman_inet_ifindex(interface);
111
112         connman_network_set_index(network, index);
113         connman_network_set_connected(network, TRUE);
114
115 done:
116         dbus_message_unref(reply);
117 }
118
119 static int pan_connect(struct connman_network *network)
120 {
121         const char *path = connman_network_get_string(network, "Path");
122         const char *uuid = "nap";
123         DBusMessage *message;
124         DBusPendingCall *call;
125
126         DBG("network %p", network);
127
128         if (path == NULL)
129                 return -EINVAL;
130
131         if (connman_network_get_index(network) >= 0)
132                 return -EISCONN;
133
134         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
135                                         BLUEZ_NETWORK_INTERFACE, CONNECT);
136         if (message == NULL)
137                 return -ENOMEM;
138
139         dbus_message_set_auto_start(message, FALSE);
140
141         dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
142                                                         DBUS_TYPE_INVALID);
143
144         if (dbus_connection_send_with_reply(connection, message,
145                                         &call, TIMEOUT * 10) == FALSE) {
146                 connman_error("Failed to connect service");
147                 dbus_message_unref(message);
148                 return -EINVAL;
149         }
150
151         if (call == NULL) {
152                 connman_error("D-Bus connection not available");
153                 dbus_message_unref(message);
154                 return -EINVAL;
155         }
156
157         dbus_pending_call_set_notify(call, connect_reply, network, NULL);
158
159         dbus_message_unref(message);
160
161         return -EINPROGRESS;
162 }
163
164 static void disconnect_reply(DBusPendingCall *call, void *user_data)
165 {
166         struct connman_network *network = user_data;
167         DBusMessage *reply;
168         DBusError error;
169
170         DBG("network %p", network);
171
172         reply = dbus_pending_call_steal_reply(call);
173         if (reply == NULL)
174                 return;
175
176         dbus_error_init(&error);
177
178         if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
179                 if (dbus_error_is_set(&error) == TRUE) {
180                         connman_error("%s", error.message);
181                         dbus_error_free(&error);
182                 } else
183                         connman_error("Wrong arguments for disconnect");
184                 goto done;
185         }
186
187         connman_network_set_connected(network, FALSE);
188         connman_network_set_index(network, -1);
189
190 done:
191         dbus_message_unref(reply);
192 }
193
194 static int pan_disconnect(struct connman_network *network)
195 {
196         const char *path = connman_network_get_string(network, "Path");
197         DBusMessage *message;
198         DBusPendingCall *call;
199
200         DBG("network %p", network);
201
202         if (path == NULL)
203                 return -EINVAL;
204
205         if (connman_network_get_index(network) < 0)
206                 return -ENOTCONN;
207
208         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
209                                         BLUEZ_NETWORK_INTERFACE, DISCONNECT);
210         if (message == NULL)
211                 return -ENOMEM;
212
213         dbus_message_set_auto_start(message, FALSE);
214
215         dbus_message_append_args(message, DBUS_TYPE_INVALID);
216
217         if (dbus_connection_send_with_reply(connection, message,
218                                                 &call, TIMEOUT) == FALSE) {
219                 connman_error("Failed to disconnect service");
220                 dbus_message_unref(message);
221                 return -EINVAL;
222         }
223
224         if (call == NULL) {
225                 connman_error("D-Bus connection not available");
226                 dbus_message_unref(message);
227                 return -EINVAL;
228         }
229
230         dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
231
232         dbus_message_unref(message);
233
234         return 0;
235 }
236
237 static struct connman_network_driver pan_driver = {
238         .name           = "bluetooth-pan",
239         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
240         .probe          = pan_probe,
241         .remove         = pan_remove,
242         .connect        = pan_connect,
243         .disconnect     = pan_disconnect,
244 };
245
246 static int bluetooth_probe(struct connman_device *device)
247 {
248         DBG("device %p", device);
249
250         return 0;
251 }
252
253 static void bluetooth_remove(struct connman_device *device)
254 {
255         DBG("device %p", device);
256 }
257
258 static void powered_reply(DBusPendingCall *call, void *user_data)
259 {
260         DBusMessage *reply;
261
262         DBG("");
263
264         reply = dbus_pending_call_steal_reply(call);
265
266         dbus_message_unref(reply);
267 }
268
269 static int change_powered(DBusConnection *connection, const char *path,
270                                                         dbus_bool_t powered)
271 {
272         DBusMessage *message;
273         DBusMessageIter iter;
274         DBusPendingCall *call;
275
276         DBG("");
277
278         if (path == NULL)
279                 return -EINVAL;
280
281         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
282                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
283         if (message == NULL)
284                 return -ENOMEM;
285
286         dbus_message_set_auto_start(message, FALSE);
287
288         dbus_message_iter_init_append(message, &iter);
289         connman_dbus_property_append_variant(&iter, "Powered",
290                                                 DBUS_TYPE_BOOLEAN, &powered);
291
292         if (dbus_connection_send_with_reply(connection, message,
293                                                 &call, TIMEOUT) == FALSE) {
294                 connman_error("Failed to change Powered property");
295                 dbus_message_unref(message);
296                 return -EINVAL;
297         }
298
299         if (call == NULL) {
300                 connman_error("D-Bus connection not available");
301                 dbus_message_unref(message);
302                 return -EINVAL;
303         }
304
305         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
306
307         dbus_message_unref(message);
308
309         return -EINPROGRESS;
310 }
311
312 static int bluetooth_enable(struct connman_device *device)
313 {
314         const char *path = connman_device_get_string(device, "Path");
315
316         DBG("device %p", device);
317
318         return change_powered(connection, path, TRUE);
319 }
320
321 static int bluetooth_disable(struct connman_device *device)
322 {
323         const char *path = connman_device_get_string(device, "Path");
324
325         DBG("device %p", device);
326
327         return change_powered(connection, path, FALSE);
328 }
329
330 static struct connman_device_driver bluetooth_driver = {
331         .name           = "bluetooth",
332         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
333         .probe          = bluetooth_probe,
334         .remove         = bluetooth_remove,
335         .enable         = bluetooth_enable,
336         .disable        = bluetooth_disable,
337 };
338
339 static void extract_properties(DBusMessage *reply, const char **parent,
340                                                 const char **address,
341                                                 const char **name,
342                                                 const char **alias,
343                                                 dbus_bool_t *powered,
344                                                 dbus_bool_t *scanning,
345                                                 DBusMessageIter *uuids,
346                                                 DBusMessageIter *networks)
347 {
348         DBusMessageIter array, dict;
349
350         if (dbus_message_iter_init(reply, &array) == FALSE)
351                 return;
352
353         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
354                 return;
355
356         dbus_message_iter_recurse(&array, &dict);
357
358         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
359                 DBusMessageIter entry, value;
360                 const char *key;
361
362                 dbus_message_iter_recurse(&dict, &entry);
363                 dbus_message_iter_get_basic(&entry, &key);
364
365                 dbus_message_iter_next(&entry);
366                 dbus_message_iter_recurse(&entry, &value);
367
368                 if (g_str_equal(key, "Adapter") == TRUE) {
369                         if (parent != NULL)
370                                 dbus_message_iter_get_basic(&value, parent);
371                 } else if (g_str_equal(key, "Address") == TRUE) {
372                         if (address != NULL)
373                                 dbus_message_iter_get_basic(&value, address);
374                 } else if (g_str_equal(key, "Name") == TRUE) {
375                         if (name != NULL)
376                                 dbus_message_iter_get_basic(&value, name);
377                 } else if (g_str_equal(key, "Alias") == TRUE) {
378                         if (alias != NULL)
379                                 dbus_message_iter_get_basic(&value, alias);
380                 } else if (g_str_equal(key, "Powered") == TRUE) {
381                         if (powered != NULL)
382                                 dbus_message_iter_get_basic(&value, powered);
383                 } else if (g_str_equal(key, "Discovering") == TRUE) {
384                         if (scanning != NULL)
385                                 dbus_message_iter_get_basic(&value, scanning);
386                 } else if (g_str_equal(key, "Devices") == TRUE) {
387                         if (networks != NULL)
388                                 memcpy(networks, &value, sizeof(value));
389                 } else if (g_str_equal(key, "UUIDs") == TRUE) {
390                         if (uuids != NULL)
391                                 memcpy(uuids, &value, sizeof(value));
392                 }
393
394                 dbus_message_iter_next(&dict);
395         }
396 }
397
398 static dbus_bool_t has_pan(DBusMessageIter *array)
399 {
400         DBusMessageIter value;
401
402         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
403                 return FALSE;
404
405         dbus_message_iter_recurse(array, &value);
406
407         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
408                 const char *uuid;
409
410                 dbus_message_iter_get_basic(&value, &uuid);
411
412                 if (g_strcmp0(uuid, UUID_NAP) == 0)
413                         return TRUE;
414
415                 dbus_message_iter_next(&value);
416         }
417
418         return FALSE;
419 }
420
421 static void network_properties_reply(DBusPendingCall *call, void *user_data)
422 {
423         char *path = user_data;
424         struct connman_device *device;
425         struct connman_network *network;
426         DBusMessage *reply;
427         DBusMessageIter uuids;
428         const char *parent = NULL, *address = NULL, *name = NULL;
429         struct ether_addr addr;
430         char ident[13];
431
432         reply = dbus_pending_call_steal_reply(call);
433         if (reply == NULL)
434                 return;
435
436         extract_properties(reply, &parent, &address, NULL, &name,
437                                                 NULL, NULL, &uuids, NULL);
438
439         if (parent == NULL)
440                 goto done;
441
442         device = g_hash_table_lookup(bluetooth_devices, parent);
443         if (device == NULL)
444                 goto done;
445
446         if (address == NULL)
447                 goto done;
448
449         ether_aton_r(address, &addr);
450
451         snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
452                                                 addr.ether_addr_octet[0],
453                                                 addr.ether_addr_octet[1],
454                                                 addr.ether_addr_octet[2],
455                                                 addr.ether_addr_octet[3],
456                                                 addr.ether_addr_octet[4],
457                                                 addr.ether_addr_octet[5]);
458
459         if (has_pan(&uuids) == FALSE)
460                 goto done;
461
462         network = connman_device_get_network(device, ident);
463         if (network != NULL)
464                 goto done;
465
466         network = connman_network_create(ident,
467                                         CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
468         if (network == NULL)
469                 goto done;
470
471         connman_network_set_string(network, "Path", path);
472
473         connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
474
475         connman_network_set_name(network, name);
476
477         connman_device_add_network(device, network);
478
479         connman_network_set_group(network, ident);
480
481 done:
482         dbus_message_unref(reply);
483 }
484
485 static void add_network(struct connman_device *device, const char *path)
486 {
487         DBusMessage *message;
488         DBusPendingCall *call;
489
490         DBG("path %s", path);
491
492         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
493                                 BLUEZ_DEVICE_INTERFACE, GET_PROPERTIES);
494         if (message == NULL)
495                 return;
496
497         dbus_message_set_auto_start(message, FALSE);
498
499         if (dbus_connection_send_with_reply(connection, message,
500                                                 &call, TIMEOUT) == FALSE) {
501                 connman_error("Failed to get network properties for %s", path);
502                 goto done;
503         }
504
505         if (call == NULL) {
506                 connman_error("D-Bus connection not available");
507                 goto done;
508         }
509
510         dbus_pending_call_set_notify(call, network_properties_reply,
511                                                 g_strdup(path), g_free);
512
513 done:
514         dbus_message_unref(message);
515 }
516
517 static void check_networks(struct connman_device *device,
518                                                 DBusMessageIter *array)
519 {
520         DBusMessageIter value;
521
522         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
523                 return;
524
525         dbus_message_iter_recurse(array, &value);
526
527         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
528                 const char *path;
529
530                 dbus_message_iter_get_basic(&value, &path);
531
532                 add_network(device, path);
533
534                 dbus_message_iter_next(&value);
535         }
536 }
537
538 static void adapter_changed(DBusConnection *connection, DBusMessage *message)
539 {
540         const char *path = dbus_message_get_path(message);
541         struct connman_device *device;
542         DBusMessageIter iter, value;
543         const char *key;
544
545         DBG("path %s", path);
546
547         device = g_hash_table_lookup(bluetooth_devices, path);
548         if (device == NULL)
549                 return;
550
551         if (dbus_message_iter_init(message, &iter) == FALSE)
552                 return;
553
554         dbus_message_iter_get_basic(&iter, &key);
555
556         dbus_message_iter_next(&iter);
557         dbus_message_iter_recurse(&iter, &value);
558
559         if (g_str_equal(key, "Powered") == TRUE) {
560                 dbus_bool_t val;
561
562                 dbus_message_iter_get_basic(&value, &val);
563                 connman_device_set_powered(device, val);
564         } else if (g_str_equal(key, "Discovering") == TRUE) {
565                 dbus_bool_t val;
566
567                 dbus_message_iter_get_basic(&value, &val);
568                 connman_device_set_scanning(device, val);
569         } else if (g_str_equal(key, "Devices") == TRUE) {
570                 check_networks(device, &value);
571         }
572 }
573
574 static void adapter_properties_reply(DBusPendingCall *call, void *user_data)
575 {
576         char *path = user_data;
577         struct connman_device *device;
578         DBusMessage *reply;
579         DBusMessageIter networks;
580         const char *address = NULL, *name = NULL;
581         dbus_bool_t powered = FALSE, scanning = FALSE;
582         struct ether_addr addr;
583         char ident[13];
584
585         DBG("path %s", path);
586
587         reply = dbus_pending_call_steal_reply(call);
588         if (reply == NULL)
589                 return;
590
591         if (path == NULL)
592                 goto done;
593
594         extract_properties(reply, NULL, &address, &name, NULL,
595                                         &powered, &scanning, NULL, &networks);
596
597         if (address == NULL)
598                 goto done;
599
600         device = g_hash_table_lookup(bluetooth_devices, path);
601         if (device != NULL)
602                 goto update;
603
604         ether_aton_r(address, &addr);
605
606         snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
607                                                 addr.ether_addr_octet[0],
608                                                 addr.ether_addr_octet[1],
609                                                 addr.ether_addr_octet[2],
610                                                 addr.ether_addr_octet[3],
611                                                 addr.ether_addr_octet[4],
612                                                 addr.ether_addr_octet[5]);
613
614         device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
615         if (device == NULL)
616                 goto done;
617
618         connman_device_set_ident(device, ident);
619
620         connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE);
621
622         if (connman_device_register(device) < 0) {
623                 connman_device_unref(device);
624                 goto done;
625         }
626
627         g_hash_table_insert(bluetooth_devices, g_strdup(path), device);
628
629 update:
630         connman_device_set_string(device, "Address", address);
631         connman_device_set_string(device, "Name", name);
632
633         connman_device_set_powered(device, powered);
634         connman_device_set_scanning(device, scanning);
635
636         if (powered == TRUE)
637                 check_networks(device, &networks);
638
639 done:
640         dbus_message_unref(reply);
641 }
642
643 static void add_adapter(DBusConnection *connection, const char *path)
644 {
645         DBusMessage *message;
646         DBusPendingCall *call;
647
648         DBG("path %s", path);
649
650         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
651                                 BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
652         if (message == NULL)
653                 return;
654
655         dbus_message_set_auto_start(message, FALSE);
656
657         if (dbus_connection_send_with_reply(connection, message,
658                                                 &call, TIMEOUT) == FALSE) {
659                 connman_error("Failed to get adapter properties for %s", path);
660                 goto done;
661         }
662
663         if (call == NULL) {
664                 connman_error("D-Bus connection not available");
665                 goto done;
666         }
667
668         dbus_pending_call_set_notify(call, adapter_properties_reply,
669                                                 g_strdup(path), g_free);
670
671 done:
672         dbus_message_unref(message);
673 }
674
675 static void remove_adapter(DBusConnection *connection, const char *path)
676 {
677         DBG("path %s", path);
678
679         g_hash_table_remove(bluetooth_devices, path);
680 }
681
682 static void list_adapters_reply(DBusPendingCall *call, void *user_data)
683 {
684         DBusMessage *reply;
685         DBusError error;
686         char **adapters;
687         int i, num_adapters;
688
689         DBG("");
690
691         reply = dbus_pending_call_steal_reply(call);
692         if (reply == NULL)
693                 return;
694
695         dbus_error_init(&error);
696
697         if (dbus_message_get_args(reply, &error,
698                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
699                                                 &adapters, &num_adapters,
700                                                 DBUS_TYPE_INVALID) == FALSE) {
701                 if (dbus_error_is_set(&error) == TRUE) {
702                         connman_error("%s", error.message);
703                         dbus_error_free(&error);
704                 } else
705                         connman_error("Wrong arguments for adapter list");
706                 goto done;
707         }
708
709         for (i = 0; i < num_adapters; i++)
710                 add_adapter(connection, adapters[i]);
711
712         g_strfreev(adapters);
713
714 done:
715         dbus_message_unref(reply);
716 }
717
718 static void unregister_device(gpointer data)
719 {
720         struct connman_device *device = data;
721
722         DBG("");
723
724         connman_device_unregister(device);
725         connman_device_unref(device);
726 }
727
728 static void bluetooth_connect(DBusConnection *connection, void *user_data)
729 {
730         DBusMessage *message;
731         DBusPendingCall *call;
732
733         DBG("connection %p", connection);
734
735         bluetooth_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
736                                                 g_free, unregister_device);
737
738         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
739                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
740         if (message == NULL)
741                 return;
742
743         dbus_message_set_auto_start(message, FALSE);
744
745         if (dbus_connection_send_with_reply(connection, message,
746                                                 &call, TIMEOUT) == FALSE) {
747                 connman_error("Failed to get Bluetooth adapters");
748                 goto done;
749         }
750
751         if (call == NULL) {
752                 connman_error("D-Bus connection not available");
753                 goto done;
754         }
755
756         dbus_pending_call_set_notify(call, list_adapters_reply, NULL, NULL);
757
758 done:
759         dbus_message_unref(message);
760 }
761
762 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
763 {
764         DBG("connection %p", connection);
765
766         g_hash_table_destroy(bluetooth_devices);
767         bluetooth_devices = NULL;
768 }
769
770 static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
771                                         DBusMessage *message, void *user_data)
772 {
773         if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
774                                                 PROPERTY_CHANGED) == TRUE) {
775                 adapter_changed(connection, message);
776         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
777                                                 ADAPTER_ADDED) == TRUE) {
778                 const char *path;
779                 dbus_message_get_args(message, NULL,
780                                         DBUS_TYPE_OBJECT_PATH, &path,
781                                                         DBUS_TYPE_INVALID);
782                 add_adapter(connection, path);
783         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
784                                                 ADAPTER_REMOVED) == TRUE) {
785                 const char *path;
786                 dbus_message_get_args(message, NULL,
787                                         DBUS_TYPE_OBJECT_PATH, &path,
788                                                         DBUS_TYPE_INVALID);
789                 remove_adapter(connection, path);
790         }
791
792         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
793 }
794
795 static guint watch;
796
797 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
798                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
799 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
800                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
801
802 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
803                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
804
805 static int bluetooth_init(void)
806 {
807         int err = -EIO;
808
809         connection = connman_dbus_get_connection();
810         if (connection == NULL)
811                 return -EIO;
812
813         if (dbus_connection_add_filter(connection, bluetooth_signal,
814                                                         NULL, NULL) == FALSE)
815                 goto unref;
816
817         err = connman_network_driver_register(&pan_driver);
818         if (err < 0)
819                 goto remove;
820
821         err = connman_device_driver_register(&bluetooth_driver);
822         if (err < 0) {
823                 connman_network_driver_unregister(&pan_driver);
824                 goto remove;
825         }
826
827         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
828                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
829         if (watch == 0) {
830                 connman_device_driver_unregister(&bluetooth_driver);
831                 connman_network_driver_unregister(&pan_driver);
832                 err = -EIO;
833                 goto remove;
834         }
835
836         dbus_bus_add_match(connection, added_rule, NULL);
837         dbus_bus_add_match(connection, removed_rule, NULL);
838         dbus_bus_add_match(connection, adapter_rule, NULL);
839         dbus_connection_flush(connection);
840
841         return 0;
842
843 remove:
844         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
845
846 unref:
847         dbus_connection_unref(connection);
848
849         return err;
850 }
851
852 static void bluetooth_exit(void)
853 {
854         dbus_bus_remove_match(connection, adapter_rule, NULL);
855         dbus_bus_remove_match(connection, removed_rule, NULL);
856         dbus_bus_remove_match(connection, added_rule, NULL);
857         dbus_connection_flush(connection);
858
859         g_dbus_remove_watch(connection, watch);
860
861         bluetooth_disconnect(connection, NULL);
862
863         connman_device_driver_unregister(&bluetooth_driver);
864         connman_network_driver_unregister(&pan_driver);
865
866         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
867
868         dbus_connection_unref(connection);
869 }
870
871 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
872                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)