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