bluetooth: Fix using pointer-pointer when appending an array as variant
[profile/ivi/pulseaudio-panda.git] / src / modules / bluetooth / bluetooth-util.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2008-2009 Joao Paulo Rechi Vita
5
6   PulseAudio is free software; you can redistribute it and/or modify
7   it under the terms of the GNU Lesser General Public License as
8   published by the Free Software Foundation; either version 2.1 of the
9   License, or (at your option) any later version.
10
11   PulseAudio is distributed in the hope that it will be useful, but
12   WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14   General Public License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with PulseAudio; if not, write to the Free Software
18   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19   USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <pulsecore/core-util.h>
27 #include <pulsecore/shared.h>
28 #include <pulsecore/dbus-shared.h>
29
30 #include "bluetooth-util.h"
31 #include "ipc.h"
32 #include "a2dp-codecs.h"
33
34 #define HFP_AG_ENDPOINT "/MediaEndpoint/HFPAG"
35 #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
36 #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
37
38 #define ENDPOINT_INTROSPECT_XML                                         \
39     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
40     "<node>"                                                            \
41     " <interface name=\"org.bluez.MediaEndpoint\">"                     \
42     "  <method name=\"SetConfiguration\">"                              \
43     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
44     "   <arg name=\"configuration\" direction=\"in\" type=\"ay\"/>"     \
45     "  </method>"                                                       \
46     "  <method name=\"SelectConfiguration\">"                           \
47     "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
48     "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
49     "  </method>"                                                       \
50     "  <method name=\"ClearConfiguration\">"                            \
51     "  </method>"                                                       \
52     "  <method name=\"Release\">"                                       \
53     "  </method>"                                                       \
54     " </interface>"                                                     \
55     " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
56     "  <method name=\"Introspect\">"                                    \
57     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
58     "  </method>"                                                       \
59     " </interface>"                                                     \
60     "</node>"
61
62 struct pa_bluetooth_discovery {
63     PA_REFCNT_DECLARE;
64
65     pa_core *core;
66     pa_dbus_connection *connection;
67     PA_LLIST_HEAD(pa_dbus_pending, pending);
68     pa_hashmap *devices;
69     pa_hook hook;
70     pa_bool_t filter_added;
71 };
72
73 static void get_properties_reply(DBusPendingCall *pending, void *userdata);
74 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func, void *call_data);
75
76 static pa_bt_audio_state_t pa_bt_audio_state_from_string(const char* value) {
77     pa_assert(value);
78
79     if (pa_streq(value, "disconnected"))
80         return PA_BT_AUDIO_STATE_DISCONNECTED;
81     else if (pa_streq(value, "connecting"))
82         return PA_BT_AUDIO_STATE_CONNECTING;
83     else if (pa_streq(value, "connected"))
84         return PA_BT_AUDIO_STATE_CONNECTED;
85     else if (pa_streq(value, "playing"))
86         return PA_BT_AUDIO_STATE_PLAYING;
87
88     return PA_BT_AUDIO_STATE_INVALID;
89 }
90
91 static pa_bluetooth_uuid *uuid_new(const char *uuid) {
92     pa_bluetooth_uuid *u;
93
94     u = pa_xnew(pa_bluetooth_uuid, 1);
95     u->uuid = pa_xstrdup(uuid);
96     PA_LLIST_INIT(pa_bluetooth_uuid, u);
97
98     return u;
99 }
100
101 static void uuid_free(pa_bluetooth_uuid *u) {
102     pa_assert(u);
103
104     pa_xfree(u->uuid);
105     pa_xfree(u);
106 }
107
108 static pa_bluetooth_device* device_new(const char *path) {
109     pa_bluetooth_device *d;
110
111     d = pa_xnew(pa_bluetooth_device, 1);
112
113     d->dead = FALSE;
114
115     d->device_info_valid = 0;
116
117     d->name = NULL;
118     d->path = pa_xstrdup(path);
119     d->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
120     d->paired = -1;
121     d->alias = NULL;
122     d->device_connected = -1;
123     PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
124     d->address = NULL;
125     d->class = -1;
126     d->trusted = -1;
127
128     d->audio_state = PA_BT_AUDIO_STATE_INVALID;
129     d->audio_sink_state = PA_BT_AUDIO_STATE_INVALID;
130     d->audio_source_state = PA_BT_AUDIO_STATE_INVALID;
131     d->headset_state = PA_BT_AUDIO_STATE_INVALID;
132     d->hfgw_state = PA_BT_AUDIO_STATE_INVALID;
133
134     return d;
135 }
136
137 static void transport_free(pa_bluetooth_transport *t) {
138     pa_assert(t);
139
140     pa_xfree(t->path);
141     pa_xfree(t->config);
142     pa_xfree(t);
143 }
144
145 static void device_free(pa_bluetooth_device *d) {
146     pa_bluetooth_uuid *u;
147     pa_bluetooth_transport *t;
148
149     pa_assert(d);
150
151     while ((t = pa_hashmap_steal_first(d->transports)))
152         transport_free(t);
153
154     pa_hashmap_free(d->transports, NULL, NULL);
155
156     while ((u = d->uuids)) {
157         PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
158         uuid_free(u);
159     }
160
161     pa_xfree(d->name);
162     pa_xfree(d->path);
163     pa_xfree(d->alias);
164     pa_xfree(d->address);
165     pa_xfree(d);
166 }
167
168 static pa_bool_t device_is_audio(pa_bluetooth_device *d) {
169     pa_assert(d);
170
171     return
172         d->device_info_valid && (d->hfgw_state != PA_BT_AUDIO_STATE_INVALID ||
173         (d->audio_state != PA_BT_AUDIO_STATE_INVALID &&
174          (d->audio_sink_state != PA_BT_AUDIO_STATE_INVALID ||
175           d->audio_source_state != PA_BT_AUDIO_STATE_INVALID ||
176           d->headset_state != PA_BT_AUDIO_STATE_INVALID)));
177 }
178
179 static int parse_device_property(pa_bluetooth_discovery *y, pa_bluetooth_device *d, DBusMessageIter *i) {
180     const char *key;
181     DBusMessageIter variant_i;
182
183     pa_assert(y);
184     pa_assert(d);
185     pa_assert(i);
186
187     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
188         pa_log("Property name not a string.");
189         return -1;
190     }
191
192     dbus_message_iter_get_basic(i, &key);
193
194     if (!dbus_message_iter_next(i)) {
195         pa_log("Property value missing");
196         return -1;
197     }
198
199     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
200         pa_log("Property value not a variant.");
201         return -1;
202     }
203
204     dbus_message_iter_recurse(i, &variant_i);
205
206 /*     pa_log_debug("Parsing property org.bluez.Device.%s", key); */
207
208     switch (dbus_message_iter_get_arg_type(&variant_i)) {
209
210         case DBUS_TYPE_STRING: {
211
212             const char *value;
213             dbus_message_iter_get_basic(&variant_i, &value);
214
215             if (pa_streq(key, "Name")) {
216                 pa_xfree(d->name);
217                 d->name = pa_xstrdup(value);
218             } else if (pa_streq(key, "Alias")) {
219                 pa_xfree(d->alias);
220                 d->alias = pa_xstrdup(value);
221             } else if (pa_streq(key, "Address")) {
222                 pa_xfree(d->address);
223                 d->address = pa_xstrdup(value);
224             }
225
226 /*             pa_log_debug("Value %s", value); */
227
228             break;
229         }
230
231         case DBUS_TYPE_BOOLEAN: {
232
233             dbus_bool_t value;
234             dbus_message_iter_get_basic(&variant_i, &value);
235
236             if (pa_streq(key, "Paired"))
237                 d->paired = !!value;
238             else if (pa_streq(key, "Connected"))
239                 d->device_connected = !!value;
240             else if (pa_streq(key, "Trusted"))
241                 d->trusted = !!value;
242
243 /*             pa_log_debug("Value %s", pa_yes_no(value)); */
244
245             break;
246         }
247
248         case DBUS_TYPE_UINT32: {
249
250             uint32_t value;
251             dbus_message_iter_get_basic(&variant_i, &value);
252
253             if (pa_streq(key, "Class"))
254                 d->class = (int) value;
255
256 /*             pa_log_debug("Value %u", (unsigned) value); */
257
258             break;
259         }
260
261         case DBUS_TYPE_ARRAY: {
262
263             DBusMessageIter ai;
264             dbus_message_iter_recurse(&variant_i, &ai);
265
266             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING &&
267                 pa_streq(key, "UUIDs")) {
268
269                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
270                     pa_bluetooth_uuid *node;
271                     const char *value;
272                     DBusMessage *m;
273
274                     dbus_message_iter_get_basic(&ai, &value);
275                     node = uuid_new(value);
276                     PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
277
278                     /* Vudentz said the interfaces are here when the UUIDs are announced */
279                     if (strcasecmp(HSP_AG_UUID, value) == 0 || strcasecmp(HFP_AG_UUID, value) == 0) {
280                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.HandsfreeGateway", "GetProperties"));
281                         send_and_add_to_pending(y, m, get_properties_reply, d);
282                     } else if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
283                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset", "GetProperties"));
284                         send_and_add_to_pending(y, m, get_properties_reply, d);
285                     } else if (strcasecmp(A2DP_SINK_UUID, value) == 0) {
286                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink", "GetProperties"));
287                         send_and_add_to_pending(y, m, get_properties_reply, d);
288                     } else if (strcasecmp(A2DP_SOURCE_UUID, value) == 0) {
289                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSource", "GetProperties"));
290                         send_and_add_to_pending(y, m, get_properties_reply, d);
291                     }
292
293                     /* this might eventually be racy if .Audio is not there yet, but the State change will come anyway later, so this call is for cold-detection mostly */
294                     pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
295                     send_and_add_to_pending(y, m, get_properties_reply, d);
296
297                     if (!dbus_message_iter_next(&ai))
298                         break;
299                 }
300             }
301
302             break;
303         }
304     }
305
306     return 0;
307 }
308
309 static int parse_audio_property(pa_bluetooth_discovery *u, int *state, DBusMessageIter *i) {
310     const char *key;
311     DBusMessageIter variant_i;
312
313     pa_assert(u);
314     pa_assert(state);
315     pa_assert(i);
316
317     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
318         pa_log("Property name not a string.");
319         return -1;
320     }
321
322     dbus_message_iter_get_basic(i, &key);
323
324     if (!dbus_message_iter_next(i)) {
325         pa_log("Property value missing");
326         return -1;
327     }
328
329     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
330         pa_log("Property value not a variant.");
331         return -1;
332     }
333
334     dbus_message_iter_recurse(i, &variant_i);
335
336 /*     pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */
337
338     switch (dbus_message_iter_get_arg_type(&variant_i)) {
339
340         case DBUS_TYPE_STRING: {
341
342             const char *value;
343             dbus_message_iter_get_basic(&variant_i, &value);
344
345             if (pa_streq(key, "State")) {
346                 *state = pa_bt_audio_state_from_string(value);
347                 pa_log_debug("dbus: property 'State' changed to value '%s'", value);
348             }
349
350             break;
351         }
352     }
353
354     return 0;
355 }
356
357 static void run_callback(pa_bluetooth_discovery *y, pa_bluetooth_device *d, pa_bool_t dead) {
358     pa_assert(y);
359     pa_assert(d);
360
361     if (!device_is_audio(d))
362         return;
363
364     d->dead = dead;
365     pa_hook_fire(&y->hook, d);
366 }
367
368 static void remove_all_devices(pa_bluetooth_discovery *y) {
369     pa_bluetooth_device *d;
370
371     pa_assert(y);
372
373     while ((d = pa_hashmap_steal_first(y->devices))) {
374         run_callback(y, d, TRUE);
375         device_free(d);
376     }
377 }
378
379 static pa_bluetooth_device *found_device(pa_bluetooth_discovery *y, const char* path) {
380     DBusMessage *m;
381     pa_bluetooth_device *d;
382
383     pa_assert(y);
384     pa_assert(path);
385
386     d = pa_hashmap_get(y->devices, path);
387     if (d)
388         return d;
389
390     d = device_new(path);
391
392     pa_hashmap_put(y->devices, d->path, d);
393
394     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
395     send_and_add_to_pending(y, m, get_properties_reply, d);
396
397     /* Before we read the other properties (Audio, AudioSink, AudioSource,
398      * Headset) we wait that the UUID is read */
399     return d;
400 }
401
402 static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
403     DBusMessage *r;
404     DBusMessageIter arg_i, element_i;
405     pa_dbus_pending *p;
406     pa_bluetooth_device *d;
407     pa_bluetooth_discovery *y;
408     int valid;
409
410     pa_assert_se(p = userdata);
411     pa_assert_se(y = p->context_data);
412     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
413
414 /*     pa_log_debug("Got %s.GetProperties response for %s", */
415 /*                  dbus_message_get_interface(p->message), */
416 /*                  dbus_message_get_path(p->message)); */
417
418     /* We don't use p->call_data here right-away since the device
419      * might already be invalidated at this point */
420
421     if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(p->message))))
422         return;
423
424     pa_assert(p->call_data == d);
425
426     valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
427
428     if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
429         d->device_info_valid = valid;
430
431     if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
432         pa_log_debug("Bluetooth daemon is apparently not available.");
433         remove_all_devices(y);
434         goto finish2;
435     }
436
437     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
438
439         if (!dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD))
440             pa_log("Error from GetProperties reply: %s", dbus_message_get_error_name(r));
441
442         goto finish;
443     }
444
445     if (!dbus_message_iter_init(r, &arg_i)) {
446         pa_log("GetProperties reply has no arguments.");
447         goto finish;
448     }
449
450     if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
451         pa_log("GetProperties argument is not an array.");
452         goto finish;
453     }
454
455     dbus_message_iter_recurse(&arg_i, &element_i);
456     while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
457
458         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
459             DBusMessageIter dict_i;
460
461             dbus_message_iter_recurse(&element_i, &dict_i);
462
463             if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
464                 if (parse_device_property(y, d, &dict_i) < 0)
465                     goto finish;
466
467             } else if (dbus_message_has_interface(p->message, "org.bluez.Audio")) {
468                 if (parse_audio_property(y, &d->audio_state, &dict_i) < 0)
469                     goto finish;
470
471             } else if (dbus_message_has_interface(p->message, "org.bluez.Headset")) {
472                 if (parse_audio_property(y, &d->headset_state, &dict_i) < 0)
473                     goto finish;
474
475             }  else if (dbus_message_has_interface(p->message, "org.bluez.AudioSink")) {
476                 if (parse_audio_property(y, &d->audio_sink_state, &dict_i) < 0)
477                     goto finish;
478
479             }  else if (dbus_message_has_interface(p->message, "org.bluez.AudioSource")) {
480                 if (parse_audio_property(y, &d->audio_source_state, &dict_i) < 0)
481                     goto finish;
482
483             }  else if (dbus_message_has_interface(p->message, "org.bluez.HandsfreeGateway")) {
484                 if (parse_audio_property(y, &d->hfgw_state, &arg_i) < 0)
485                     goto finish;
486
487             }
488         }
489
490         if (!dbus_message_iter_next(&element_i))
491             break;
492     }
493
494 finish:
495     run_callback(y, d, FALSE);
496
497 finish2:
498     dbus_message_unref(r);
499
500     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
501     pa_dbus_pending_free(p);
502 }
503
504 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func, void *call_data) {
505     pa_dbus_pending *p;
506     DBusPendingCall *call;
507
508     pa_assert(y);
509     pa_assert(m);
510
511     pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
512
513     p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
514     PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
515     dbus_pending_call_set_notify(call, func, p, NULL);
516
517     return p;
518 }
519
520 #ifdef DBUS_TYPE_UNIX_FD
521 static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
522     DBusError e;
523     DBusMessage *r;
524     pa_dbus_pending *p;
525     pa_bluetooth_discovery *y;
526     char *endpoint;
527
528     pa_assert(pending);
529
530     dbus_error_init(&e);
531
532     pa_assert_se(p = userdata);
533     pa_assert_se(y = p->context_data);
534     pa_assert_se(endpoint = p->call_data);
535     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
536
537     if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
538         pa_log_debug("Bluetooth daemon is apparently not available.");
539         remove_all_devices(y);
540         goto finish;
541     }
542
543     if (dbus_message_is_error(r, PA_BLUETOOTH_ERROR_NOT_SUPPORTED)) {
544         pa_log_info("Couldn't register endpoint %s, because BlueZ is configured to disable the endpoint type.", endpoint);
545         goto finish;
546     }
547
548     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
549         pa_log("Error from RegisterEndpoint reply: %s", dbus_message_get_error_name(r));
550         goto finish;
551     }
552
553 finish:
554     dbus_message_unref(r);
555
556     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
557     pa_dbus_pending_free(p);
558
559     pa_xfree(endpoint);
560 }
561 #endif
562
563 static void list_devices_reply(DBusPendingCall *pending, void *userdata) {
564     DBusError e;
565     DBusMessage *r;
566     char **paths = NULL;
567     int num = -1;
568     pa_dbus_pending *p;
569     pa_bluetooth_discovery *y;
570
571     pa_assert(pending);
572
573     dbus_error_init(&e);
574
575     pa_assert_se(p = userdata);
576     pa_assert_se(y = p->context_data);
577     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
578
579     if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
580         pa_log_debug("Bluetooth daemon is apparently not available.");
581         remove_all_devices(y);
582         goto finish;
583     }
584
585     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
586         pa_log("Error from ListDevices reply: %s", dbus_message_get_error_name(r));
587         goto finish;
588     }
589
590     if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
591         pa_log("org.bluez.Adapter.ListDevices returned an error: '%s'\n", e.message);
592         dbus_error_free(&e);
593     } else {
594         int i;
595
596         for (i = 0; i < num; ++i)
597             found_device(y, paths[i]);
598     }
599
600 finish:
601     if (paths)
602         dbus_free_string_array(paths);
603
604     dbus_message_unref(r);
605
606     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
607     pa_dbus_pending_free(p);
608 }
609
610 #ifdef DBUS_TYPE_UNIX_FD
611 static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
612     DBusMessage *m;
613     DBusMessageIter i, d;
614     uint8_t codec = 0;
615
616     pa_log_debug("Registering %s on adapter %s.", endpoint, path);
617
618     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Media", "RegisterEndpoint"));
619
620     dbus_message_iter_init_append(m, &i);
621
622     dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint);
623
624     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
625                                     DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
626                                     &d);
627
628     pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
629
630     pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
631
632     if (pa_streq(uuid, HFP_AG_UUID)) {
633         uint8_t capability = 0;
634         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capability, 1);
635     } else {
636         a2dp_sbc_t capabilities;
637
638         capabilities.channel_mode = BT_A2DP_CHANNEL_MODE_MONO | BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL |
639                                     BT_A2DP_CHANNEL_MODE_STEREO | BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
640         capabilities.frequency = BT_SBC_SAMPLING_FREQ_16000 | BT_SBC_SAMPLING_FREQ_32000 |
641                                  BT_SBC_SAMPLING_FREQ_44100 | BT_SBC_SAMPLING_FREQ_48000;
642         capabilities.allocation_method = BT_A2DP_ALLOCATION_SNR | BT_A2DP_ALLOCATION_LOUDNESS;
643         capabilities.subbands = BT_A2DP_SUBBANDS_4 | BT_A2DP_SUBBANDS_8;
644         capabilities.block_length = BT_A2DP_BLOCK_LENGTH_4 | BT_A2DP_BLOCK_LENGTH_8 |
645                                     BT_A2DP_BLOCK_LENGTH_12 | BT_A2DP_BLOCK_LENGTH_16;
646         capabilities.min_bitpool = MIN_BITPOOL;
647         capabilities.max_bitpool = MAX_BITPOOL;
648
649         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
650     }
651
652     dbus_message_iter_close_container(&i, &d);
653
654     send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
655 }
656 #endif
657
658 static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
659     DBusMessage *m;
660
661     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "ListDevices"));
662     send_and_add_to_pending(y, m, list_devices_reply, NULL);
663
664 #ifdef DBUS_TYPE_UNIX_FD
665     register_endpoint(y, path, HFP_AG_ENDPOINT, HFP_AG_UUID);
666     register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, A2DP_SOURCE_UUID);
667     register_endpoint(y, path, A2DP_SINK_ENDPOINT, A2DP_SINK_UUID);
668 #endif
669 }
670
671 static void list_adapters_reply(DBusPendingCall *pending, void *userdata) {
672     DBusError e;
673     DBusMessage *r;
674     char **paths = NULL;
675     int num = -1;
676     pa_dbus_pending *p;
677     pa_bluetooth_discovery *y;
678
679     pa_assert(pending);
680
681     dbus_error_init(&e);
682
683     pa_assert_se(p = userdata);
684     pa_assert_se(y = p->context_data);
685     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
686
687     if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
688         pa_log_debug("Bluetooth daemon is apparently not available.");
689         remove_all_devices(y);
690         goto finish;
691     }
692
693     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
694         pa_log("Error from ListAdapters reply: %s", dbus_message_get_error_name(r));
695         goto finish;
696     }
697
698     if (!dbus_message_get_args(r, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH, &paths, &num, DBUS_TYPE_INVALID)) {
699         pa_log("org.bluez.Manager.ListAdapters returned an error: %s", e.message);
700         dbus_error_free(&e);
701     } else {
702         int i;
703
704         for (i = 0; i < num; ++i)
705             found_adapter(y, paths[i]);
706     }
707
708 finish:
709     if (paths)
710         dbus_free_string_array(paths);
711
712     dbus_message_unref(r);
713
714     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
715     pa_dbus_pending_free(p);
716 }
717
718 static void list_adapters(pa_bluetooth_discovery *y) {
719     DBusMessage *m;
720     pa_assert(y);
721
722     pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "ListAdapters"));
723     send_and_add_to_pending(y, m, list_adapters_reply, NULL);
724 }
725
726 int pa_bluetooth_transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i)
727 {
728     const char *key;
729     DBusMessageIter variant_i;
730
731     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
732         pa_log("Property name not a string.");
733         return -1;
734     }
735
736     dbus_message_iter_get_basic(i, &key);
737
738     if (!dbus_message_iter_next(i))  {
739         pa_log("Property value missing");
740         return -1;
741     }
742
743     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
744         pa_log("Property value not a variant.");
745         return -1;
746     }
747
748     dbus_message_iter_recurse(i, &variant_i);
749
750     switch (dbus_message_iter_get_arg_type(&variant_i)) {
751
752         case DBUS_TYPE_BOOLEAN: {
753
754             pa_bool_t *value;
755             dbus_message_iter_get_basic(&variant_i, &value);
756
757             if (pa_streq(key, "NREC"))
758                 t->nrec = value;
759
760             break;
761          }
762     }
763
764     return 0;
765 }
766
767 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
768     DBusError err;
769     pa_bluetooth_discovery *y;
770
771     pa_assert(bus);
772     pa_assert(m);
773
774     pa_assert_se(y = userdata);
775
776     dbus_error_init(&err);
777
778     pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
779             dbus_message_get_interface(m),
780             dbus_message_get_path(m),
781             dbus_message_get_member(m));
782
783     if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
784         const char *path;
785         pa_bluetooth_device *d;
786
787         if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
788             pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
789             goto fail;
790         }
791
792         pa_log_debug("Device %s removed", path);
793
794         if ((d = pa_hashmap_remove(y->devices, path))) {
795             run_callback(y, d, TRUE);
796             device_free(d);
797         }
798
799         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
800
801     } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
802         const char *path;
803
804         if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
805             pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
806             goto fail;
807         }
808
809         pa_log_debug("Device %s created", path);
810
811         found_device(y, path);
812         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
813
814     } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
815         const char *path;
816
817         if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
818             pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
819             goto fail;
820         }
821
822         pa_log_debug("Adapter %s created", path);
823
824         found_adapter(y, path);
825         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
826
827     } else if (dbus_message_is_signal(m, "org.bluez.Audio", "PropertyChanged") ||
828                dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
829                dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
830                dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged") ||
831                dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged") ||
832                dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
833
834         pa_bluetooth_device *d;
835
836         if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
837             DBusMessageIter arg_i;
838
839             if (!dbus_message_iter_init(m, &arg_i)) {
840                 pa_log("Failed to parse PropertyChanged: %s", err.message);
841                 goto fail;
842             }
843
844             if (dbus_message_has_interface(m, "org.bluez.Device")) {
845                 if (parse_device_property(y, d, &arg_i) < 0)
846                     goto fail;
847
848             } else if (dbus_message_has_interface(m, "org.bluez.Audio")) {
849                 if (parse_audio_property(y, &d->audio_state, &arg_i) < 0)
850                     goto fail;
851
852             } else if (dbus_message_has_interface(m, "org.bluez.Headset")) {
853                 if (parse_audio_property(y, &d->headset_state, &arg_i) < 0)
854                     goto fail;
855
856             }  else if (dbus_message_has_interface(m, "org.bluez.AudioSink")) {
857                 if (parse_audio_property(y, &d->audio_sink_state, &arg_i) < 0)
858                     goto fail;
859
860             }  else if (dbus_message_has_interface(m, "org.bluez.AudioSource")) {
861                 if (parse_audio_property(y, &d->audio_source_state, &arg_i) < 0)
862                     goto fail;
863
864             }  else if (dbus_message_has_interface(m, "org.bluez.HandsfreeGateway")) {
865                 if (parse_audio_property(y, &d->hfgw_state, &arg_i) < 0)
866                     goto fail;
867             }
868
869             run_callback(y, d, FALSE);
870         }
871
872         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
873
874     } else if (dbus_message_is_signal(m, "org.bluez.Device", "DisconnectRequested")) {
875         pa_bluetooth_device *d;
876
877         if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
878             /* Device will disconnect in 2 sec */
879             d->audio_state = PA_BT_AUDIO_STATE_DISCONNECTED;
880             d->audio_sink_state = PA_BT_AUDIO_STATE_DISCONNECTED;
881             d->audio_source_state = PA_BT_AUDIO_STATE_DISCONNECTED;
882             d->headset_state = PA_BT_AUDIO_STATE_DISCONNECTED;
883             d->hfgw_state = PA_BT_AUDIO_STATE_DISCONNECTED;
884
885             run_callback(y, d, FALSE);
886         }
887
888         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
889
890     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
891         const char *name, *old_owner, *new_owner;
892
893         if (!dbus_message_get_args(m, &err,
894                                    DBUS_TYPE_STRING, &name,
895                                    DBUS_TYPE_STRING, &old_owner,
896                                    DBUS_TYPE_STRING, &new_owner,
897                                    DBUS_TYPE_INVALID)) {
898             pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
899             goto fail;
900         }
901
902         if (pa_streq(name, "org.bluez")) {
903             if (old_owner && *old_owner) {
904                 pa_log_debug("Bluetooth daemon disappeared.");
905                 remove_all_devices(y);
906             }
907
908             if (new_owner && *new_owner) {
909                 pa_log_debug("Bluetooth daemon appeared.");
910                 list_adapters(y);
911             }
912         }
913
914         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
915     } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) {
916         pa_bluetooth_device *d;
917         pa_bluetooth_transport *t;
918         void *state = NULL;
919         DBusMessageIter arg_i;
920
921         while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
922             if ((t = pa_hashmap_get(d->transports, dbus_message_get_path(m))))
923                 break;
924
925         if (!t)
926             goto fail;
927
928         if (!dbus_message_iter_init(m, &arg_i)) {
929             pa_log("Failed to parse PropertyChanged: %s", err.message);
930             goto fail;
931         }
932
933         if (pa_bluetooth_transport_parse_property(t, &arg_i) < 0)
934             goto fail;
935
936         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
937     }
938
939 fail:
940     dbus_error_free(&err);
941
942     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
943 }
944
945 const pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *y, const char* address) {
946     pa_bluetooth_device *d;
947     void *state = NULL;
948
949     pa_assert(y);
950     pa_assert(PA_REFCNT_VALUE(y) > 0);
951     pa_assert(address);
952
953     if (!pa_hook_is_firing(&y->hook))
954         pa_bluetooth_discovery_sync(y);
955
956     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
957         if (pa_streq(d->address, address))
958             return device_is_audio(d) ? d : NULL;
959
960     return NULL;
961 }
962
963 const pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *y, const char* path) {
964     pa_bluetooth_device *d;
965
966     pa_assert(y);
967     pa_assert(PA_REFCNT_VALUE(y) > 0);
968     pa_assert(path);
969
970     if (!pa_hook_is_firing(&y->hook))
971         pa_bluetooth_discovery_sync(y);
972
973     if ((d = pa_hashmap_get(y->devices, path)))
974         if (device_is_audio(d))
975             return d;
976
977     return NULL;
978 }
979
980 const pa_bluetooth_transport* pa_bluetooth_discovery_get_transport(pa_bluetooth_discovery *y, const char *path) {
981     pa_bluetooth_device *d;
982     pa_bluetooth_transport *t;
983     void *state = NULL;
984
985     pa_assert(y);
986     pa_assert(PA_REFCNT_VALUE(y) > 0);
987     pa_assert(path);
988
989     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
990         if ((t = pa_hashmap_get(d->transports, path)))
991             return t;
992
993     return NULL;
994 }
995
996 const pa_bluetooth_transport* pa_bluetooth_device_get_transport(const pa_bluetooth_device *d, enum profile profile) {
997     pa_bluetooth_transport *t;
998     void *state = NULL;
999
1000     pa_assert(d);
1001
1002     while ((t = pa_hashmap_iterate(d->transports, &state, NULL)))
1003         if (t->profile == profile)
1004             return t;
1005
1006     return NULL;
1007 }
1008
1009 int pa_bluetooth_transport_acquire(const pa_bluetooth_transport *t, const char *accesstype, size_t *imtu, size_t *omtu) {
1010     DBusMessage *m, *r;
1011     DBusError err;
1012     int ret;
1013     uint16_t i, o;
1014
1015     pa_assert(t);
1016     pa_assert(t->y);
1017
1018     dbus_error_init(&err);
1019
1020     pa_assert_se(m = dbus_message_new_method_call("org.bluez", t->path, "org.bluez.MediaTransport", "Acquire"));
1021     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
1022     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
1023
1024     if (dbus_error_is_set(&err) || !r) {
1025         pa_log("Failed to acquire transport fd: %s", err.message);
1026         dbus_error_free(&err);
1027         return -1;
1028     }
1029
1030 #ifdef DBUS_TYPE_UNIX_FD
1031     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o, DBUS_TYPE_INVALID)) {
1032         pa_log("Failed to parse org.bluez.MediaTransport.Acquire(): %s", err.message);
1033         ret = -1;
1034         dbus_error_free(&err);
1035         goto fail;
1036     }
1037 #endif
1038
1039     if (imtu)
1040         *imtu = i;
1041
1042     if (omtu)
1043         *omtu = o;
1044
1045 #ifdef DBUS_TYPE_UNIX_FD
1046 fail:
1047 #endif
1048     dbus_message_unref(r);
1049     return ret;
1050 }
1051
1052 void pa_bluetooth_transport_release(const pa_bluetooth_transport *t, const char *accesstype) {
1053     DBusMessage *m;
1054     DBusError err;
1055
1056     pa_assert(t);
1057     pa_assert(t->y);
1058
1059     dbus_error_init(&err);
1060
1061     pa_assert_se(m = dbus_message_new_method_call("org.bluez", t->path, "org.bluez.MediaTransport", "Release"));
1062     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
1063     dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->y->connection), m, -1, &err);
1064
1065     if (dbus_error_is_set(&err)) {
1066         pa_log("Failed to release transport %s: %s", t->path, err.message);
1067         dbus_error_free(&err);
1068     } else
1069         pa_log_info("Transport %s released", t->path);
1070 }
1071
1072 static int setup_dbus(pa_bluetooth_discovery *y) {
1073     DBusError err;
1074
1075     dbus_error_init(&err);
1076
1077     y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err);
1078
1079     if (dbus_error_is_set(&err) || !y->connection) {
1080         pa_log("Failed to get D-Bus connection: %s", err.message);
1081         dbus_error_free(&err);
1082         return -1;
1083     }
1084
1085     return 0;
1086 }
1087
1088 #ifdef DBUS_TYPE_UNIX_FD
1089 static pa_bluetooth_transport *transport_new(pa_bluetooth_discovery *y, const char *path, enum profile p, const uint8_t *config, int size) {
1090     pa_bluetooth_transport *t;
1091
1092     t = pa_xnew0(pa_bluetooth_transport, 1);
1093     t->y = y;
1094     t->path = pa_xstrdup(path);
1095     t->profile = p;
1096     t->config_size = size;
1097
1098     if (size > 0) {
1099         t->config = pa_xnew(uint8_t, size);
1100         memcpy(t->config, config, size);
1101     }
1102
1103     return t;
1104 }
1105
1106 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1107     pa_bluetooth_discovery *y = userdata;
1108     pa_bluetooth_device *d;
1109     pa_bluetooth_transport *t;
1110     const char *path, *dev_path = NULL, *uuid = NULL;
1111     uint8_t *config = NULL;
1112     int size = 0;
1113     pa_bool_t nrec;
1114     enum profile p;
1115     DBusMessageIter args, props;
1116     DBusMessage *r;
1117
1118     dbus_message_iter_init(m, &args);
1119
1120     dbus_message_iter_get_basic(&args, &path);
1121     if (!dbus_message_iter_next(&args))
1122         goto fail;
1123
1124     dbus_message_iter_recurse(&args, &props);
1125     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
1126         goto fail;
1127
1128     /* Read transport properties */
1129     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
1130         const char *key;
1131         DBusMessageIter value, entry;
1132         int var;
1133
1134         dbus_message_iter_recurse(&props, &entry);
1135         dbus_message_iter_get_basic(&entry, &key);
1136
1137         dbus_message_iter_next(&entry);
1138         dbus_message_iter_recurse(&entry, &value);
1139
1140         var = dbus_message_iter_get_arg_type(&value);
1141         if (strcasecmp(key, "UUID") == 0) {
1142             if (var != DBUS_TYPE_STRING)
1143                 goto fail;
1144             dbus_message_iter_get_basic(&value, &uuid);
1145         } else if (strcasecmp(key, "Device") == 0) {
1146             if (var != DBUS_TYPE_OBJECT_PATH)
1147                 goto fail;
1148             dbus_message_iter_get_basic(&value, &dev_path);
1149         } else if (strcasecmp(key, "NREC") == 0) {
1150             if (var != DBUS_TYPE_BOOLEAN)
1151                 goto fail;
1152             dbus_message_iter_get_basic(&value, &nrec);
1153         } else if (strcasecmp(key, "Configuration") == 0) {
1154             DBusMessageIter array;
1155             if (var != DBUS_TYPE_ARRAY)
1156                 goto fail;
1157             dbus_message_iter_recurse(&value, &array);
1158             dbus_message_iter_get_fixed_array(&array, &config, &size);
1159         }
1160
1161         dbus_message_iter_next(&props);
1162     }
1163
1164     d = found_device(y, dev_path);
1165     if (!d)
1166         goto fail;
1167
1168     if (dbus_message_has_path(m, HFP_AG_ENDPOINT))
1169         p = PROFILE_HSP;
1170     else if (dbus_message_has_path(m, A2DP_SOURCE_ENDPOINT))
1171         p = PROFILE_A2DP;
1172     else
1173         p = PROFILE_A2DP_SOURCE;
1174
1175     t = transport_new(y, path, p, config, size);
1176     if (nrec)
1177         t->nrec = nrec;
1178     pa_hashmap_put(d->transports, t->path, t);
1179
1180     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
1181
1182     pa_assert_se(r = dbus_message_new_method_return(m));
1183
1184     return r;
1185
1186 fail:
1187     pa_log("org.bluez.MediaEndpoint.SetConfiguration: invalid arguments");
1188     pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
1189                                                         "Unable to set configuration")));
1190     return r;
1191 }
1192
1193 static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
1194     pa_bluetooth_discovery *y = userdata;
1195     pa_bluetooth_device *d;
1196     pa_bluetooth_transport *t;
1197     void *state = NULL;
1198     DBusMessage *r;
1199     DBusError e;
1200     const char *path;
1201
1202     dbus_error_init(&e);
1203
1204     if (!dbus_message_get_args(m, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1205         pa_log("org.bluez.MediaEndpoint.ClearConfiguration: %s", e.message);
1206         dbus_error_free(&e);
1207         goto fail;
1208     }
1209
1210     while ((d = pa_hashmap_iterate(y->devices, &state, NULL))) {
1211         if ((t = pa_hashmap_get(d->transports, path))) {
1212             pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
1213             pa_hashmap_remove(d->transports, t->path);
1214             transport_free(t);
1215             break;
1216         }
1217     }
1218
1219     pa_assert_se(r = dbus_message_new_method_return(m));
1220
1221     return r;
1222
1223 fail:
1224     pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
1225                                                         "Unable to clear configuration")));
1226     return r;
1227 }
1228
1229 static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
1230
1231     switch (freq) {
1232         case BT_SBC_SAMPLING_FREQ_16000:
1233         case BT_SBC_SAMPLING_FREQ_32000:
1234             return 53;
1235
1236         case BT_SBC_SAMPLING_FREQ_44100:
1237
1238             switch (mode) {
1239                 case BT_A2DP_CHANNEL_MODE_MONO:
1240                 case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
1241                     return 31;
1242
1243                 case BT_A2DP_CHANNEL_MODE_STEREO:
1244                 case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
1245                     return 53;
1246
1247                 default:
1248                     pa_log_warn("Invalid channel mode %u", mode);
1249                     return 53;
1250             }
1251
1252         case BT_SBC_SAMPLING_FREQ_48000:
1253
1254             switch (mode) {
1255                 case BT_A2DP_CHANNEL_MODE_MONO:
1256                 case BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL:
1257                     return 29;
1258
1259                 case BT_A2DP_CHANNEL_MODE_STEREO:
1260                 case BT_A2DP_CHANNEL_MODE_JOINT_STEREO:
1261                     return 51;
1262
1263                 default:
1264                     pa_log_warn("Invalid channel mode %u", mode);
1265                     return 51;
1266             }
1267
1268         default:
1269             pa_log_warn("Invalid sampling freq %u", freq);
1270             return 53;
1271     }
1272 }
1273
1274 static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
1275     pa_bluetooth_discovery *y = userdata;
1276     a2dp_sbc_t *cap, config;
1277     uint8_t *pconf = (uint8_t *) &config;
1278     int i, size;
1279     DBusMessage *r;
1280     DBusError e;
1281
1282     static const struct {
1283         uint32_t rate;
1284         uint8_t cap;
1285     } freq_table[] = {
1286         { 16000U, BT_SBC_SAMPLING_FREQ_16000 },
1287         { 32000U, BT_SBC_SAMPLING_FREQ_32000 },
1288         { 44100U, BT_SBC_SAMPLING_FREQ_44100 },
1289         { 48000U, BT_SBC_SAMPLING_FREQ_48000 }
1290     };
1291
1292     dbus_error_init(&e);
1293
1294     if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
1295         pa_log("org.bluez.MediaEndpoint.SelectConfiguration: %s", e.message);
1296         dbus_error_free(&e);
1297         goto fail;
1298     }
1299
1300     if (dbus_message_has_path(m, HFP_AG_ENDPOINT))
1301         goto done;
1302
1303     pa_assert(size == sizeof(config));
1304
1305     memset(&config, 0, sizeof(config));
1306
1307     /* Find the lowest freq that is at least as high as the requested
1308      * sampling rate */
1309     for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
1310         if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
1311             config.frequency = freq_table[i].cap;
1312             break;
1313         }
1314
1315     if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
1316         for (--i; i >= 0; i--) {
1317             if (cap->frequency & freq_table[i].cap) {
1318                 config.frequency = freq_table[i].cap;
1319                 break;
1320             }
1321         }
1322
1323         if (i < 0) {
1324             pa_log("Not suitable sample rate");
1325             goto fail;
1326         }
1327     }
1328
1329     pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
1330
1331     if (y->core->default_sample_spec.channels <= 1) {
1332         if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO)
1333             config.channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
1334     }
1335
1336     if (y->core->default_sample_spec.channels >= 2) {
1337         if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_JOINT_STEREO)
1338             config.channel_mode = BT_A2DP_CHANNEL_MODE_JOINT_STEREO;
1339         else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_STEREO)
1340             config.channel_mode = BT_A2DP_CHANNEL_MODE_STEREO;
1341         else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL)
1342             config.channel_mode = BT_A2DP_CHANNEL_MODE_DUAL_CHANNEL;
1343         else if (cap->channel_mode & BT_A2DP_CHANNEL_MODE_MONO) {
1344             config.channel_mode = BT_A2DP_CHANNEL_MODE_MONO;
1345         } else {
1346             pa_log("No supported channel modes");
1347             goto fail;
1348         }
1349     }
1350
1351     if (cap->block_length & BT_A2DP_BLOCK_LENGTH_16)
1352         config.block_length = BT_A2DP_BLOCK_LENGTH_16;
1353     else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_12)
1354         config.block_length = BT_A2DP_BLOCK_LENGTH_12;
1355     else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_8)
1356         config.block_length = BT_A2DP_BLOCK_LENGTH_8;
1357     else if (cap->block_length & BT_A2DP_BLOCK_LENGTH_4)
1358         config.block_length = BT_A2DP_BLOCK_LENGTH_4;
1359     else {
1360         pa_log_error("No supported block lengths");
1361         goto fail;
1362     }
1363
1364     if (cap->subbands & BT_A2DP_SUBBANDS_8)
1365         config.subbands = BT_A2DP_SUBBANDS_8;
1366     else if (cap->subbands & BT_A2DP_SUBBANDS_4)
1367         config.subbands = BT_A2DP_SUBBANDS_4;
1368     else {
1369         pa_log_error("No supported subbands");
1370         goto fail;
1371     }
1372
1373     if (cap->allocation_method & BT_A2DP_ALLOCATION_LOUDNESS)
1374         config.allocation_method = BT_A2DP_ALLOCATION_LOUDNESS;
1375     else if (cap->allocation_method & BT_A2DP_ALLOCATION_SNR)
1376         config.allocation_method = BT_A2DP_ALLOCATION_SNR;
1377
1378     config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
1379     config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
1380
1381 done:
1382     pa_assert_se(r = dbus_message_new_method_return(m));
1383
1384     pa_assert_se(dbus_message_append_args(
1385                                      r,
1386                                      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size,
1387                                      DBUS_TYPE_INVALID));
1388
1389     return r;
1390
1391 fail:
1392     pa_assert_se(r = (dbus_message_new_error(m, "org.bluez.MediaEndpoint.Error.InvalidArguments",
1393                                                         "Unable to select configuration")));
1394     return r;
1395 }
1396
1397 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1398     struct pa_bluetooth_discovery *y = userdata;
1399     DBusMessage *r = NULL;
1400     DBusError e;
1401     const char *path;
1402
1403     pa_assert(y);
1404
1405     pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
1406             dbus_message_get_interface(m),
1407             dbus_message_get_path(m),
1408             dbus_message_get_member(m));
1409
1410     path = dbus_message_get_path(m);
1411     dbus_error_init(&e);
1412
1413     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, HFP_AG_ENDPOINT))
1414         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1415
1416     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1417         const char *xml = ENDPOINT_INTROSPECT_XML;
1418
1419         pa_assert_se(r = dbus_message_new_method_return(m));
1420         pa_assert_se(dbus_message_append_args(
1421                                  r,
1422                                  DBUS_TYPE_STRING, &xml,
1423                                  DBUS_TYPE_INVALID));
1424
1425     } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SetConfiguration")) {
1426         r = endpoint_set_configuration(c, m, userdata);
1427     } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "SelectConfiguration")) {
1428         r = endpoint_select_configuration(c, m, userdata);
1429     } else if (dbus_message_is_method_call(m, "org.bluez.MediaEndpoint", "ClearConfiguration"))
1430         r = endpoint_clear_configuration(c, m, userdata);
1431     else
1432         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1433
1434     if (r) {
1435         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1436         dbus_message_unref(r);
1437     }
1438
1439     return DBUS_HANDLER_RESULT_HANDLED;
1440 }
1441 #endif /* DBUS_TYPE_UNIX_FD */
1442
1443 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
1444     DBusError err;
1445     pa_bluetooth_discovery *y;
1446 #ifdef DBUS_TYPE_UNIX_FD
1447     static const DBusObjectPathVTable vtable_endpoint = {
1448         .message_function = endpoint_handler,
1449     };
1450 #endif
1451
1452     pa_assert(c);
1453
1454     dbus_error_init(&err);
1455
1456     if ((y = pa_shared_get(c, "bluetooth-discovery")))
1457         return pa_bluetooth_discovery_ref(y);
1458
1459     y = pa_xnew0(pa_bluetooth_discovery, 1);
1460     PA_REFCNT_INIT(y);
1461     y->core = c;
1462     y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1463     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
1464     pa_hook_init(&y->hook, y);
1465     pa_shared_set(c, "bluetooth-discovery", y);
1466
1467     if (setup_dbus(y) < 0)
1468         goto fail;
1469
1470     /* dynamic detection of bluetooth audio devices */
1471     if (!dbus_connection_add_filter(pa_dbus_connection_get(y->connection), filter_cb, y, NULL)) {
1472         pa_log_error("Failed to add filter function");
1473         goto fail;
1474     }
1475     y->filter_added = TRUE;
1476
1477     if (pa_dbus_add_matches(
1478                 pa_dbus_connection_get(y->connection), &err,
1479                 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'",
1480                 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
1481                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
1482                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
1483                 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
1484                 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='DisconnectRequested'",
1485                 "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
1486                 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
1487                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
1488                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
1489                 "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
1490                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
1491                 NULL) < 0) {
1492         pa_log("Failed to add D-Bus matches: %s", err.message);
1493         goto fail;
1494     }
1495
1496 #ifdef DBUS_TYPE_UNIX_FD
1497     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT, &vtable_endpoint, y));
1498     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT, &vtable_endpoint, y));
1499     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT, &vtable_endpoint, y));
1500 #endif
1501
1502     list_adapters(y);
1503
1504     return y;
1505
1506 fail:
1507
1508     if (y)
1509         pa_bluetooth_discovery_unref(y);
1510
1511     dbus_error_free(&err);
1512
1513     return NULL;
1514 }
1515
1516 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
1517     pa_assert(y);
1518     pa_assert(PA_REFCNT_VALUE(y) > 0);
1519
1520     PA_REFCNT_INC(y);
1521
1522     return y;
1523 }
1524
1525 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
1526     pa_assert(y);
1527     pa_assert(PA_REFCNT_VALUE(y) > 0);
1528
1529     if (PA_REFCNT_DEC(y) > 0)
1530         return;
1531
1532     pa_dbus_free_pending_list(&y->pending);
1533
1534     if (y->devices) {
1535         remove_all_devices(y);
1536         pa_hashmap_free(y->devices, NULL, NULL);
1537     }
1538
1539     if (y->connection) {
1540 #ifdef DBUS_TYPE_UNIX_FD
1541         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT);
1542         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
1543         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
1544 #endif
1545         pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
1546                                "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',arg0='org.bluez'",
1547                                "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
1548                                "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
1549                                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
1550                                "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
1551                                "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
1552                                "type='signal',sender='org.bluez',interface='org.bluez.Device',member='DisconnectRequested'",
1553                                "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
1554                                "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
1555                                "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
1556                                "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
1557                                "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
1558                                "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
1559                                NULL);
1560
1561         if (y->filter_added)
1562             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
1563
1564         pa_dbus_connection_unref(y->connection);
1565     }
1566
1567     pa_hook_done(&y->hook);
1568
1569     if (y->core)
1570         pa_shared_remove(y->core, "bluetooth-discovery");
1571
1572     pa_xfree(y);
1573 }
1574
1575 void pa_bluetooth_discovery_sync(pa_bluetooth_discovery *y) {
1576     pa_assert(y);
1577     pa_assert(PA_REFCNT_VALUE(y) > 0);
1578
1579     pa_dbus_sync_pending_list(&y->pending);
1580 }
1581
1582 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y) {
1583     pa_assert(y);
1584     pa_assert(PA_REFCNT_VALUE(y) > 0);
1585
1586     return &y->hook;
1587 }
1588
1589 const char*pa_bluetooth_get_form_factor(uint32_t class) {
1590     unsigned i;
1591     const char *r;
1592
1593     static const char * const table[] = {
1594         [1] = "headset",
1595         [2] = "hands-free",
1596         [4] = "microphone",
1597         [5] = "speaker",
1598         [6] = "headphone",
1599         [7] = "portable",
1600         [8] = "car",
1601         [10] = "hifi"
1602     };
1603
1604     if (((class >> 8) & 31) != 4)
1605         return NULL;
1606
1607     if ((i = (class >> 2) & 63) > PA_ELEMENTSOF(table))
1608         r =  NULL;
1609     else
1610         r = table[i];
1611
1612     if (!r)
1613         pa_log_debug("Unknown Bluetooth minor device class %u", i);
1614
1615     return r;
1616 }
1617
1618 char *pa_bluetooth_cleanup_name(const char *name) {
1619     char *t, *s, *d;
1620     pa_bool_t space = FALSE;
1621
1622     pa_assert(name);
1623
1624     while ((*name >= 1 && *name <= 32) || *name >= 127)
1625         name++;
1626
1627     t = pa_xstrdup(name);
1628
1629     for (s = d = t; *s; s++) {
1630
1631         if (*s <= 32 || *s >= 127 || *s == '_') {
1632             space = TRUE;
1633             continue;
1634         }
1635
1636         if (space) {
1637             *(d++) = ' ';
1638             space = FALSE;
1639         }
1640
1641         *(d++) = *s;
1642     }
1643
1644     *d = 0;
1645
1646     return t;
1647 }
1648
1649 pa_bool_t pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid) {
1650     pa_assert(uuid);
1651
1652     while (uuids) {
1653         if (strcasecmp(uuids->uuid, uuid) == 0)
1654             return TRUE;
1655
1656         uuids = uuids->next;
1657     }
1658
1659     return FALSE;
1660 }