aec26d1edddecb3ad3344aa84d9bf5423bf8b9b6
[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
28 #include <gdbus.h>
29
30 #include <connman/plugin.h>
31 #include <connman/driver.h>
32 #include <connman/dbus.h>
33 #include <connman/log.h>
34
35 #define BLUEZ_SERVICE                   "org.bluez"
36 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
37 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
38
39 #define LIST_ADAPTERS                   "ListAdapters"
40 #define ADAPTER_ADDED                   "AdapterAdded"
41 #define ADAPTER_REMOVED                 "AdapterRemoved"
42
43 #define PROPERTY_CHANGED                "PropertyChanged"
44 #define SET_PROPERTY                    "SetProperty"
45
46 #define TIMEOUT 5000
47
48 struct adapter_data {
49         DBusConnection *connection;
50 };
51
52 static int bluetooth_probe(struct connman_element *adapter)
53 {
54         struct adapter_data *data;
55
56         DBG("adapter %p name %s", adapter, adapter->name);
57
58         data = g_try_new0(struct adapter_data, 1);
59         if (data == NULL)
60                 return -ENOMEM;
61
62         data->connection = connman_dbus_get_connection();
63         if (data->connection == NULL) {
64                 g_free(data);
65                 return -EIO;
66         }
67
68         connman_element_set_data(adapter, data);
69
70         return 0;
71 }
72
73 static void bluetooth_remove(struct connman_element *adapter)
74 {
75         struct adapter_data *data = connman_element_get_data(adapter);
76
77         DBG("adapter %p name %s", adapter, adapter->name);
78
79         connman_element_set_data(adapter, NULL);
80
81         dbus_connection_unref(data->connection);
82
83         g_free(data);
84 }
85
86 static void powered_reply(DBusPendingCall *call, void *user_data)
87 {
88         DBusMessage *reply;
89
90         DBG("");
91
92         reply = dbus_pending_call_steal_reply(call);
93
94         dbus_message_unref(reply);
95 }
96
97 static int change_powered(DBusConnection *connection, const char *path,
98                                                         dbus_bool_t powered)
99 {
100         DBusMessage *message;
101         DBusMessageIter iter;
102         DBusPendingCall *call;
103
104         DBG("");
105
106         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
107                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
108         if (message == NULL)
109                 return -ENOMEM;
110
111         dbus_message_iter_init_append(message, &iter);
112         connman_dbus_property_append_variant(&iter, "Powered",
113                                                 DBUS_TYPE_BOOLEAN, &powered);
114
115         if (dbus_connection_send_with_reply(connection, message,
116                                                 &call, TIMEOUT) == FALSE) {
117                 connman_error("Failed to change Powered property");
118                 dbus_message_unref(message);
119                 return -EINVAL;
120         }
121
122         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
123
124         dbus_message_unref(message);
125
126         return -EINPROGRESS;
127 }
128
129 static int bluetooth_enable(struct connman_element *adapter)
130 {
131         struct adapter_data *data = connman_element_get_data(adapter);
132
133         DBG("adapter %p name %s", adapter, adapter->name);
134
135         return change_powered(data->connection, adapter->devpath, TRUE);
136 }
137
138 static int bluetooth_disable(struct connman_element *adapter)
139 {
140         struct adapter_data *data = connman_element_get_data(adapter);
141
142         DBG("adapter %p name %s", adapter, adapter->name);
143
144         return change_powered(data->connection, adapter->devpath, FALSE);
145 }
146
147 static struct connman_driver bluetooth_driver = {
148         .name           = "bluetooth",
149         .type           = CONNMAN_ELEMENT_TYPE_DEVICE,
150         .subtype        = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH,
151         .probe          = bluetooth_probe,
152         .remove         = bluetooth_remove,
153         .enable         = bluetooth_enable,
154         .disable        = bluetooth_disable,
155 };
156
157 static GSList *device_list = NULL;
158
159 static struct connman_element *find_adapter(const char *path)
160 {
161         GSList *list;
162
163         DBG("path %s", path);
164
165         for (list = device_list; list; list = list->next) {
166                 struct connman_element *device = list->data;
167
168                 if (g_str_equal(device->devpath, path) == TRUE)
169                         return device;
170         }
171
172         return NULL;
173 }
174
175 static void property_changed(DBusConnection *connection, DBusMessage *message)
176 {
177         const char *path = dbus_message_get_path(message);
178         struct connman_element *device;
179         DBusMessageIter iter, value;
180         const char *key;
181
182         DBG("path %s", path);
183
184         device = find_adapter(path);
185         if (device == NULL)
186                 return;
187
188         if (dbus_message_iter_init(message, &iter) == FALSE)
189                 return;
190
191         dbus_message_iter_get_basic(&iter, &key);
192
193         dbus_message_iter_next(&iter);
194         dbus_message_iter_recurse(&iter, &value);
195
196         if (g_str_equal(key, "Powered") == TRUE) {
197                 gboolean val;
198
199                 dbus_message_iter_get_basic(&value, &val);
200                 connman_element_set_enabled(device, val);
201         } else if (g_str_equal(key, "Discovering") == TRUE) {
202                 gboolean val;
203
204                 dbus_message_iter_get_basic(&value, &val);
205                 connman_element_set_scanning(device, val);
206         }
207 }
208
209 static void properties_reply(DBusPendingCall *call, void *user_data)
210 {
211         DBusMessage *message = user_data;
212         const char *path = dbus_message_get_path(message);
213         struct connman_element *device;
214         DBusMessageIter array, dict;
215         DBusMessage *reply;
216
217         DBG("path %s", path);
218
219         device = find_adapter(path);
220
221         dbus_message_unref(message);
222
223         reply = dbus_pending_call_steal_reply(call);
224
225         if (device == NULL)
226                 goto done;
227
228         if (dbus_message_iter_init(reply, &array) == FALSE)
229                 goto done;
230
231         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
232                 goto done;
233
234         dbus_message_iter_recurse(&array, &dict);
235         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
236                 DBusMessageIter entry, value;
237                 const char *key;
238
239                 dbus_message_iter_recurse(&dict, &entry);
240                 dbus_message_iter_get_basic(&entry, &key);
241
242                 dbus_message_iter_next(&entry);
243                 dbus_message_iter_recurse(&entry, &value);
244
245                 if (g_str_equal(key, "Powered") == TRUE) {
246                         gboolean val;
247
248                         dbus_message_iter_get_basic(&value, &val);
249                         connman_element_set_enabled(device, val);
250                 } else if (g_str_equal(key, "Discovering") == TRUE) {
251                         gboolean val;
252
253                         dbus_message_iter_get_basic(&value, &val);
254                         connman_element_set_scanning(device, val);
255                 }
256
257                 dbus_message_iter_next(&dict);
258         }
259
260 done:
261         dbus_message_unref(reply);
262 }
263
264 static void devices_reply(DBusPendingCall *call, void *user_data)
265 {
266         DBusMessage *message = user_data;
267         const char *path = dbus_message_get_path(message);
268         DBusMessage *reply;
269         DBusError error;
270         char **devices;
271         int i, num_devices;
272
273         DBG("path %s", path);
274
275         dbus_message_unref(message);
276
277         reply = dbus_pending_call_steal_reply(call);
278
279         dbus_error_init(&error);
280
281         if (dbus_message_get_args(reply, &error,
282                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
283                                                 &devices, &num_devices,
284                                                 DBUS_TYPE_INVALID) == FALSE) {
285                 if (dbus_error_is_set(&error) == TRUE) {
286                         connman_error("%s", error.message);
287                         dbus_error_free(&error);
288                 } else
289                         connman_error("Wrong arguments for device list");
290                 goto done;
291         }
292
293         for (i = 0; i < num_devices; i++) {
294                 DBG("device %s", devices[i]);
295         }
296
297         g_strfreev(devices);
298
299 done:
300         dbus_message_unref(reply);
301 }
302
303 static void add_adapter(DBusConnection *connection, const char *path)
304 {
305         struct connman_element *device;
306         DBusMessage *message;
307         DBusPendingCall *call;
308
309         DBG("path %s", path);
310
311         device = find_adapter(path);
312         if (device != NULL)
313                 return;
314
315         device = connman_element_create(NULL);
316         device->type = CONNMAN_ELEMENT_TYPE_DEVICE;
317         device->subtype = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH;
318         device->policy = CONNMAN_ELEMENT_POLICY_IGNORE;
319
320         device->name = g_path_get_basename(path);
321         device->devpath = g_strdup(path);
322
323         if (connman_element_register(device, NULL) < 0) {
324                 connman_element_unref(device);
325                 return;
326         }
327
328         device_list = g_slist_append(device_list, device);
329
330         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
331                                 BLUEZ_ADAPTER_INTERFACE, "GetProperties");
332         if (message == NULL)
333                 return;
334
335         if (dbus_connection_send_with_reply(connection, message,
336                                                 &call, TIMEOUT) == FALSE) {
337                 connman_error("Failed to get adapter properties");
338                 dbus_message_unref(message);
339                 return;
340         }
341
342         dbus_pending_call_set_notify(call, properties_reply, message, NULL);
343
344         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
345                                 BLUEZ_ADAPTER_INTERFACE, "ListDevices");
346         if (message == NULL)
347                 return;
348
349         if (dbus_connection_send_with_reply(connection, message,
350                                                 &call, TIMEOUT) == FALSE) {
351                 connman_error("Failed to get Bluetooth devices");
352                 dbus_message_unref(message);
353                 return;
354         }
355
356         dbus_pending_call_set_notify(call, devices_reply, message, NULL);
357 }
358
359 static void remove_adapter(DBusConnection *connection, const char *path)
360 {
361         struct connman_element *device;
362
363         DBG("path %s", path);
364
365         device = find_adapter(path);
366         if (device == NULL)
367                 return;
368
369         device_list = g_slist_remove(device_list, device);
370
371         connman_element_unregister(device);
372         connman_element_unref(device);
373 }
374
375 static void adapters_reply(DBusPendingCall *call, void *user_data)
376 {
377         DBusConnection *connection = user_data;
378         DBusMessage *reply;
379         DBusError error;
380         char **adapters;
381         int i, num_adapters;
382
383         DBG("");
384
385         reply = dbus_pending_call_steal_reply(call);
386
387         dbus_error_init(&error);
388
389         if (dbus_message_get_args(reply, &error,
390                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
391                                                 &adapters, &num_adapters,
392                                                 DBUS_TYPE_INVALID) == FALSE) {
393                 if (dbus_error_is_set(&error) == TRUE) {
394                         connman_error("%s", error.message);
395                         dbus_error_free(&error);
396                 } else
397                         connman_error("Wrong arguments for adapter list");
398                 goto done;
399         }
400
401         for (i = 0; i < num_adapters; i++)
402                 add_adapter(connection, adapters[i]);
403
404         g_strfreev(adapters);
405
406 done:
407         dbus_message_unref(reply);
408 }
409
410 static void bluetooth_connect(DBusConnection *connection, void *user_data)
411 {
412         DBusMessage *message;
413         DBusPendingCall *call;
414
415         DBG("connection %p", connection);
416
417         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
418                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
419         if (message == NULL)
420                 return;
421
422         if (dbus_connection_send_with_reply(connection, message,
423                                                 &call, TIMEOUT) == FALSE) {
424                 connman_error("Failed to get Bluetooth adapters");
425                 dbus_message_unref(message);
426                 return;
427         }
428
429         dbus_pending_call_set_notify(call, adapters_reply, connection, NULL);
430
431         dbus_message_unref(message);
432 }
433
434 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
435 {
436         GSList *list;
437
438         DBG("connection %p", connection);
439
440         for (list = device_list; list; list = list->next) {
441                 struct connman_element *device = list->data;
442
443                 connman_element_unregister(device);
444                 connman_element_unref(device);
445         }
446
447         g_slist_free(device_list);
448         device_list = NULL;
449 }
450
451 static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
452                                         DBusMessage *message, void *user_data)
453 {
454         DBG("connection %p", connection);
455
456         if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
457                                                 PROPERTY_CHANGED) == TRUE) {
458                 property_changed(connection, message);
459         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
460                                                 ADAPTER_ADDED) == TRUE) {
461                 const char *path;
462                 dbus_message_get_args(message, NULL,
463                                         DBUS_TYPE_OBJECT_PATH, &path,
464                                                         DBUS_TYPE_INVALID);
465                 add_adapter(connection, path);
466         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
467                                                 ADAPTER_REMOVED) == TRUE) {
468                 const char *path;
469                 dbus_message_get_args(message, NULL,
470                                         DBUS_TYPE_OBJECT_PATH, &path,
471                                                         DBUS_TYPE_INVALID);
472                 remove_adapter(connection, path);
473         }
474
475         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
476 }
477
478 static DBusConnection *connection;
479 static guint watch;
480
481 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
482                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
483 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
484                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
485
486 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
487                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
488
489 static int bluetooth_init(void)
490 {
491         int err = -EIO;
492
493         connection = connman_dbus_get_connection();
494         if (connection == NULL)
495                 return -EIO;
496
497         if (dbus_connection_add_filter(connection, bluetooth_signal,
498                                                         NULL, NULL) == FALSE)
499                 goto unref;
500
501         err = connman_driver_register(&bluetooth_driver);
502         if (err < 0)
503                 goto remove;
504
505         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
506                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
507         if (watch == 0) {
508                 connman_driver_unregister(&bluetooth_driver);
509                 err = -EIO;
510                 goto remove;
511         }
512
513         if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
514                 bluetooth_connect(connection, NULL);
515
516         dbus_bus_add_match(connection, added_rule, NULL);
517         dbus_bus_add_match(connection, removed_rule, NULL);
518         dbus_bus_add_match(connection, adapter_rule, NULL);
519         dbus_connection_flush(connection);
520
521         return 0;
522
523 remove:
524         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
525
526 unref:
527         dbus_connection_unref(connection);
528
529         return err;
530 }
531
532 static void bluetooth_exit(void)
533 {
534         dbus_bus_remove_match(connection, adapter_rule, NULL);
535         dbus_bus_remove_match(connection, removed_rule, NULL);
536         dbus_bus_remove_match(connection, added_rule, NULL);
537         dbus_connection_flush(connection);
538
539         g_dbus_remove_watch(connection, watch);
540
541         bluetooth_disconnect(connection, NULL);
542
543         connman_driver_unregister(&bluetooth_driver);
544
545         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
546
547         dbus_connection_unref(connection);
548 }
549
550 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
551                                                 bluetooth_init, bluetooth_exit)