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