Add CONNMAN_API_SUBJECT_TO_CHANGE definition
[connman] / plugins / bluetooth.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  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 <stdlib.h>
28
29 #include <gdbus.h>
30
31 #define CONNMAN_API_SUBJECT_TO_CHANGE
32 #include <connman/plugin.h>
33 #include <connman/device.h>
34 #include <connman/dbus.h>
35 #include <connman/log.h>
36
37 #define BLUEZ_SERVICE                   "org.bluez"
38 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
39 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
40 #define BLUEZ_DEVICE_INTERFACE          BLUEZ_SERVICE ".Device"
41
42 #define LIST_ADAPTERS                   "ListAdapters"
43 #define ADAPTER_ADDED                   "AdapterAdded"
44 #define ADAPTER_REMOVED                 "AdapterRemoved"
45
46 #define PROPERTY_CHANGED                "PropertyChanged"
47 #define GET_PROPERTIES                  "GetProperties"
48 #define SET_PROPERTY                    "SetProperty"
49
50 #define TIMEOUT 5000
51
52 typedef void (* properties_callback_t) (DBusConnection *connection,
53                                                         const char *path,
54                                                         DBusMessage *message,
55                                                         void *user_data);
56
57 struct properties_data {
58         DBusConnection *connection;
59         DBusMessage *message;
60         properties_callback_t callback;
61         void *user_data;
62 };
63
64 static void get_properties_reply(DBusPendingCall *call, void *user_data)
65 {
66         struct properties_data *data = user_data;
67         DBusMessage *reply;
68         const char *path;
69
70         reply = dbus_pending_call_steal_reply(call);
71         if (reply == NULL)
72                 goto done;
73
74         path = dbus_message_get_path(data->message);
75
76         data->callback(data->connection, path, reply, data->user_data);
77
78         dbus_message_unref(reply);
79
80 done:
81         dbus_message_unref(data->message);
82         g_free(data);
83 }
84
85 static void get_properties(DBusConnection *connection,
86                                 const char *path, const char *interface,
87                                 properties_callback_t callback, void *user_data)
88 {
89         struct properties_data *data;
90         DBusMessage *message;
91         DBusPendingCall *call;
92
93         DBG("path %s interface %s", path, interface);
94
95         data = g_try_new0(struct properties_data, 1);
96         if (data == NULL)
97                 return;
98
99         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
100                                                 interface, GET_PROPERTIES);
101         if (message == NULL) {
102                 g_free(data);
103                 return;
104         }
105
106         if (dbus_connection_send_with_reply(connection, message,
107                                                 &call, TIMEOUT) == FALSE) {
108                 connman_error("Failed to get properties for %s", interface);
109                 dbus_message_unref(message);
110                 g_free(data);
111                 return;
112         }
113
114         data->connection = connection;
115         data->message    = message;
116         data->callback   = callback;
117         data->user_data  = user_data;
118
119         dbus_pending_call_set_notify(call, get_properties_reply, data, NULL);
120 }
121
122 struct adapter_data {
123         DBusConnection *connection;
124 };
125
126 static int bluetooth_probe(struct connman_device *adapter)
127 {
128         struct adapter_data *data;
129
130         DBG("adapter %p", adapter);
131
132         data = g_try_new0(struct adapter_data, 1);
133         if (data == NULL)
134                 return -ENOMEM;
135
136         data->connection = connman_dbus_get_connection();
137         if (data->connection == NULL) {
138                 g_free(data);
139                 return -EIO;
140         }
141
142         connman_device_set_data(adapter, data);
143
144         return 0;
145 }
146
147 static void bluetooth_remove(struct connman_device *adapter)
148 {
149         struct adapter_data *data = connman_device_get_data(adapter);
150
151         DBG("adapter %p", adapter);
152
153         connman_device_set_data(adapter, NULL);
154
155         dbus_connection_unref(data->connection);
156
157         g_free(data);
158 }
159
160 static void powered_reply(DBusPendingCall *call, void *user_data)
161 {
162         DBusMessage *reply;
163
164         DBG("");
165
166         reply = dbus_pending_call_steal_reply(call);
167
168         dbus_message_unref(reply);
169 }
170
171 static int change_powered(DBusConnection *connection, const char *path,
172                                                         dbus_bool_t powered)
173 {
174         DBusMessage *message;
175         DBusMessageIter iter;
176         DBusPendingCall *call;
177
178         DBG("");
179
180         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
181                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
182         if (message == NULL)
183                 return -ENOMEM;
184
185         dbus_message_iter_init_append(message, &iter);
186         connman_dbus_property_append_variant(&iter, "Powered",
187                                                 DBUS_TYPE_BOOLEAN, &powered);
188
189         if (dbus_connection_send_with_reply(connection, message,
190                                                 &call, TIMEOUT) == FALSE) {
191                 connman_error("Failed to change Powered property");
192                 dbus_message_unref(message);
193                 return -EINVAL;
194         }
195
196         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
197
198         dbus_message_unref(message);
199
200         return -EINPROGRESS;
201 }
202
203 static int bluetooth_enable(struct connman_device *adapter)
204 {
205         struct adapter_data *data = connman_device_get_data(adapter);
206         const char *path = connman_device_get_path(adapter);
207
208         DBG("adapter %p", adapter);
209
210         return change_powered(data->connection, path, TRUE);
211 }
212
213 static int bluetooth_disable(struct connman_device *adapter)
214 {
215         struct adapter_data *data = connman_device_get_data(adapter);
216         const char *path = connman_device_get_path(adapter);
217
218         DBG("adapter %p", adapter);
219
220         return change_powered(data->connection, path, FALSE);
221 }
222
223 static int bluetooth_scan(struct connman_device *adapter)
224 {
225         DBG("adapter %p", adapter);
226
227         return -EIO;
228 }
229
230 static struct connman_device_driver bluetooth_driver = {
231         .name           = "bluetooth",
232         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
233         .probe          = bluetooth_probe,
234         .remove         = bluetooth_remove,
235         .enable         = bluetooth_enable,
236         .disable        = bluetooth_disable,
237         .scan           = bluetooth_scan,
238 };
239
240 static GSList *adapter_list = NULL;
241
242 static void free_adapters(void)
243 {
244         GSList *list;
245
246         DBG("");
247
248         for (list = adapter_list; list; list = list->next) {
249                 struct connman_device *adapter = list->data;
250
251                 connman_device_unregister(adapter);
252                 connman_device_unref(adapter);
253         }
254
255         g_slist_free(adapter_list);
256         adapter_list = NULL;
257 }
258
259 static struct connman_device *find_adapter(const char *path)
260 {
261         GSList *list;
262
263         DBG("path %s", path);
264
265         for (list = adapter_list; list; list = list->next) {
266                 struct connman_device *adapter = list->data;
267                 const char *adapter_path = connman_device_get_path(adapter);
268
269                 if (adapter_path == NULL)
270                         continue;
271
272                 if (g_str_equal(adapter_path, path) == TRUE)
273                         return adapter;
274         }
275
276         return NULL;
277 }
278
279 static void device_properties(DBusConnection *connection, const char *path,
280                                 DBusMessage *message, void *user_data)
281 {
282         struct connman_device *device = user_data;
283         const char *node = g_basename(path);
284         struct connman_network *network;
285
286         DBG("path %s", path);
287
288         network = connman_device_get_network(device, node);
289         if (network != NULL)
290                 return;
291
292         network = connman_network_create(node, CONNMAN_NETWORK_TYPE_WIFI);
293         if (network == NULL)
294                 return;
295
296         connman_device_add_network(device, network);
297 }
298
299 static void check_devices(struct connman_device *adapter,
300                         DBusConnection *connection, DBusMessageIter *array)
301 {
302         DBusMessageIter value;
303
304         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
305                 return;
306
307         dbus_message_iter_recurse(array, &value);
308
309         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
310                 const char *path;
311
312                 dbus_message_iter_get_basic(&value, &path);
313
314                 get_properties(connection, path, BLUEZ_DEVICE_INTERFACE,
315                                                 device_properties, adapter);
316
317                 dbus_message_iter_next(&value);
318         }
319 }
320
321 static void property_changed(DBusConnection *connection, DBusMessage *message)
322 {
323         const char *path = dbus_message_get_path(message);
324         struct connman_device *adapter;
325         DBusMessageIter iter, value;
326         const char *key;
327
328         DBG("path %s", path);
329
330         adapter = find_adapter(path);
331         if (adapter == NULL)
332                 return;
333
334         if (dbus_message_iter_init(message, &iter) == FALSE)
335                 return;
336
337         dbus_message_iter_get_basic(&iter, &key);
338
339         dbus_message_iter_next(&iter);
340         dbus_message_iter_recurse(&iter, &value);
341
342         if (g_str_equal(key, "Powered") == TRUE) {
343                 gboolean val;
344
345                 dbus_message_iter_get_basic(&value, &val);
346                 connman_device_set_powered(adapter, val);
347         } else if (g_str_equal(key, "Discovering") == TRUE) {
348                 gboolean val;
349
350                 dbus_message_iter_get_basic(&value, &val);
351                 connman_device_set_scanning(adapter, val);
352         }
353 }
354
355 static void parse_adapter_properties(struct connman_device *adapter,
356                                                 DBusConnection *connection,
357                                                         DBusMessage *reply)
358 {
359         DBusMessageIter array, dict;
360
361         if (dbus_message_iter_init(reply, &array) == FALSE)
362                 return;
363
364         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
365                 return;
366
367         dbus_message_iter_recurse(&array, &dict);
368
369         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
370                 DBusMessageIter entry, value;
371                 const char *key;
372
373                 dbus_message_iter_recurse(&dict, &entry);
374                 dbus_message_iter_get_basic(&entry, &key);
375
376                 dbus_message_iter_next(&entry);
377                 dbus_message_iter_recurse(&entry, &value);
378
379                 if (g_str_equal(key, "Powered") == TRUE) {
380                         gboolean val;
381
382                         dbus_message_iter_get_basic(&value, &val);
383                         connman_device_set_powered(adapter, val);
384                 } else if (g_str_equal(key, "Discovering") == TRUE) {
385                         gboolean val;
386
387                         dbus_message_iter_get_basic(&value, &val);
388                         connman_device_set_scanning(adapter, val);
389                 } else if (g_str_equal(key, "Devices") == TRUE) {
390                         check_devices(adapter, connection, &value);
391                 }
392
393                 dbus_message_iter_next(&dict);
394         }
395 }
396
397 static void adapter_properties(DBusConnection *connection, const char *path,
398                                 DBusMessage *message, void *user_data)
399 {
400         const char *node = g_basename(path);
401         struct connman_device *adapter;
402
403         DBG("path %s", path);
404
405         adapter = find_adapter(path);
406         if (adapter != NULL)
407                 goto done;
408
409         adapter = connman_device_create(node, CONNMAN_DEVICE_TYPE_BLUETOOTH);
410         if (adapter == NULL)
411                 return;
412
413         connman_device_set_path(adapter, path);
414
415         if (g_str_has_prefix(node, "hci") == TRUE) {
416                 int index;
417                 errno = 0;
418                 index = atoi(node + 3);
419                 if (errno == 0)
420                         connman_device_set_index(adapter, index);
421         }
422
423         connman_device_set_interface(adapter, node);
424
425         connman_device_set_policy(adapter, CONNMAN_DEVICE_POLICY_MANUAL);
426         connman_device_set_mode(adapter, CONNMAN_DEVICE_MODE_MULTIPLE_NETWORKS);
427
428         if (connman_device_register(adapter) < 0) {
429                 connman_device_unref(adapter);
430                 return;
431         }
432
433         adapter_list = g_slist_append(adapter_list, adapter);
434
435 done:
436         parse_adapter_properties(adapter, connection, message);
437 }
438
439 static void add_adapter(DBusConnection *connection, const char *path)
440 {
441         DBG("path %s", path);
442
443         get_properties(connection, path, BLUEZ_ADAPTER_INTERFACE,
444                                                 adapter_properties, NULL);
445 }
446
447 static void remove_adapter(DBusConnection *connection, const char *path)
448 {
449         struct connman_device *adapter;
450
451         DBG("path %s", path);
452
453         adapter = find_adapter(path);
454         if (adapter == NULL)
455                 return;
456
457         adapter_list = g_slist_remove(adapter_list, adapter);
458
459         connman_device_unregister(adapter);
460         connman_device_unref(adapter);
461 }
462
463 static void list_adapters_reply(DBusPendingCall *call, void *user_data)
464 {
465         DBusConnection *connection = user_data;
466         DBusMessage *reply;
467         DBusError error;
468         char **adapters;
469         int i, num_adapters;
470
471         DBG("");
472
473         reply = dbus_pending_call_steal_reply(call);
474
475         dbus_error_init(&error);
476
477         if (dbus_message_get_args(reply, &error,
478                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
479                                                 &adapters, &num_adapters,
480                                                 DBUS_TYPE_INVALID) == FALSE) {
481                 if (dbus_error_is_set(&error) == TRUE) {
482                         connman_error("%s", error.message);
483                         dbus_error_free(&error);
484                 } else
485                         connman_error("Wrong arguments for adapter list");
486                 goto done;
487         }
488
489         for (i = 0; i < num_adapters; i++)
490                 get_properties(connection, adapters[i],
491                                         BLUEZ_ADAPTER_INTERFACE,
492                                                 adapter_properties, NULL);
493
494         g_strfreev(adapters);
495
496 done:
497         dbus_message_unref(reply);
498 }
499
500 static void bluetooth_connect(DBusConnection *connection, void *user_data)
501 {
502         DBusMessage *message;
503         DBusPendingCall *call;
504
505         DBG("connection %p", connection);
506
507         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
508                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
509         if (message == NULL)
510                 return;
511
512         if (dbus_connection_send_with_reply(connection, message,
513                                                 &call, TIMEOUT) == FALSE) {
514                 connman_error("Failed to get Bluetooth adapters");
515                 dbus_message_unref(message);
516                 return;
517         }
518
519         dbus_pending_call_set_notify(call, list_adapters_reply,
520                                                         connection, NULL);
521
522         dbus_message_unref(message);
523 }
524
525 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
526 {
527         DBG("connection %p", connection);
528
529         free_adapters();
530 }
531
532 static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
533                                         DBusMessage *message, void *user_data)
534 {
535         if (dbus_message_has_interface(message,
536                         BLUEZ_MANAGER_INTERFACE) == FALSE &&
537                                 dbus_message_has_interface(message,
538                                         BLUEZ_ADAPTER_INTERFACE) == FALSE)
539                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
540
541         DBG("connection %p", connection);
542
543         if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
544                                                 PROPERTY_CHANGED) == TRUE) {
545                 property_changed(connection, message);
546         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
547                                                 ADAPTER_ADDED) == TRUE) {
548                 const char *path;
549                 dbus_message_get_args(message, NULL,
550                                         DBUS_TYPE_OBJECT_PATH, &path,
551                                                         DBUS_TYPE_INVALID);
552                 add_adapter(connection, path);
553         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
554                                                 ADAPTER_REMOVED) == TRUE) {
555                 const char *path;
556                 dbus_message_get_args(message, NULL,
557                                         DBUS_TYPE_OBJECT_PATH, &path,
558                                                         DBUS_TYPE_INVALID);
559                 remove_adapter(connection, path);
560         }
561
562         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
563 }
564
565 static DBusConnection *connection;
566 static guint watch;
567
568 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
569                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
570 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
571                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
572
573 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
574                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
575
576 static int bluetooth_init(void)
577 {
578         int err = -EIO;
579
580         connection = connman_dbus_get_connection();
581         if (connection == NULL)
582                 return -EIO;
583
584         if (dbus_connection_add_filter(connection, bluetooth_signal,
585                                                         NULL, NULL) == FALSE)
586                 goto unref;
587
588         err = connman_device_driver_register(&bluetooth_driver);
589         if (err < 0)
590                 goto remove;
591
592         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
593                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
594         if (watch == 0) {
595                 connman_device_driver_unregister(&bluetooth_driver);
596                 err = -EIO;
597                 goto remove;
598         }
599
600         if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
601                 bluetooth_connect(connection, NULL);
602
603         dbus_bus_add_match(connection, added_rule, NULL);
604         dbus_bus_add_match(connection, removed_rule, NULL);
605         dbus_bus_add_match(connection, adapter_rule, NULL);
606         dbus_connection_flush(connection);
607
608         return 0;
609
610 remove:
611         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
612
613 unref:
614         dbus_connection_unref(connection);
615
616         return err;
617 }
618
619 static void bluetooth_exit(void)
620 {
621         dbus_bus_remove_match(connection, adapter_rule, NULL);
622         dbus_bus_remove_match(connection, removed_rule, NULL);
623         dbus_bus_remove_match(connection, added_rule, NULL);
624         dbus_connection_flush(connection);
625
626         g_dbus_remove_watch(connection, watch);
627
628         free_adapters();
629
630         connman_device_driver_unregister(&bluetooth_driver);
631
632         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
633
634         dbus_connection_unref(connection);
635 }
636
637 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
638                                                 bluetooth_init, bluetooth_exit)