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