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