3a9a695f96561ac05b4647f48dc53b1cec883c73
[platform/upstream/connman.git] / plugins / bluetooth.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 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 <errno.h>
27 #include <string.h>
28
29 #define CONNMAN_API_SUBJECT_TO_CHANGE
30 #include <connman/plugin.h>
31 #include <connman/dbus.h>
32 #include <connman/technology.h>
33 #include <connman/device.h>
34 #include <connman/inet.h>
35 #include <gdbus.h>
36
37 #define BLUEZ_SERVICE                   "org.bluez"
38 #define BLUEZ_PATH                      "/org/bluez"
39 #define BLUETOOTH_PAN_NAP               "00001116-0000-1000-8000-00805f9b34fb"
40
41 #define BLUETOOTH_ADDR_LEN              6
42
43 static DBusConnection *connection;
44 static GDBusClient *client;
45 static GHashTable *devices;
46 static GHashTable *networks;
47
48 struct bluetooth_pan {
49         struct connman_network *network;
50         GDBusProxy *btdevice_proxy;
51         GDBusProxy *btnetwork_proxy;
52 };
53
54 static void address2ident(const char *address, char *ident)
55 {
56         int i;
57
58         for (i = 0; i < BLUETOOTH_ADDR_LEN; i++) {
59                 ident[i * 2] = address[i * 3];
60                 ident[i * 2 + 1] = address[i * 3 + 1];
61         }
62         ident[BLUETOOTH_ADDR_LEN * 2] = '\0';
63 }
64
65 static const char *proxy_get_string(GDBusProxy *proxy, const char *property)
66 {
67         DBusMessageIter iter;
68         const char *str;
69
70         if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE)
71                 return NULL;
72         dbus_message_iter_get_basic(&iter, &str);
73         return str;
74 }
75
76 static connman_bool_t proxy_get_bool(GDBusProxy *proxy, const char *property)
77 {
78         DBusMessageIter iter;
79         connman_bool_t value;
80
81         if (g_dbus_proxy_get_property(proxy, property, &iter) == FALSE)
82                 return FALSE;
83         dbus_message_iter_get_basic(&iter, &value);
84         return value;
85 }
86
87 static connman_bool_t proxy_get_nap(GDBusProxy *proxy)
88 {
89         DBusMessageIter iter, value;
90
91         if (proxy == NULL)
92                 return FALSE;
93
94         if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE)
95                 return FALSE;
96
97         dbus_message_iter_recurse(&iter, &value);
98         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
99                 const char *uuid;
100
101                 dbus_message_iter_get_basic(&value, &uuid);
102                 if (strcmp(uuid, BLUETOOTH_PAN_NAP) == 0)
103                         return TRUE;
104
105                 dbus_message_iter_next(&value);
106         }
107         return FALSE;
108 }
109
110 static int bluetooth_pan_probe(struct connman_network *network)
111 {
112         GHashTableIter iter;
113         gpointer key, value;
114
115         DBG("network %p", network);
116
117         g_hash_table_iter_init(&iter, networks);
118
119         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
120                 struct bluetooth_pan *pan = value;
121
122                 if (network == pan->network)
123                         return 0;
124         }
125
126         return -EOPNOTSUPP;
127 }
128
129 static void pan_remove_nap(struct bluetooth_pan *pan)
130 {
131         struct connman_device *device;
132         struct connman_network *network = pan->network;
133
134         DBG("network %p pan %p", pan->network, pan);
135
136         if (network == NULL)
137                 return;
138
139         pan->network = NULL;
140         connman_network_set_data(network, NULL);
141
142         device = connman_network_get_device(network);
143         if (device != NULL)
144                 connman_device_remove_network(device, network);
145
146         connman_network_unref(network);
147 }
148
149 static void bluetooth_pan_remove(struct connman_network *network)
150 {
151         struct bluetooth_pan *pan = connman_network_get_data(network);
152
153         DBG("network %p pan %p", network, pan);
154
155         connman_network_set_data(network, NULL);
156
157         if (pan != NULL)
158                 pan_remove_nap(pan);
159 }
160
161 static int bluetooth_pan_connect(struct connman_network *network)
162 {
163         return -EIO;
164 }
165
166 static int bluetooth_pan_disconnect(struct connman_network *network)
167 {
168         return -EIO;
169 }
170
171 static void btnetwork_property_change(GDBusProxy *proxy, const char *name,
172                 DBusMessageIter *iter, void *user_data)
173 {
174         struct bluetooth_pan *pan;
175         connman_bool_t proxy_connected;
176
177         if (strcmp(name, "Connected") != 0)
178                 return;
179
180         pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy));
181         if (pan == NULL)
182                 return;
183
184         dbus_message_iter_get_basic(iter, &proxy_connected);
185
186         DBG("proxy connected %d", proxy_connected);
187 }
188
189 static void pan_create_nap(struct bluetooth_pan *pan)
190 {
191         struct connman_device *device;
192
193         if (proxy_get_nap(pan->btdevice_proxy) == FALSE) {
194                 pan_remove_nap(pan);
195                 return;
196         }
197
198         device = g_hash_table_lookup(devices,
199                         proxy_get_string(pan->btdevice_proxy, "Adapter"));
200
201         if (device == NULL || connman_device_get_powered(device) == FALSE)
202                 return;
203
204         if (pan->network == NULL) {
205                 const char *address;
206                 char ident[BLUETOOTH_ADDR_LEN * 2 + 1];
207                 const char *name, *path;
208
209                 address = proxy_get_string(pan->btdevice_proxy, "Address");
210                 address2ident(address, ident);
211
212                 pan->network = connman_network_create(ident,
213                                 CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
214
215                 name = proxy_get_string(pan->btdevice_proxy, "Alias");
216                 path = g_dbus_proxy_get_path(pan->btnetwork_proxy);
217
218                 DBG("network %p %s %s", pan->network, path, name);
219
220                 if (pan->network == NULL) {
221                         connman_warn("Bluetooth network %s creation failed",
222                                         path);
223                         return;
224                 }
225
226                 connman_network_set_data(pan->network, pan);
227                 connman_network_set_name(pan->network, name);
228                 connman_network_set_group(pan->network, ident);
229         }
230
231         connman_device_add_network(device, pan->network);
232 }
233
234 static void btdevice_property_change(GDBusProxy *proxy, const char *name,
235                 DBusMessageIter *iter, void *user_data)
236 {
237         struct bluetooth_pan *pan;
238         connman_bool_t pan_nap = FALSE;
239
240         if (strcmp(name, "UUIDs") != 0)
241                 return;
242
243         pan = g_hash_table_lookup(networks, g_dbus_proxy_get_path(proxy));
244         if (pan == NULL)
245                 return;
246
247         if (pan->network != NULL &&
248                         connman_network_get_device(pan->network) != NULL)
249                 pan_nap = TRUE;
250
251         DBG("network %p network nap %d proxy nap %d", pan->network, pan_nap,
252                         proxy_get_nap(pan->btdevice_proxy));
253
254         if (proxy_get_nap(pan->btdevice_proxy) == pan_nap)
255                 return;
256
257         pan_create_nap(pan);
258 }
259
260 static void pan_free(gpointer data)
261 {
262         struct bluetooth_pan *pan = data;
263
264         if (pan->btnetwork_proxy != NULL) {
265                 g_dbus_proxy_unref(pan->btnetwork_proxy);
266                 pan->btnetwork_proxy = NULL;
267         }
268
269         if (pan->btdevice_proxy != NULL) {
270                 g_dbus_proxy_unref(pan->btdevice_proxy);
271                 pan->btdevice_proxy = NULL;
272         }
273
274         pan_remove_nap(pan);
275
276         g_free(pan);
277 }
278
279 static void pan_create(GDBusProxy *network_proxy)
280 {
281         const char *path = g_dbus_proxy_get_path(network_proxy);
282         struct bluetooth_pan *pan;
283
284         pan = g_try_new0(struct bluetooth_pan, 1);
285
286         if (pan == NULL) {
287                 connman_error("Out of memory creating PAN NAP");
288                 return;
289         }
290
291         g_hash_table_replace(networks, g_strdup(path), pan);
292
293         pan->btnetwork_proxy = g_dbus_proxy_ref(network_proxy);
294         pan->btdevice_proxy = g_dbus_proxy_new(client, path,
295                         "org.bluez.Device1");
296
297         if (pan->btdevice_proxy == NULL) {
298                 connman_error("Cannot create BT PAN watcher %s", path);
299                 g_hash_table_remove(networks, path);
300                 return;
301         }
302
303         g_dbus_proxy_set_property_watch(pan->btnetwork_proxy,
304                         btnetwork_property_change, NULL);
305
306         g_dbus_proxy_set_property_watch(pan->btdevice_proxy,
307                         btdevice_property_change, NULL);
308
309         DBG("pan %p %s nap %d", pan, path, proxy_get_nap(pan->btdevice_proxy));
310
311         pan_create_nap(pan);
312 }
313
314 static struct connman_network_driver network_driver = {
315         .name           = "bluetooth",
316         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
317         .probe          = bluetooth_pan_probe,
318         .remove         = bluetooth_pan_remove,
319         .connect        = bluetooth_pan_connect,
320         .disconnect     = bluetooth_pan_disconnect,
321 };
322
323 static void device_enable_cb(const DBusError *error, void *user_data)
324 {
325         char *path = user_data;
326         struct connman_device *device;
327         GHashTableIter iter;
328         gpointer key, value;
329
330         device = g_hash_table_lookup(devices, path);
331         if (device == NULL) {
332                 DBG("device already removed");
333                 goto out;
334         }
335
336         if (dbus_error_is_set(error) == TRUE) {
337                 connman_warn("Bluetooth device %s not enabled %s",
338                                 path, error->message);
339                 goto out;
340         }
341
342         DBG("device %p %s", device, path);
343
344         connman_device_set_powered(device, TRUE);
345
346         g_hash_table_iter_init(&iter, networks);
347         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
348                 struct bluetooth_pan *pan = value;
349
350                 if (g_strcmp0(proxy_get_string(pan->btdevice_proxy, "Adapter"),
351                                                 path) == 0) {
352
353                         DBG("enable network %p", pan->network);
354                         pan_create_nap(pan);
355                 }
356         }
357
358 out:
359         g_free(path);
360 }
361
362 static int bluetooth_device_enable(struct connman_device *device)
363 {
364         GDBusProxy *proxy = connman_device_get_data(device);
365         connman_bool_t device_powered = TRUE;
366         const char *path;
367
368         if (proxy == NULL)
369                 return 0;
370
371         path = g_dbus_proxy_get_path(proxy);
372
373         if (proxy_get_bool(proxy, "Powered") == TRUE) {
374                 DBG("already enabled %p %s", device, path);
375                 return -EALREADY;
376         }
377
378         DBG("device %p %s", device, path);
379
380         g_dbus_proxy_set_property_basic(proxy, "Powered",
381                         DBUS_TYPE_BOOLEAN, &device_powered,
382                         device_enable_cb, g_strdup(path), NULL);
383
384         return -EINPROGRESS;
385 }
386
387 static void device_disable_cb(const DBusError *error, void *user_data)
388 {
389         char *path = user_data;
390         struct connman_device *device;
391         GHashTableIter iter;
392         gpointer key, value;
393
394         device = g_hash_table_lookup(devices, path);
395         if (device == NULL) {
396                 DBG("device already removed");
397                 goto out;
398         }
399
400         if (dbus_error_is_set(error) == TRUE) {
401                 connman_warn("Bluetooth device %s not disabled: %s",
402                                 path, error->message);
403                 goto out;
404         }
405
406         DBG("device %p %s", device, path);
407         connman_device_set_powered(device, FALSE);
408
409         g_hash_table_iter_init(&iter, networks);
410         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
411                 struct bluetooth_pan *pan = value;
412
413                 if (connman_network_get_device(pan->network) == device) {
414                         DBG("disable network %p", pan->network);
415                         connman_device_remove_network(device, pan->network);
416                 }
417         }
418
419 out:
420         g_free(path);
421 }
422
423 static int bluetooth_device_disable(struct connman_device *device)
424 {
425         GDBusProxy *proxy = connman_device_get_data(device);
426         connman_bool_t device_powered = FALSE;
427         const char *path;
428
429         if (proxy == NULL)
430                 return 0;
431
432         path = g_dbus_proxy_get_path(proxy);
433
434         if (proxy_get_bool(proxy, "Powered") == FALSE) {
435                 DBG("already disabled %p %s", device, path);
436                 return -EALREADY;
437         }
438
439         DBG("device %p %s", device, path);
440
441         g_dbus_proxy_set_property_basic(proxy, "Powered",
442                         DBUS_TYPE_BOOLEAN, &device_powered,
443                         device_disable_cb, g_strdup(path), NULL);
444
445         return -EINPROGRESS;
446 }
447
448 static void adapter_property_change(GDBusProxy *proxy, const char *name,
449                 DBusMessageIter *iter, void *user_data)
450 {
451         struct connman_device *device;
452         const char *path;
453         connman_bool_t adapter_powered, device_powered;
454
455         if (strcmp(name, "Powered") != 0)
456                 return;
457
458         path = g_dbus_proxy_get_path(proxy);
459         device = g_hash_table_lookup(devices, path);
460
461         adapter_powered = proxy_get_bool(proxy, "Powered");
462         device_powered = connman_device_get_powered(device);
463
464         DBG("device %p %s device powered %d adapter powered %d", device, path,
465                         device_powered, adapter_powered);
466
467         if (device_powered != adapter_powered) {
468                 DBG("powering adapter");
469                 if (device_powered == TRUE)
470                         bluetooth_device_enable(device);
471                 else
472                         bluetooth_device_disable(device);
473         }
474 }
475
476 static void device_free(gpointer data)
477 {
478         struct connman_device *device = data;
479         GDBusProxy *proxy = connman_device_get_data(device);
480
481         connman_device_set_data(device, NULL);
482         if (proxy != NULL)
483                 g_dbus_proxy_unref(proxy);
484
485         connman_device_unregister(device);
486         connman_device_unref(device);
487 }
488
489 static void device_create(GDBusProxy *proxy)
490 {
491         struct connman_device *device = NULL;
492         const char *path = g_dbus_proxy_get_path(proxy);
493         const char *address;
494         char ident[BLUETOOTH_ADDR_LEN * 2 + 1];
495         connman_bool_t powered;
496
497         address = proxy_get_string(proxy, "Address");
498         if (address == NULL)
499                 return;
500
501         address2ident(address, ident);
502
503         device = connman_device_create("bluetooth",
504                         CONNMAN_DEVICE_TYPE_BLUETOOTH);
505         if (device == NULL)
506                 return;
507
508         connman_device_set_data(device, g_dbus_proxy_ref(proxy));
509         connman_device_set_ident(device, ident);
510
511         g_hash_table_replace(devices, g_strdup(path), device);
512
513         DBG("device %p %s device powered %d adapter powered %d", device,
514                         path, connman_device_get_powered(device),
515                         proxy_get_bool(proxy, "Powered"));
516
517         if (connman_device_register(device) < 0) {
518                 g_hash_table_remove(devices, device);
519                 return;
520         }
521
522         g_dbus_proxy_set_property_watch(proxy, adapter_property_change, NULL);
523
524         powered = proxy_get_bool(proxy, "Powered");
525         connman_device_set_powered(device, powered);
526 }
527
528 static void object_added(GDBusProxy *proxy, void *user_data)
529 {
530         const char *interface;
531
532         interface = g_dbus_proxy_get_interface(proxy);
533
534         if (strcmp(interface, "org.bluez.Adapter1") == 0) {
535                 DBG("%s %s", interface, g_dbus_proxy_get_path(proxy));
536                 device_create(proxy);
537                 return;
538         }
539
540         if (strcmp(interface, "org.bluez.Network1") == 0) {
541                 DBG("%s %s", interface, g_dbus_proxy_get_path(proxy));
542                 pan_create(proxy);
543                 return;
544         }
545 }
546
547 static void object_removed(GDBusProxy *proxy, void *user_data)
548 {
549         const char *interface, *path;
550
551         interface = g_dbus_proxy_get_interface(proxy);
552
553         if (strcmp(interface, "org.bluez.Adapter1") == 0) {
554                 path = g_dbus_proxy_get_path(proxy);
555                 DBG("%s %s", interface, path);
556
557                 g_hash_table_remove(devices, path);
558         }
559
560         if (strcmp(interface, "org.bluez.Network1") == 0) {
561                 path = g_dbus_proxy_get_path(proxy);
562                 DBG("%s %s", interface, path);
563
564                 g_hash_table_remove(networks, path);
565         }
566
567 }
568
569 static int bluetooth_device_probe(struct connman_device *device)
570 {
571         GHashTableIter iter;
572         gpointer key, value;
573
574         g_hash_table_iter_init(&iter, devices);
575
576         while (g_hash_table_iter_next(&iter, &key, &value) == TRUE) {
577                 struct connman_device *known = value;
578
579                 if (device == known)
580                         return 0;
581         }
582
583         return -EOPNOTSUPP;
584 }
585
586 static void bluetooth_device_remove(struct connman_device *device)
587 {
588         DBG("%p", device);
589 }
590
591 static struct connman_device_driver device_driver = {
592         .name           = "bluetooth",
593         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
594         .probe          = bluetooth_device_probe,
595         .remove         = bluetooth_device_remove,
596         .enable         = bluetooth_device_enable,
597         .disable        = bluetooth_device_disable,
598 };
599
600 static int bluetooth_tech_probe(struct connman_technology *technology)
601 {
602         return 0;
603 }
604
605 static void bluetooth_tech_remove(struct connman_technology *technology)
606 {
607
608 }
609
610 static struct connman_technology_driver tech_driver = {
611         .name           = "bluetooth",
612         .type           = CONNMAN_SERVICE_TYPE_BLUETOOTH,
613         .probe          = bluetooth_tech_probe,
614         .remove         = bluetooth_tech_remove,
615 };
616
617 static int bluetooth_init(void)
618 {
619         connection = connman_dbus_get_connection();
620         if (connection == NULL)
621                 goto out;
622
623         if (connman_technology_driver_register(&tech_driver) < 0) {
624                 connman_warn("Failed to initialize technology for Bluez 5");
625                 goto out;
626         }
627
628         devices = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
629                         device_free);
630
631         if (connman_device_driver_register(&device_driver) < 0) {
632                 connman_warn("Failed to initialize device driver for "
633                                 BLUEZ_SERVICE);
634                 connman_technology_driver_unregister(&tech_driver);
635                 goto out;
636         }
637
638         if (connman_network_driver_register(&network_driver) < 0) {
639                 connman_technology_driver_unregister(&tech_driver);
640                 connman_device_driver_unregister(&device_driver);
641                 goto out;
642         }
643
644         networks = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
645                         pan_free);
646
647         client = g_dbus_client_new(connection, BLUEZ_SERVICE, BLUEZ_PATH);
648         if (client == NULL) {
649                 connman_warn("Failed to initialize D-Bus client for "
650                                 BLUEZ_SERVICE);
651                 goto out;
652         }
653
654         g_dbus_client_set_proxy_handlers(client, object_added, object_removed,
655                         NULL, NULL);
656
657         return 0;
658
659 out:
660         if (networks != NULL)
661                 g_hash_table_destroy(networks);
662
663         if (devices != NULL)
664                 g_hash_table_destroy(devices);
665
666         if (client != NULL)
667                 g_dbus_client_unref(client);
668
669         if (connection != NULL)
670                 dbus_connection_unref(connection);
671
672         return -EIO;
673 }
674
675 static void bluetooth_exit(void)
676 {
677         connman_network_driver_unregister(&network_driver);
678         g_hash_table_destroy(networks);
679
680         connman_device_driver_unregister(&device_driver);
681         g_hash_table_destroy(devices);
682
683         connman_technology_driver_unregister(&tech_driver);
684         dbus_connection_unref(connection);
685 }
686
687 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
688                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)