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