session: Add configuration plugin
[platform/upstream/connman.git] / plugins / bluetooth.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2012  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/technology.h>
37 #include <connman/device.h>
38 #include <connman/inet.h>
39 #include <connman/dbus.h>
40 #include <connman/log.h>
41
42 #define BLUEZ_SERVICE                   "org.bluez"
43 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
44 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
45 #define BLUEZ_DEVICE_INTERFACE          BLUEZ_SERVICE ".Device"
46 #define BLUEZ_NETWORK_INTERFACE         BLUEZ_SERVICE ".Network"
47 #define BLUEZ_NETWORK_SERVER            BLUEZ_SERVICE ".NetworkServer"
48
49 #define LIST_ADAPTERS                   "ListAdapters"
50 #define ADAPTER_ADDED                   "AdapterAdded"
51 #define ADAPTER_REMOVED                 "AdapterRemoved"
52 #define DEVICE_REMOVED                  "DeviceRemoved"
53
54 #define PROPERTY_CHANGED                "PropertyChanged"
55 #define GET_PROPERTIES                  "GetProperties"
56 #define SET_PROPERTY                    "SetProperty"
57
58 #define CONNECT                         "Connect"
59 #define DISCONNECT                      "Disconnect"
60
61 #define REGISTER                        "Register"
62 #define UNREGISTER                      "Unregister"
63
64 #define UUID_NAP        "00001116-0000-1000-8000-00805f9b34fb"
65
66 #define TIMEOUT 5000
67
68 static DBusConnection *connection;
69
70 static GHashTable *bluetooth_devices = NULL;
71 static GHashTable *bluetooth_networks = NULL;
72 static GHashTable *pending_networks = NULL;
73
74 static int pan_probe(struct connman_network *network)
75 {
76         DBG("network %p", network);
77
78         return 0;
79 }
80
81 static void pan_remove(struct connman_network *network)
82 {
83         DBG("network %p", network);
84 }
85
86 static void connect_reply(DBusPendingCall *call, void *user_data)
87 {
88         struct connman_network *network = user_data;
89         DBusMessage *reply;
90         DBusError error;
91         const char *interface = NULL;
92         int index;
93
94         DBG("network %p", network);
95
96         reply = dbus_pending_call_steal_reply(call);
97
98         dbus_error_init(&error);
99
100         if (dbus_set_error_from_message(&error, reply) == TRUE) {
101                 connman_error("%s", error.message);
102                 dbus_error_free(&error);
103
104                 goto err;
105         }
106
107         if (dbus_message_get_args(reply, &error,
108                                         DBUS_TYPE_STRING, &interface,
109                                                 DBUS_TYPE_INVALID) == FALSE) {
110                 if (dbus_error_is_set(&error) == TRUE) {
111                         connman_error("%s", error.message);
112                         dbus_error_free(&error);
113                 } else
114                         connman_error("Wrong arguments for connect");
115                 goto err;
116         }
117
118         if (interface == NULL)
119                 goto err;
120
121         DBG("interface %s", interface);
122
123         index = connman_inet_ifindex(interface);
124
125         connman_network_set_index(network, index);
126
127         connman_network_set_connected(network, TRUE);
128
129         dbus_message_unref(reply);
130
131         dbus_pending_call_unref(call);
132
133         return;
134 err:
135
136         connman_network_set_connected(network, FALSE);
137
138         dbus_message_unref(reply);
139
140         dbus_pending_call_unref(call);
141 }
142
143 static int pan_connect(struct connman_network *network)
144 {
145         const char *path = connman_network_get_string(network, "Path");
146         const char *uuid = "nap";
147         DBusMessage *message;
148         DBusPendingCall *call;
149
150         DBG("network %p", network);
151
152         if (path == NULL)
153                 return -EINVAL;
154
155         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
156                                         BLUEZ_NETWORK_INTERFACE, CONNECT);
157         if (message == NULL)
158                 return -ENOMEM;
159
160         dbus_message_set_auto_start(message, FALSE);
161
162         dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
163                                                         DBUS_TYPE_INVALID);
164
165         if (dbus_connection_send_with_reply(connection, message,
166                                         &call, TIMEOUT * 10) == FALSE) {
167                 connman_error("Failed to connect service");
168                 dbus_message_unref(message);
169                 return -EINVAL;
170         }
171
172         if (call == NULL) {
173                 connman_error("D-Bus connection not available");
174                 dbus_message_unref(message);
175                 return -EINVAL;
176         }
177
178         dbus_pending_call_set_notify(call, connect_reply, network, NULL);
179
180         dbus_message_unref(message);
181
182         return -EINPROGRESS;
183 }
184
185 static void disconnect_reply(DBusPendingCall *call, void *user_data)
186 {
187         struct connman_network *network = user_data;
188         DBusMessage *reply;
189         DBusError error;
190
191         DBG("network %p", network);
192
193         reply = dbus_pending_call_steal_reply(call);
194
195         dbus_error_init(&error);
196
197         if (dbus_set_error_from_message(&error, reply) == TRUE) {
198                 connman_error("%s", error.message);
199                 dbus_error_free(&error);
200                 goto done;
201         }
202
203         if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
204                 if (dbus_error_is_set(&error) == TRUE) {
205                         connman_error("%s", error.message);
206                         dbus_error_free(&error);
207                 } else
208                         connman_error("Wrong arguments for disconnect");
209                 goto done;
210         }
211
212         connman_network_set_connected(network, FALSE);
213
214 done:
215         dbus_message_unref(reply);
216
217         dbus_pending_call_unref(call);
218
219         connman_network_unref(network);
220 }
221
222 static int pan_disconnect(struct connman_network *network)
223 {
224         const char *path = connman_network_get_string(network, "Path");
225         DBusMessage *message;
226         DBusPendingCall *call;
227
228         DBG("network %p", network);
229
230         if (path == NULL)
231                 return -EINVAL;
232
233         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
234                                         BLUEZ_NETWORK_INTERFACE, DISCONNECT);
235         if (message == NULL)
236                 return -ENOMEM;
237
238         dbus_message_set_auto_start(message, FALSE);
239
240         dbus_message_append_args(message, DBUS_TYPE_INVALID);
241
242         if (dbus_connection_send_with_reply(connection, message,
243                                                 &call, TIMEOUT) == FALSE) {
244                 connman_error("Failed to disconnect service");
245                 dbus_message_unref(message);
246                 return -EINVAL;
247         }
248
249         if (call == NULL) {
250                 connman_error("D-Bus connection not available");
251                 dbus_message_unref(message);
252                 return -EINVAL;
253         }
254
255         connman_network_ref(network);
256
257         connman_network_set_associating(network, FALSE);
258
259         dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
260
261         dbus_message_unref(message);
262
263         return 0;
264 }
265
266 static struct connman_network_driver pan_driver = {
267         .name           = "bluetooth-pan",
268         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
269         .probe          = pan_probe,
270         .remove         = pan_remove,
271         .connect        = pan_connect,
272         .disconnect     = pan_disconnect,
273 };
274
275 static gboolean network_changed(DBusConnection *conn,
276                                 DBusMessage *message, void *user_data)
277 {
278         const char *path = dbus_message_get_path(message);
279         struct connman_network *network;
280         DBusMessageIter iter, value;
281         const char *key;
282
283         DBG("path %s", path);
284
285         network = g_hash_table_lookup(bluetooth_networks, path);
286         if (network == NULL)
287                 return TRUE;
288
289         if (dbus_message_iter_init(message, &iter) == FALSE)
290                 return TRUE;
291
292         dbus_message_iter_get_basic(&iter, &key);
293
294         dbus_message_iter_next(&iter);
295         dbus_message_iter_recurse(&iter, &value);
296
297         if (g_str_equal(key, "Connected") == TRUE) {
298                 dbus_bool_t connected;
299
300                 dbus_message_iter_get_basic(&value, &connected);
301
302                 if (connected == TRUE)
303                         return TRUE;
304
305                 connman_network_set_associating(network, FALSE);
306                 connman_network_set_connected(network, FALSE);
307         }
308
309         return TRUE;
310 }
311
312 static void extract_properties(DBusMessage *reply, const char **parent,
313                                                 const char **address,
314                                                 const char **name,
315                                                 const char **alias,
316                                                 dbus_bool_t *powered,
317                                                 dbus_bool_t *scanning,
318                                                 DBusMessageIter *uuids,
319                                                 DBusMessageIter *networks)
320 {
321         DBusMessageIter array, dict;
322
323         if (dbus_message_iter_init(reply, &array) == FALSE)
324                 return;
325
326         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
327                 return;
328
329         dbus_message_iter_recurse(&array, &dict);
330
331         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
332                 DBusMessageIter entry, value;
333                 const char *key;
334
335                 dbus_message_iter_recurse(&dict, &entry);
336                 dbus_message_iter_get_basic(&entry, &key);
337
338                 dbus_message_iter_next(&entry);
339                 dbus_message_iter_recurse(&entry, &value);
340
341                 if (g_str_equal(key, "Adapter") == TRUE) {
342                         if (parent != NULL)
343                                 dbus_message_iter_get_basic(&value, parent);
344                 } else if (g_str_equal(key, "Address") == TRUE) {
345                         if (address != NULL)
346                                 dbus_message_iter_get_basic(&value, address);
347                 } else if (g_str_equal(key, "Name") == TRUE) {
348                         if (name != NULL)
349                                 dbus_message_iter_get_basic(&value, name);
350                 } else if (g_str_equal(key, "Alias") == TRUE) {
351                         if (alias != NULL)
352                                 dbus_message_iter_get_basic(&value, alias);
353                 } else if (g_str_equal(key, "Powered") == TRUE) {
354                         if (powered != NULL)
355                                 dbus_message_iter_get_basic(&value, powered);
356                 } else if (g_str_equal(key, "Discovering") == TRUE) {
357                         if (scanning != NULL)
358                                 dbus_message_iter_get_basic(&value, scanning);
359                 } else if (g_str_equal(key, "Devices") == TRUE) {
360                         if (networks != NULL)
361                                 memcpy(networks, &value, sizeof(value));
362                 } else if (g_str_equal(key, "UUIDs") == TRUE) {
363                         if (uuids != NULL)
364                                 memcpy(uuids, &value, sizeof(value));
365                 }
366
367                 dbus_message_iter_next(&dict);
368         }
369 }
370
371 static dbus_bool_t has_pan(DBusMessageIter *array)
372 {
373         DBusMessageIter value;
374
375         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
376                 return FALSE;
377
378         dbus_message_iter_recurse(array, &value);
379
380         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
381                 const char *uuid;
382
383                 dbus_message_iter_get_basic(&value, &uuid);
384
385                 if (g_strcmp0(uuid, UUID_NAP) == 0)
386                         return TRUE;
387
388                 dbus_message_iter_next(&value);
389         }
390
391         return FALSE;
392 }
393
394 static void network_properties_reply(DBusPendingCall *call, void *user_data)
395 {
396         char *path = user_data;
397         struct connman_device *device;
398         struct connman_network *network;
399         DBusMessage *reply;
400         DBusMessageIter uuids;
401         const char *parent = NULL, *address = NULL, *name = NULL;
402         struct ether_addr addr;
403         char ident[13];
404
405         reply = dbus_pending_call_steal_reply(call);
406
407         extract_properties(reply, &parent, &address, NULL, &name,
408                                                 NULL, NULL, &uuids, NULL);
409
410         if (parent == NULL)
411                 goto done;
412
413         device = g_hash_table_lookup(bluetooth_devices, parent);
414         if (device == NULL)
415                 goto done;
416
417         if (address == NULL)
418                 goto done;
419
420         ether_aton_r(address, &addr);
421
422         snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
423                                                 addr.ether_addr_octet[0],
424                                                 addr.ether_addr_octet[1],
425                                                 addr.ether_addr_octet[2],
426                                                 addr.ether_addr_octet[3],
427                                                 addr.ether_addr_octet[4],
428                                                 addr.ether_addr_octet[5]);
429
430         if (has_pan(&uuids) == FALSE)
431                 goto done;
432
433         network = connman_device_get_network(device, ident);
434         if (network != NULL)
435                 goto done;
436
437         network = connman_network_create(ident,
438                                         CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
439         if (network == NULL)
440                 goto done;
441
442         connman_network_set_string(network, "Path", path);
443
444         connman_network_set_name(network, name);
445
446         connman_device_add_network(device, network);
447
448         connman_network_set_group(network, ident);
449
450         g_hash_table_replace(bluetooth_networks, g_strdup(path), network);
451
452 done:
453         dbus_message_unref(reply);
454
455         dbus_pending_call_unref(call);
456 }
457
458 static void add_network(const char *path)
459 {
460         DBusMessage *message;
461         DBusPendingCall *call;
462
463         DBG("path %s", path);
464
465         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
466                                 BLUEZ_DEVICE_INTERFACE, GET_PROPERTIES);
467         if (message == NULL)
468                 return;
469
470         dbus_message_set_auto_start(message, FALSE);
471
472         if (dbus_connection_send_with_reply(connection, message,
473                                                 &call, TIMEOUT) == FALSE) {
474                 connman_error("Failed to get network properties for %s", path);
475                 goto done;
476         }
477
478         if (call == NULL) {
479                 connman_error("D-Bus connection not available");
480                 goto done;
481         }
482
483         dbus_pending_call_set_notify(call, network_properties_reply,
484                                                 g_strdup(path), g_free);
485
486 done:
487         dbus_message_unref(message);
488 }
489
490 static void check_networks(DBusMessageIter *array)
491 {
492         DBusMessageIter value;
493
494         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
495                 return;
496
497         dbus_message_iter_recurse(array, &value);
498
499         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
500                 const char *path;
501
502                 dbus_message_iter_get_basic(&value, &path);
503
504                 add_network(path);
505
506                 dbus_message_iter_next(&value);
507         }
508 }
509
510 static void check_pending_networks(const char *adapter)
511 {
512         GSList *networks, *list;
513
514         networks = g_hash_table_lookup(pending_networks, adapter);
515         if (networks == NULL)
516                 return;
517
518         for (list = networks; list != NULL; list = list->next) {
519                 char *path = list->data;
520
521                 add_network(path);
522         }
523
524         g_hash_table_remove(pending_networks, adapter);
525 }
526
527 static gboolean adapter_changed(DBusConnection *conn,
528                                 DBusMessage *message, void *user_data)
529 {
530         const char *path = dbus_message_get_path(message);
531         struct connman_device *device;
532         DBusMessageIter iter, value;
533         const char *key;
534
535         DBG("path %s", path);
536
537         device = g_hash_table_lookup(bluetooth_devices, path);
538         if (device == NULL)
539                 return TRUE;
540
541         if (dbus_message_iter_init(message, &iter) == FALSE)
542                 return TRUE;
543
544         dbus_message_iter_get_basic(&iter, &key);
545
546         dbus_message_iter_next(&iter);
547         dbus_message_iter_recurse(&iter, &value);
548
549         if (g_str_equal(key, "Powered") == TRUE) {
550                 dbus_bool_t val;
551
552                 dbus_message_iter_get_basic(&value, &val);
553                 connman_device_set_powered(device, val);
554                 if (val == TRUE)
555                         check_pending_networks(path);
556         } else if (g_str_equal(key, "Discovering") == TRUE) {
557                 dbus_bool_t val;
558
559                 dbus_message_iter_get_basic(&value, &val);
560                 connman_device_set_scanning(device, val);
561         } else if (g_str_equal(key, "Devices") == TRUE) {
562                 check_networks(&value);
563         }
564
565         return TRUE;
566 }
567
568 static gboolean device_removed(DBusConnection *conn,
569                                 DBusMessage *message, void *user_data)
570 {
571         const char *network_path;
572         struct connman_network *network;
573         struct connman_device *device;
574         DBusMessageIter iter;
575
576         DBG("");
577
578         if (dbus_message_iter_init(message, &iter) == FALSE)
579                 return TRUE;
580
581         dbus_message_iter_get_basic(&iter, &network_path);
582
583         network = g_hash_table_lookup(bluetooth_networks, network_path);
584         if (network == NULL)
585                 return TRUE;
586
587         device = connman_network_get_device(network);
588         if (device == NULL)
589                 return TRUE;
590
591         g_hash_table_remove(bluetooth_networks, network_path);
592
593         return TRUE;
594 }
595
596 static gboolean device_changed(DBusConnection *conn,
597                                 DBusMessage *message, void *user_data)
598 {
599         const char *path = dbus_message_get_path(message);
600         DBusMessageIter iter, value;
601         const char *key;
602
603         DBG("path %s", path);
604
605         if (dbus_message_iter_init(message, &iter) == FALSE)
606                 return TRUE;
607
608         dbus_message_iter_get_basic(&iter, &key);
609
610         dbus_message_iter_next(&iter);
611         dbus_message_iter_recurse(&iter, &value);
612
613         DBG("key %s", key);
614
615         if (g_str_equal(key, "UUIDs") == TRUE)
616                 add_network(path);
617
618         return TRUE;
619 }
620
621 static void remove_device_networks(struct connman_device *device)
622 {
623         GHashTableIter iter;
624         gpointer key, value;
625         GSList *key_list = NULL;
626         GSList *list;
627
628         if (bluetooth_networks == NULL)
629                 return;
630
631         g_hash_table_iter_init(&iter, bluetooth_networks);
632
633         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
634                 struct connman_network *network = value;
635
636                 if (connman_network_get_device(network) != device)
637                         continue;
638
639                 key_list = g_slist_append(key_list, key);
640         }
641
642         for (list = key_list; list != NULL; list = list->next) {
643                 const char *network_path = list->data;
644
645                 g_hash_table_remove(bluetooth_networks, network_path);
646         }
647
648         g_slist_free(key_list);
649 }
650
651 static void add_pending_networks(const char *adapter, DBusMessageIter *array)
652 {
653         DBusMessageIter value;
654         GSList *list = NULL;
655
656         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
657                 return;
658
659         dbus_message_iter_recurse(array, &value);
660
661         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
662                 const char *path;
663
664                 dbus_message_iter_get_basic(&value, &path);
665
666                 list = g_slist_prepend(list, g_strdup(path));
667
668                 dbus_message_iter_next(&value);
669         }
670
671         if (list == NULL)
672                 return;
673
674         g_hash_table_replace(pending_networks, g_strdup(adapter), list);
675 }
676
677 static void adapter_properties_reply(DBusPendingCall *call, void *user_data)
678 {
679         char *path = user_data;
680         struct connman_device *device;
681         DBusMessage *reply;
682         DBusMessageIter networks;
683         const char *address = NULL, *name = NULL;
684         dbus_bool_t powered = FALSE, scanning = FALSE;
685         struct ether_addr addr;
686         char ident[13];
687
688         DBG("path %s", path);
689
690         reply = dbus_pending_call_steal_reply(call);
691
692         if (path == NULL)
693                 goto done;
694
695         extract_properties(reply, NULL, &address, &name, NULL,
696                                         &powered, &scanning, NULL, &networks);
697
698         if (address == NULL)
699                 goto done;
700
701         if (g_strcmp0(address, "00:00:00:00:00:00") == 0)
702                 goto done;
703
704         device = g_hash_table_lookup(bluetooth_devices, path);
705         if (device != NULL)
706                 goto update;
707
708         ether_aton_r(address, &addr);
709
710         snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
711                                                 addr.ether_addr_octet[0],
712                                                 addr.ether_addr_octet[1],
713                                                 addr.ether_addr_octet[2],
714                                                 addr.ether_addr_octet[3],
715                                                 addr.ether_addr_octet[4],
716                                                 addr.ether_addr_octet[5]);
717
718         device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
719         if (device == NULL)
720                 goto done;
721
722         g_hash_table_insert(bluetooth_devices, g_strdup(path), device);
723
724         connman_device_set_ident(device, ident);
725
726         connman_device_set_string(device, "Path", path);
727
728         if (connman_device_register(device) < 0) {
729                 connman_device_unref(device);
730                 g_hash_table_remove(bluetooth_devices, path);
731                 goto done;
732         }
733
734 update:
735         connman_device_set_string(device, "Address", address);
736         connman_device_set_string(device, "Name", name);
737         connman_device_set_string(device, "Path", path);
738
739         connman_device_set_powered(device, powered);
740         connman_device_set_scanning(device, scanning);
741
742         if (powered == FALSE) {
743                 remove_device_networks(device);
744                 add_pending_networks(path, &networks);
745         } else
746                 check_networks(&networks);
747
748 done:
749         dbus_message_unref(reply);
750
751         dbus_pending_call_unref(call);
752 }
753
754 static void add_adapter(DBusConnection *conn, const char *path)
755 {
756         DBusMessage *message;
757         DBusPendingCall *call;
758
759         DBG("path %s", path);
760
761         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
762                                 BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
763         if (message == NULL)
764                 return;
765
766         dbus_message_set_auto_start(message, FALSE);
767
768         if (dbus_connection_send_with_reply(conn, message,
769                                                 &call, TIMEOUT) == FALSE) {
770                 connman_error("Failed to get adapter properties for %s", path);
771                 goto done;
772         }
773
774         if (call == NULL) {
775                 connman_error("D-Bus connection not available");
776                 goto done;
777         }
778
779         dbus_pending_call_set_notify(call, adapter_properties_reply,
780                                                 g_strdup(path), g_free);
781
782 done:
783         dbus_message_unref(message);
784 }
785
786 static gboolean adapter_added(DBusConnection *conn, DBusMessage *message,
787                                 void *user_data)
788 {
789         const char *path;
790
791         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
792                                 DBUS_TYPE_INVALID);
793         add_adapter(conn, path);
794         return TRUE;
795 }
796
797 static void remove_adapter(DBusConnection *conn, const char *path)
798 {
799         DBG("path %s", path);
800
801         g_hash_table_remove(bluetooth_devices, path);
802         g_hash_table_remove(pending_networks, path);
803 }
804
805 static gboolean adapter_removed(DBusConnection *conn, DBusMessage *message,
806                                 void *user_data)
807 {
808         const char *path;
809
810         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
811                                 DBUS_TYPE_INVALID);
812         remove_adapter(conn, path);
813         return TRUE;
814 }
815
816 static void list_adapters_reply(DBusPendingCall *call, void *user_data)
817 {
818         DBusMessage *reply;
819         DBusError error;
820         char **adapters;
821         int i, num_adapters;
822
823         DBG("");
824
825         reply = dbus_pending_call_steal_reply(call);
826
827         dbus_error_init(&error);
828
829         if (dbus_set_error_from_message(&error, reply) == TRUE) {
830                 connman_error("%s", error.message);
831                 dbus_error_free(&error);
832                 goto done;
833         }
834
835         if (dbus_message_get_args(reply, &error,
836                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
837                                                 &adapters, &num_adapters,
838                                                 DBUS_TYPE_INVALID) == FALSE) {
839                 if (dbus_error_is_set(&error) == TRUE) {
840                         connman_error("%s", error.message);
841                         dbus_error_free(&error);
842                 } else
843                         connman_error("Wrong arguments for adapter list");
844                 goto done;
845         }
846
847         for (i = 0; i < num_adapters; i++)
848                 add_adapter(connection, adapters[i]);
849
850         g_strfreev(adapters);
851
852 done:
853         dbus_message_unref(reply);
854
855         dbus_pending_call_unref(call);
856 }
857
858 static void unregister_device(gpointer data)
859 {
860         struct connman_device *device = data;
861
862         DBG("");
863
864         remove_device_networks(device);
865
866         connman_device_unregister(device);
867         connman_device_unref(device);
868 }
869
870 static void remove_network(gpointer data)
871 {
872         struct connman_network *network = data;
873         struct connman_device *device;
874
875         DBG("network %p", network);
876
877         device = connman_network_get_device(network);
878         if (device != NULL)
879                 connman_device_remove_network(device, network);
880
881         connman_network_unref(network);
882 }
883
884 static void remove_pending_networks(gpointer data)
885 {
886         GSList *list = data;
887
888         g_slist_free_full(list, g_free);
889 }
890
891 static void bluetooth_connect(DBusConnection *conn, void *user_data)
892 {
893         DBusMessage *message;
894         DBusPendingCall *call;
895
896         DBG("connection %p", conn);
897
898         bluetooth_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
899                                                 g_free, unregister_device);
900
901         bluetooth_networks = g_hash_table_new_full(g_str_hash, g_str_equal,
902                                                 g_free, remove_network);
903
904         pending_networks = g_hash_table_new_full(g_str_hash, g_str_equal,
905                                         g_free, remove_pending_networks);
906
907         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
908                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
909         if (message == NULL)
910                 return;
911
912         dbus_message_set_auto_start(message, FALSE);
913
914         if (dbus_connection_send_with_reply(conn, message,
915                                                 &call, TIMEOUT) == FALSE) {
916                 connman_error("Failed to get Bluetooth adapters");
917                 goto done;
918         }
919
920         if (call == NULL) {
921                 connman_error("D-Bus connection not available");
922                 goto done;
923         }
924
925         dbus_pending_call_set_notify(call, list_adapters_reply, NULL, NULL);
926
927 done:
928         dbus_message_unref(message);
929 }
930
931 static void bluetooth_disconnect(DBusConnection *conn, void *user_data)
932 {
933         DBG("connection %p", conn);
934
935         if (bluetooth_devices == NULL)
936                 return;
937
938         g_hash_table_destroy(bluetooth_networks);
939         bluetooth_networks = NULL;
940         g_hash_table_destroy(bluetooth_devices);
941         bluetooth_devices = NULL;
942         g_hash_table_destroy(pending_networks);
943         pending_networks = NULL;
944 }
945
946 static int bluetooth_probe(struct connman_device *device)
947 {
948         GHashTableIter iter;
949         gpointer key, value;
950
951         DBG("device %p", device);
952
953         if (bluetooth_devices == NULL)
954                 return -ENOTSUP;
955
956         g_hash_table_iter_init(&iter, bluetooth_devices);
957
958         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
959                 struct connman_device *device_pan = value;
960
961                 if (device == device_pan)
962                         return 0;
963         }
964
965         return -ENOTSUP;
966 }
967
968 static void bluetooth_remove(struct connman_device *device)
969 {
970         DBG("device %p", device);
971 }
972
973 static void powered_reply(DBusPendingCall *call, void *user_data)
974 {
975         DBusError error;
976         DBusMessage *reply;
977
978         DBG("");
979
980         reply = dbus_pending_call_steal_reply(call);
981
982         dbus_error_init(&error);
983
984         if (dbus_set_error_from_message(&error, reply) == TRUE) {
985                 connman_error("%s", error.message);
986                 dbus_error_free(&error);
987                 dbus_message_unref(reply);
988                 dbus_pending_call_unref(call);
989                 return;
990         }
991
992         dbus_message_unref(reply);
993         dbus_pending_call_unref(call);
994
995         add_adapter(connection, user_data);
996 }
997
998 static int change_powered(DBusConnection *conn, const char *path,
999                                                         dbus_bool_t powered)
1000 {
1001         DBusMessage *message;
1002         DBusMessageIter iter;
1003         DBusPendingCall *call;
1004
1005         DBG("");
1006
1007         if (path == NULL)
1008                 return -EINVAL;
1009
1010         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
1011                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
1012         if (message == NULL)
1013                 return -ENOMEM;
1014
1015         dbus_message_set_auto_start(message, FALSE);
1016
1017         dbus_message_iter_init_append(message, &iter);
1018         connman_dbus_property_append_basic(&iter, "Powered",
1019                                                 DBUS_TYPE_BOOLEAN, &powered);
1020
1021         if (dbus_connection_send_with_reply(conn, message,
1022                                                 &call, TIMEOUT) == FALSE) {
1023                 connman_error("Failed to change Powered property");
1024                 dbus_message_unref(message);
1025                 return -EINVAL;
1026         }
1027
1028         if (call == NULL) {
1029                 connman_error("D-Bus connection not available");
1030                 dbus_message_unref(message);
1031                 return -EINVAL;
1032         }
1033
1034         dbus_pending_call_set_notify(call, powered_reply,
1035                                         g_strdup(path), g_free);
1036
1037         dbus_message_unref(message);
1038
1039         return -EINPROGRESS;
1040 }
1041
1042 static int bluetooth_enable(struct connman_device *device)
1043 {
1044         const char *path = connman_device_get_string(device, "Path");
1045
1046         DBG("device %p", device);
1047
1048         return change_powered(connection, path, TRUE);
1049 }
1050
1051 static int bluetooth_disable(struct connman_device *device)
1052 {
1053         const char *path = connman_device_get_string(device, "Path");
1054
1055         DBG("device %p", device);
1056
1057         return change_powered(connection, path, FALSE);
1058 }
1059
1060 static struct connman_device_driver bluetooth_driver = {
1061         .name           = "bluetooth",
1062         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
1063         .probe          = bluetooth_probe,
1064         .remove         = bluetooth_remove,
1065         .enable         = bluetooth_enable,
1066         .disable        = bluetooth_disable,
1067 };
1068
1069 static int tech_probe(struct connman_technology *technology)
1070 {
1071         return 0;
1072 }
1073
1074 static void tech_remove(struct connman_technology *technology)
1075 {
1076 }
1077
1078 static void server_register_reply(DBusPendingCall *call, void *user_data)
1079 {
1080         struct connman_technology *technology = user_data;
1081         DBusError error;
1082         DBusMessage *reply;
1083
1084         DBG("");
1085
1086         reply = dbus_pending_call_steal_reply(call);
1087
1088         dbus_error_init(&error);
1089
1090         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1091                 connman_error("%s", error.message);
1092                 dbus_error_free(&error);
1093                 dbus_message_unref(reply);
1094                 dbus_pending_call_unref(call);
1095                 return;
1096         }
1097
1098         dbus_message_unref(reply);
1099         dbus_pending_call_unref(call);
1100
1101         connman_technology_tethering_notify(technology, TRUE);
1102 }
1103
1104 static void server_unregister_reply(DBusPendingCall *call, void *user_data)
1105 {
1106         struct connman_technology *technology = user_data;
1107         DBusError error;
1108         DBusMessage *reply;
1109
1110         DBG("");
1111
1112         reply = dbus_pending_call_steal_reply(call);
1113
1114         dbus_error_init(&error);
1115
1116         if (dbus_set_error_from_message(&error, reply) == TRUE) {
1117                 connman_error("%s", error.message);
1118                 dbus_error_free(&error);
1119                 dbus_message_unref(reply);
1120                 dbus_pending_call_unref(call);
1121                 return;
1122         }
1123
1124         dbus_message_unref(reply);
1125         dbus_pending_call_unref(call);
1126
1127         connman_technology_tethering_notify(technology, FALSE);
1128 }
1129
1130
1131 static void server_register(const char *path, const char *uuid,
1132                                 struct connman_technology *technology,
1133                                 const char *bridge, connman_bool_t enabled)
1134 {
1135         DBusMessage *message;
1136         DBusPendingCall *call;
1137         char *command;
1138
1139         DBG("path %s enabled %d", path, enabled);
1140
1141         command = enabled ? REGISTER : UNREGISTER;
1142
1143         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
1144                                         BLUEZ_NETWORK_SERVER, command);
1145         if (message == NULL)
1146                 return;
1147
1148         dbus_message_set_auto_start(message, FALSE);
1149
1150         dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
1151                                                         DBUS_TYPE_INVALID);
1152
1153         if (enabled == TRUE)
1154                 dbus_message_append_args(message, DBUS_TYPE_STRING, &bridge,
1155                                                         DBUS_TYPE_INVALID);
1156
1157         if (dbus_connection_send_with_reply(connection, message,
1158                                                 &call, TIMEOUT) == FALSE) {
1159                 connman_error("Failed to enable PAN server");
1160                 dbus_message_unref(message);
1161                 return;
1162         }
1163
1164         if (call == NULL) {
1165                 connman_error("D-Bus connection not available");
1166                 dbus_message_unref(message);
1167                 return;
1168         }
1169
1170         if (enabled == TRUE)
1171                 dbus_pending_call_set_notify(call, server_register_reply,
1172                                                 technology, NULL);
1173         else
1174                 dbus_pending_call_set_notify(call, server_unregister_reply,
1175                                                 technology, NULL);
1176
1177         dbus_message_unref(message);
1178 }
1179
1180 struct tethering_info {
1181         struct connman_technology *technology;
1182         const char *bridge;
1183 };
1184
1185 static void enable_nap(gpointer key, gpointer value, gpointer user_data)
1186 {
1187         struct tethering_info *info = user_data;
1188         struct connman_device *device = value;
1189         const char *path;
1190
1191         DBG("");
1192
1193         path = connman_device_get_string(device, "Path");
1194
1195         server_register(path, "nap", info->technology, info->bridge, TRUE);
1196 }
1197
1198 static void disable_nap(gpointer key, gpointer value, gpointer user_data)
1199 {
1200         struct tethering_info *info = user_data;
1201         struct connman_device *device = value;
1202         const char *path;
1203
1204         DBG("");
1205
1206         path = connman_device_get_string(device, "Path");
1207
1208         server_register(path, "nap", info->technology, info->bridge, FALSE);
1209 }
1210
1211 static int tech_set_tethering(struct connman_technology *technology,
1212                                 const char *identifier, const char *passphrase,
1213                                 const char *bridge, connman_bool_t enabled)
1214 {
1215         struct tethering_info info = {
1216                 .technology     = technology,
1217                 .bridge         = bridge,
1218         };
1219
1220         DBG("bridge %s", bridge);
1221
1222         if (enabled)
1223                 g_hash_table_foreach(bluetooth_devices, enable_nap, &info);
1224         else
1225                 g_hash_table_foreach(bluetooth_devices, disable_nap, &info);
1226
1227         return 0;
1228 }
1229
1230 static struct connman_technology_driver tech_driver = {
1231         .name           = "bluetooth",
1232         .type           = CONNMAN_SERVICE_TYPE_BLUETOOTH,
1233         .probe          = tech_probe,
1234         .remove         = tech_remove,
1235         .set_tethering  = tech_set_tethering,
1236 };
1237
1238 static guint watch;
1239 static guint added_watch;
1240 static guint removed_watch;
1241 static guint adapter_watch;
1242 static guint device_watch;
1243 static guint device_removed_watch;
1244 static guint network_watch;
1245
1246 static int bluetooth_init(void)
1247 {
1248         int err;
1249
1250         connection = connman_dbus_get_connection();
1251         if (connection == NULL)
1252                 return -EIO;
1253
1254         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
1255                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
1256
1257         added_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
1258                                                 BLUEZ_MANAGER_INTERFACE,
1259                                                 ADAPTER_ADDED, adapter_added,
1260                                                 NULL, NULL);
1261
1262         removed_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
1263                                                 BLUEZ_MANAGER_INTERFACE,
1264                                                 ADAPTER_REMOVED, adapter_removed,
1265                                                 NULL, NULL);
1266
1267         adapter_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE,
1268                                                 NULL, BLUEZ_ADAPTER_INTERFACE,
1269                                                 PROPERTY_CHANGED, adapter_changed,
1270                                                 NULL, NULL);
1271
1272         device_removed_watch = g_dbus_add_signal_watch(connection,
1273                                                 BLUEZ_SERVICE, NULL,
1274                                                 BLUEZ_ADAPTER_INTERFACE,
1275                                                 DEVICE_REMOVED, device_removed,
1276                                                 NULL, NULL);
1277
1278         device_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE, NULL,
1279                                                 BLUEZ_DEVICE_INTERFACE,
1280                                                 PROPERTY_CHANGED, device_changed,
1281                                                 NULL, NULL);
1282
1283         network_watch = g_dbus_add_signal_watch(connection, BLUEZ_SERVICE,
1284                                                 NULL, BLUEZ_NETWORK_INTERFACE,
1285                                                 PROPERTY_CHANGED, network_changed,
1286                                                 NULL, NULL);
1287
1288         if (watch == 0 || added_watch == 0 || removed_watch == 0
1289                         || adapter_watch == 0 || network_watch == 0
1290                                 || device_watch == 0
1291                                         || device_removed_watch == 0) {
1292                 err = -EIO;
1293                 goto remove;
1294         }
1295
1296         err = connman_network_driver_register(&pan_driver);
1297         if (err < 0)
1298                 goto remove;
1299
1300         err = connman_device_driver_register(&bluetooth_driver);
1301         if (err < 0) {
1302                 connman_network_driver_unregister(&pan_driver);
1303                 goto remove;
1304         }
1305
1306         err = connman_technology_driver_register(&tech_driver);
1307         if (err < 0) {
1308                 connman_device_driver_unregister(&bluetooth_driver);
1309                 connman_network_driver_unregister(&pan_driver);
1310                 goto remove;
1311         }
1312
1313         return 0;
1314
1315 remove:
1316         g_dbus_remove_watch(connection, watch);
1317         g_dbus_remove_watch(connection, added_watch);
1318         g_dbus_remove_watch(connection, removed_watch);
1319         g_dbus_remove_watch(connection, adapter_watch);
1320         g_dbus_remove_watch(connection, device_removed_watch);
1321         g_dbus_remove_watch(connection, device_watch);
1322         g_dbus_remove_watch(connection, network_watch);
1323
1324         dbus_connection_unref(connection);
1325
1326         return err;
1327 }
1328
1329 static void bluetooth_exit(void)
1330 {
1331         g_dbus_remove_watch(connection, watch);
1332         g_dbus_remove_watch(connection, added_watch);
1333         g_dbus_remove_watch(connection, removed_watch);
1334         g_dbus_remove_watch(connection, adapter_watch);
1335         g_dbus_remove_watch(connection, device_removed_watch);
1336         g_dbus_remove_watch(connection, device_watch);
1337         g_dbus_remove_watch(connection, network_watch);
1338
1339         bluetooth_disconnect(connection, NULL);
1340
1341         connman_technology_driver_unregister(&tech_driver);
1342
1343         connman_device_driver_unregister(&bluetooth_driver);
1344         connman_network_driver_unregister(&pan_driver);
1345
1346         dbus_connection_unref(connection);
1347 }
1348
1349 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
1350                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)