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