Revert "bluetooth: Support transport auto-release"
[platform/upstream/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 "a2dp-codecs.h"
34
35 #define HFP_AG_ENDPOINT "/MediaEndpoint/HFPAG"
36 #define HFP_HS_ENDPOINT "/MediaEndpoint/HFPHS"
37 #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
38 #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
39
40 #define ENDPOINT_INTROSPECT_XML                                         \
41     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
42     "<node>"                                                            \
43     " <interface name=\"org.bluez.MediaEndpoint\">"                     \
44     "  <method name=\"SetConfiguration\">"                              \
45     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
46     "   <arg name=\"configuration\" direction=\"in\" type=\"ay\"/>"     \
47     "  </method>"                                                       \
48     "  <method name=\"SelectConfiguration\">"                           \
49     "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
50     "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
51     "  </method>"                                                       \
52     "  <method name=\"ClearConfiguration\">"                            \
53     "  </method>"                                                       \
54     "  <method name=\"Release\">"                                       \
55     "  </method>"                                                       \
56     " </interface>"                                                     \
57     " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
58     "  <method name=\"Introspect\">"                                    \
59     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
60     "  </method>"                                                       \
61     " </interface>"                                                     \
62     "</node>"
63
64 #define MEDIA_ENDPOINT_1_INTROSPECT_XML                                 \
65     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
66     "<node>"                                                            \
67     " <interface name=\"org.bluez.MediaEndpoint1\">"                    \
68     "  <method name=\"SetConfiguration\">"                              \
69     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
70     "   <arg name=\"configuration\" direction=\"in\" type=\"ay\"/>"     \
71     "  </method>"                                                       \
72     "  <method name=\"SelectConfiguration\">"                           \
73     "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
74     "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
75     "  </method>"                                                       \
76     "  <method name=\"ClearConfiguration\">"                            \
77     "  </method>"                                                       \
78     "  <method name=\"Release\">"                                       \
79     "  </method>"                                                       \
80     " </interface>"                                                     \
81     " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
82     "  <method name=\"Introspect\">"                                    \
83     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
84     "  </method>"                                                       \
85     " </interface>"                                                     \
86     "</node>"
87
88 typedef enum pa_bluez_version {
89     BLUEZ_VERSION_UNKNOWN,
90     BLUEZ_VERSION_4,
91     BLUEZ_VERSION_5,
92 } pa_bluez_version_t;
93
94 struct pa_bluetooth_discovery {
95     PA_REFCNT_DECLARE;
96
97     pa_core *core;
98     pa_dbus_connection *connection;
99     PA_LLIST_HEAD(pa_dbus_pending, pending);
100     pa_bluez_version_t version;
101     bool adapters_listed;
102     pa_hashmap *devices;
103     pa_hashmap *transports;
104     pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
105     bool filter_added;
106 };
107
108 static void get_properties_reply(DBusPendingCall *pending, void *userdata);
109 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func,
110                                                 void *call_data);
111 static void found_adapter(pa_bluetooth_discovery *y, const char *path);
112 static pa_bluetooth_device *found_device(pa_bluetooth_discovery *y, const char* path);
113
114 static pa_bt_audio_state_t audio_state_from_string(const char* value) {
115     pa_assert(value);
116
117     if (pa_streq(value, "disconnected"))
118         return PA_BT_AUDIO_STATE_DISCONNECTED;
119     else if (pa_streq(value, "connecting"))
120         return PA_BT_AUDIO_STATE_CONNECTING;
121     else if (pa_streq(value, "connected"))
122         return PA_BT_AUDIO_STATE_CONNECTED;
123     else if (pa_streq(value, "playing"))
124         return PA_BT_AUDIO_STATE_PLAYING;
125
126     return PA_BT_AUDIO_STATE_INVALID;
127 }
128
129 static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
130     pa_assert(value);
131     pa_assert(state);
132
133     if (pa_streq(value, "idle"))
134         *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
135     else if (pa_streq(value, "pending") || pa_streq(value, "active")) /* We don't need such a separation */
136         *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
137     else
138         return -1;
139
140     return 0;
141 }
142
143 const char *pa_bt_profile_to_string(enum profile profile) {
144     switch(profile) {
145         case PROFILE_A2DP:
146             return "a2dp";
147         case PROFILE_A2DP_SOURCE:
148             return "a2dp_source";
149         case PROFILE_HSP:
150             return "hsp";
151         case PROFILE_HFGW:
152             return "hfgw";
153         case PROFILE_OFF:
154             pa_assert_not_reached();
155     }
156
157     pa_assert_not_reached();
158 }
159
160 static int profile_from_interface(const char *interface, enum profile *p) {
161     pa_assert(interface);
162     pa_assert(p);
163
164     if (pa_streq(interface, "org.bluez.AudioSink")) {
165         *p = PROFILE_A2DP;
166         return 0;
167     } else if (pa_streq(interface, "org.bluez.AudioSource")) {
168         *p = PROFILE_A2DP_SOURCE;
169         return 0;
170     } else if (pa_streq(interface, "org.bluez.Headset")) {
171         *p = PROFILE_HSP;
172         return 0;
173     } else if (pa_streq(interface, "org.bluez.HandsfreeGateway")) {
174         *p = PROFILE_HFGW;
175         return 0;
176     }
177
178     return -1;
179 }
180
181 static pa_bluetooth_transport_state_t audio_state_to_transport_state(pa_bt_audio_state_t state) {
182     switch (state) {
183         case PA_BT_AUDIO_STATE_INVALID: /* Typically if state hasn't been received yet */
184         case PA_BT_AUDIO_STATE_DISCONNECTED:
185         case PA_BT_AUDIO_STATE_CONNECTING:
186             return PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
187         case PA_BT_AUDIO_STATE_CONNECTED:
188             return PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
189         case PA_BT_AUDIO_STATE_PLAYING:
190             return PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
191     }
192
193     pa_assert_not_reached();
194 }
195
196 static pa_bluetooth_uuid *uuid_new(const char *uuid) {
197     pa_bluetooth_uuid *u;
198
199     u = pa_xnew(pa_bluetooth_uuid, 1);
200     u->uuid = pa_xstrdup(uuid);
201     PA_LLIST_INIT(pa_bluetooth_uuid, u);
202
203     return u;
204 }
205
206 static void uuid_free(pa_bluetooth_uuid *u) {
207     pa_assert(u);
208
209     pa_xfree(u->uuid);
210     pa_xfree(u);
211 }
212
213 static pa_bluetooth_device* device_new(pa_bluetooth_discovery *discovery, const char *path) {
214     pa_bluetooth_device *d;
215     unsigned i;
216
217     pa_assert(discovery);
218     pa_assert(path);
219
220     d = pa_xnew0(pa_bluetooth_device, 1);
221
222     d->discovery = discovery;
223     d->dead = false;
224
225     d->device_info_valid = 0;
226
227     d->name = NULL;
228     d->path = pa_xstrdup(path);
229     d->paired = -1;
230     d->alias = NULL;
231     PA_LLIST_HEAD_INIT(pa_bluetooth_uuid, d->uuids);
232     d->address = NULL;
233     d->class = -1;
234     d->trusted = -1;
235
236     d->audio_state = PA_BT_AUDIO_STATE_INVALID;
237
238     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
239         d->profile_state[i] = PA_BT_AUDIO_STATE_INVALID;
240
241     return d;
242 }
243
244 static void transport_free(pa_bluetooth_transport *t) {
245     pa_assert(t);
246
247     pa_xfree(t->owner);
248     pa_xfree(t->path);
249     pa_xfree(t->config);
250     pa_xfree(t);
251 }
252
253 static void device_free(pa_bluetooth_device *d) {
254     pa_bluetooth_uuid *u;
255     pa_bluetooth_transport *t;
256     unsigned i;
257
258     pa_assert(d);
259
260     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
261         if (!(t = d->transports[i]))
262             continue;
263
264         d->transports[i] = NULL;
265         pa_hashmap_remove(d->discovery->transports, t->path);
266         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
267         pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
268         transport_free(t);
269     }
270
271     while ((u = d->uuids)) {
272         PA_LLIST_REMOVE(pa_bluetooth_uuid, d->uuids, u);
273         uuid_free(u);
274     }
275
276     pa_xfree(d->name);
277     pa_xfree(d->path);
278     pa_xfree(d->alias);
279     pa_xfree(d->address);
280     pa_xfree(d);
281 }
282
283 static const char *check_variant_property(DBusMessageIter *i) {
284     const char *key;
285
286     pa_assert(i);
287
288     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
289         pa_log("Property name not a string.");
290         return NULL;
291     }
292
293     dbus_message_iter_get_basic(i, &key);
294
295     if (!dbus_message_iter_next(i)) {
296         pa_log("Property value missing");
297         return NULL;
298     }
299
300     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
301         pa_log("Property value not a variant.");
302         return NULL;
303     }
304
305     return key;
306 }
307
308 static int parse_manager_property(pa_bluetooth_discovery *y, DBusMessageIter *i, bool is_property_change) {
309     const char *key;
310     DBusMessageIter variant_i;
311
312     pa_assert(y);
313
314     key = check_variant_property(i);
315     if (key == NULL)
316         return -1;
317
318     dbus_message_iter_recurse(i, &variant_i);
319
320     switch (dbus_message_iter_get_arg_type(&variant_i)) {
321
322         case DBUS_TYPE_ARRAY: {
323
324             DBusMessageIter ai;
325             dbus_message_iter_recurse(&variant_i, &ai);
326
327             if (pa_streq(key, "Adapters")) {
328                 y->adapters_listed = true;
329
330                 if (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_OBJECT_PATH)
331                     break;
332
333                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
334                     const char *value;
335
336                     dbus_message_iter_get_basic(&ai, &value);
337
338                     found_adapter(y, value);
339
340                     dbus_message_iter_next(&ai);
341                 }
342             }
343
344             break;
345         }
346     }
347
348     return 0;
349 }
350
351 static int parse_adapter_property(pa_bluetooth_discovery *y, DBusMessageIter *i, bool is_property_change) {
352     const char *key;
353     DBusMessageIter variant_i;
354
355     pa_assert(y);
356
357     key = check_variant_property(i);
358     if (key == NULL)
359         return -1;
360
361     dbus_message_iter_recurse(i, &variant_i);
362
363     switch (dbus_message_iter_get_arg_type(&variant_i)) {
364
365         case DBUS_TYPE_ARRAY: {
366
367             DBusMessageIter ai;
368             dbus_message_iter_recurse(&variant_i, &ai);
369
370             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_OBJECT_PATH &&
371                 pa_streq(key, "Devices")) {
372
373                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
374                     const char *value;
375
376                     dbus_message_iter_get_basic(&ai, &value);
377
378                     found_device(y, value);
379
380                     dbus_message_iter_next(&ai);
381                 }
382             }
383
384             break;
385         }
386     }
387
388     return 0;
389 }
390
391 static int parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i, bool is_property_change) {
392     const char *key;
393     DBusMessageIter variant_i;
394
395     pa_assert(d);
396
397     key = check_variant_property(i);
398     if (key == NULL)
399         return -1;
400
401     dbus_message_iter_recurse(i, &variant_i);
402
403     switch (dbus_message_iter_get_arg_type(&variant_i)) {
404
405         case DBUS_TYPE_STRING: {
406
407             const char *value;
408             dbus_message_iter_get_basic(&variant_i, &value);
409
410             if (pa_streq(key, "Name")) {
411                 pa_xfree(d->name);
412                 d->name = pa_xstrdup(value);
413             } else if (pa_streq(key, "Alias")) {
414                 pa_xfree(d->alias);
415                 d->alias = pa_xstrdup(value);
416             } else if (pa_streq(key, "Address")) {
417                 if (is_property_change) {
418                     pa_log("Device property 'Address' expected to be constant but changed for %s", d->path);
419                     return -1;
420                 }
421
422                 if (d->address) {
423                     pa_log("Device %s: Received a duplicate Address property.", d->path);
424                     return -1;
425                 }
426
427                 d->address = pa_xstrdup(value);
428             }
429
430 /*             pa_log_debug("Value %s", value); */
431
432             break;
433         }
434
435         case DBUS_TYPE_BOOLEAN: {
436
437             dbus_bool_t value;
438             dbus_message_iter_get_basic(&variant_i, &value);
439
440             if (pa_streq(key, "Paired"))
441                 d->paired = !!value;
442             else if (pa_streq(key, "Trusted"))
443                 d->trusted = !!value;
444
445 /*             pa_log_debug("Value %s", pa_yes_no(value)); */
446
447             break;
448         }
449
450         case DBUS_TYPE_UINT32: {
451
452             uint32_t value;
453             dbus_message_iter_get_basic(&variant_i, &value);
454
455             if (pa_streq(key, "Class"))
456                 d->class = (int) value;
457
458 /*             pa_log_debug("Value %u", (unsigned) value); */
459
460             break;
461         }
462
463         case DBUS_TYPE_ARRAY: {
464
465             DBusMessageIter ai;
466             dbus_message_iter_recurse(&variant_i, &ai);
467
468             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
469                 DBusMessage *m;
470                 bool has_audio = false;
471
472                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
473                     pa_bluetooth_uuid *node;
474                     const char *value;
475                     struct pa_bluetooth_hook_uuid_data uuiddata;
476
477                     dbus_message_iter_get_basic(&ai, &value);
478
479                     if (pa_bluetooth_uuid_has(d->uuids, value)) {
480                         dbus_message_iter_next(&ai);
481                         continue;
482                     }
483
484                     node = uuid_new(value);
485                     PA_LLIST_PREPEND(pa_bluetooth_uuid, d->uuids, node);
486
487                     uuiddata.device = d;
488                     uuiddata.uuid = value;
489                     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UUID_ADDED], &uuiddata);
490
491                     if (d->discovery->version >= BLUEZ_VERSION_5) {
492                         dbus_message_iter_next(&ai);
493                         continue;
494                     }
495
496                     /* Vudentz said the interfaces are here when the UUIDs are announced */
497                     if (strcasecmp(HSP_AG_UUID, value) == 0 || strcasecmp(HFP_AG_UUID, value) == 0) {
498                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.HandsfreeGateway",
499                                                                       "GetProperties"));
500                         send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
501                         has_audio = true;
502                     } else if (strcasecmp(HSP_HS_UUID, value) == 0 || strcasecmp(HFP_HS_UUID, value) == 0) {
503                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Headset",
504                                                                       "GetProperties"));
505                         send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
506                         has_audio = true;
507                     } else if (strcasecmp(A2DP_SINK_UUID, value) == 0) {
508                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSink",
509                                                                       "GetProperties"));
510                         send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
511                         has_audio = true;
512                     } else if (strcasecmp(A2DP_SOURCE_UUID, value) == 0) {
513                         pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.AudioSource",
514                                                                       "GetProperties"));
515                         send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
516                         has_audio = true;
517                     }
518
519                     dbus_message_iter_next(&ai);
520                 }
521
522                 /* this might eventually be racy if .Audio is not there yet, but
523                    the State change will come anyway later, so this call is for
524                    cold-detection mostly */
525                 if (has_audio) {
526                     pa_assert_se(m = dbus_message_new_method_call("org.bluez", d->path, "org.bluez.Audio", "GetProperties"));
527                     send_and_add_to_pending(d->discovery, m, get_properties_reply, d);
528                 }
529             }
530
531             break;
532         }
533     }
534
535     return 0;
536 }
537
538 static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
539     switch (state) {
540         case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
541             return "disconnected";
542         case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
543             return "idle";
544         case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
545             return "playing";
546     }
547
548     pa_assert_not_reached();
549 }
550
551 static int parse_audio_property(pa_bluetooth_device *d, const char *interface, DBusMessageIter *i, bool is_property_change) {
552     pa_bluetooth_transport *transport;
553     const char *key;
554     DBusMessageIter variant_i;
555     bool is_audio_interface;
556     enum profile p = PROFILE_OFF;
557
558     pa_assert(d);
559     pa_assert(interface);
560     pa_assert(i);
561
562     if (!(is_audio_interface = pa_streq(interface, "org.bluez.Audio")))
563         if (profile_from_interface(interface, &p) < 0)
564             return 0; /* Interface not known so silently ignore property */
565
566     key = check_variant_property(i);
567     if (key == NULL)
568         return -1;
569
570     transport = p == PROFILE_OFF ? NULL : d->transports[p];
571
572     dbus_message_iter_recurse(i, &variant_i);
573
574 /*     pa_log_debug("Parsing property org.bluez.{Audio|AudioSink|AudioSource|Headset}.%s", key); */
575
576     switch (dbus_message_iter_get_arg_type(&variant_i)) {
577
578         case DBUS_TYPE_STRING: {
579
580             const char *value;
581             dbus_message_iter_get_basic(&variant_i, &value);
582
583             if (pa_streq(key, "State")) {
584                 pa_bt_audio_state_t state = audio_state_from_string(value);
585                 pa_bluetooth_transport_state_t old_state;
586
587                 pa_log_debug("Device %s interface %s property 'State' changed to value '%s'", d->path, interface, value);
588
589                 if (state == PA_BT_AUDIO_STATE_INVALID)
590                     return -1;
591
592                 if (is_audio_interface) {
593                     d->audio_state = state;
594                     break;
595                 }
596
597                 pa_assert(p != PROFILE_OFF);
598
599                 d->profile_state[p] = state;
600
601                 if (!transport)
602                     break;
603
604                 old_state = transport->state;
605                 transport->state = audio_state_to_transport_state(state);
606
607                 if (transport->state != old_state) {
608                     pa_log_debug("Transport %s (profile %s) changed state from %s to %s.", transport->path,
609                                  pa_bt_profile_to_string(transport->profile), transport_state_to_string(old_state),
610                                  transport_state_to_string(transport->state));
611
612                     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], transport);
613                 }
614             }
615
616             break;
617         }
618
619         case DBUS_TYPE_UINT16: {
620             uint16_t value;
621
622             dbus_message_iter_get_basic(&variant_i, &value);
623
624             if (pa_streq(key, "MicrophoneGain")) {
625                 uint16_t gain;
626
627                 pa_log_debug("dbus: property '%s' changed to value '%u'", key, value);
628
629                 if (!transport) {
630                     pa_log("Volume change does not have an associated transport");
631                     return -1;
632                 }
633
634                 if ((gain = PA_MIN(value, HSP_MAX_GAIN)) == transport->microphone_gain)
635                     break;
636
637                 transport->microphone_gain = gain;
638                 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_MICROPHONE_GAIN_CHANGED], transport);
639             } else if (pa_streq(key, "SpeakerGain")) {
640                 uint16_t gain;
641
642                 pa_log_debug("dbus: property '%s' changed to value '%u'", key, value);
643
644                 if (!transport) {
645                     pa_log("Volume change does not have an associated transport");
646                     return -1;
647                 }
648
649                 if ((gain = PA_MIN(value, HSP_MAX_GAIN)) == transport->speaker_gain)
650                     break;
651
652                 transport->speaker_gain = gain;
653                 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_SPEAKER_GAIN_CHANGED], transport);
654             }
655
656             break;
657         }
658     }
659
660     return 0;
661 }
662
663 static void run_callback(pa_bluetooth_device *d, bool dead) {
664     pa_assert(d);
665
666     if (d->device_info_valid != 1)
667         return;
668
669     d->dead = dead;
670     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], d);
671 }
672
673 static void remove_all_devices(pa_bluetooth_discovery *y) {
674     pa_bluetooth_device *d;
675
676     pa_assert(y);
677
678     while ((d = pa_hashmap_steal_first(y->devices))) {
679         run_callback(d, true);
680         device_free(d);
681     }
682 }
683
684 static pa_bluetooth_device *found_device(pa_bluetooth_discovery *y, const char* path) {
685     DBusMessage *m;
686     pa_bluetooth_device *d;
687
688     pa_assert(y);
689     pa_assert(path);
690
691     d = pa_hashmap_get(y->devices, path);
692     if (d)
693         return d;
694
695     d = device_new(y, path);
696
697     pa_hashmap_put(y->devices, d->path, d);
698
699     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Device", "GetProperties"));
700     send_and_add_to_pending(y, m, get_properties_reply, d);
701
702     /* Before we read the other properties (Audio, AudioSink, AudioSource,
703      * Headset) we wait that the UUID is read */
704     return d;
705 }
706
707 static void get_properties_reply(DBusPendingCall *pending, void *userdata) {
708     DBusMessage *r;
709     DBusMessageIter arg_i, element_i;
710     pa_dbus_pending *p;
711     pa_bluetooth_device *d;
712     pa_bluetooth_discovery *y;
713     int valid;
714     bool old_any_connected;
715
716     pa_assert_se(p = userdata);
717     pa_assert_se(y = p->context_data);
718     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
719
720 /*     pa_log_debug("Got %s.GetProperties response for %s", */
721 /*                  dbus_message_get_interface(p->message), */
722 /*                  dbus_message_get_path(p->message)); */
723
724     /* We don't use p->call_data here right-away since the device
725      * might already be invalidated at this point */
726
727     if (dbus_message_has_interface(p->message, "org.bluez.Manager") ||
728         dbus_message_has_interface(p->message, "org.bluez.Adapter"))
729         d = NULL;
730     else if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(p->message)))) {
731         pa_log_warn("Received GetProperties() reply from unknown device: %s (device removed?)", dbus_message_get_path(p->message));
732         goto finish2;
733     }
734
735     pa_assert(p->call_data == d);
736
737     if (d != NULL)
738         old_any_connected = pa_bluetooth_device_any_audio_connected(d);
739
740     valid = dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR ? -1 : 1;
741
742     if (dbus_message_is_method_call(p->message, "org.bluez.Device", "GetProperties"))
743         d->device_info_valid = valid;
744
745     if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
746         pa_log_debug("Bluetooth daemon is apparently not available.");
747         remove_all_devices(y);
748         goto finish2;
749     }
750
751     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
752         pa_log("%s.GetProperties() failed: %s: %s", dbus_message_get_interface(p->message), dbus_message_get_error_name(r),
753                pa_dbus_get_error_message(r));
754         goto finish;
755     }
756
757     if (!dbus_message_iter_init(r, &arg_i)) {
758         pa_log("GetProperties reply has no arguments.");
759         goto finish;
760     }
761
762     if (dbus_message_iter_get_arg_type(&arg_i) != DBUS_TYPE_ARRAY) {
763         pa_log("GetProperties argument is not an array.");
764         goto finish;
765     }
766
767     dbus_message_iter_recurse(&arg_i, &element_i);
768     while (dbus_message_iter_get_arg_type(&element_i) != DBUS_TYPE_INVALID) {
769
770         if (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
771             DBusMessageIter dict_i;
772
773             dbus_message_iter_recurse(&element_i, &dict_i);
774
775             if (dbus_message_has_interface(p->message, "org.bluez.Manager")) {
776                 if (parse_manager_property(y, &dict_i, false) < 0)
777                     goto finish;
778
779             } else if (dbus_message_has_interface(p->message, "org.bluez.Adapter")) {
780                 if (parse_adapter_property(y, &dict_i, false) < 0)
781                     goto finish;
782
783             } else if (dbus_message_has_interface(p->message, "org.bluez.Device")) {
784                 if (parse_device_property(d, &dict_i, false) < 0)
785                     goto finish;
786
787             } else if (parse_audio_property(d, dbus_message_get_interface(p->message), &dict_i, false) < 0)
788                 goto finish;
789
790         }
791
792         dbus_message_iter_next(&element_i);
793     }
794
795 finish:
796     if (d != NULL && old_any_connected != pa_bluetooth_device_any_audio_connected(d))
797         run_callback(d, false);
798
799 finish2:
800     dbus_message_unref(r);
801
802     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
803     pa_dbus_pending_free(p);
804 }
805
806 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m, DBusPendingCallNotifyFunction func,
807                                                 void *call_data) {
808     pa_dbus_pending *p;
809     DBusPendingCall *call;
810
811     pa_assert(y);
812     pa_assert(m);
813
814     pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
815
816     p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
817     PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
818     dbus_pending_call_set_notify(call, func, p, NULL);
819
820     return p;
821 }
822
823 static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
824     DBusMessage *r;
825     pa_dbus_pending *p;
826     pa_bluetooth_discovery *y;
827     char *endpoint;
828
829     pa_assert(pending);
830     pa_assert_se(p = userdata);
831     pa_assert_se(y = p->context_data);
832     pa_assert_se(endpoint = p->call_data);
833     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
834
835     if (dbus_message_is_error(r, DBUS_ERROR_SERVICE_UNKNOWN)) {
836         pa_log_debug("Bluetooth daemon is apparently not available.");
837         remove_all_devices(y);
838         goto finish;
839     }
840
841     if (dbus_message_is_error(r, PA_BLUETOOTH_ERROR_NOT_SUPPORTED)) {
842         pa_log_info("Couldn't register endpoint %s, because BlueZ is configured to disable the endpoint type.", endpoint);
843         goto finish;
844     }
845
846     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
847         pa_log("RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
848         goto finish;
849     }
850
851 finish:
852     dbus_message_unref(r);
853
854     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
855     pa_dbus_pending_free(p);
856
857     pa_xfree(endpoint);
858 }
859
860 static void register_endpoint(pa_bluetooth_discovery *y, const char *path, const char *endpoint, const char *uuid) {
861     DBusMessage *m;
862     DBusMessageIter i, d;
863     uint8_t codec = 0;
864     const char *interface = y->version == BLUEZ_VERSION_4 ? "org.bluez.Media" : "org.bluez.Media1";
865
866     pa_log_debug("Registering %s on adapter %s.", endpoint, path);
867
868     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, interface, "RegisterEndpoint"));
869
870     dbus_message_iter_init_append(m, &i);
871
872     dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint);
873
874     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
875                                     DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
876                                     &d);
877
878     pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
879
880     pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec);
881
882     if (pa_streq(uuid, HFP_AG_UUID) || pa_streq(uuid, HFP_HS_UUID)) {
883         uint8_t capability = 0;
884         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capability, 1);
885     } else {
886         a2dp_sbc_t capabilities;
887
888         capabilities.channel_mode = SBC_CHANNEL_MODE_MONO | SBC_CHANNEL_MODE_DUAL_CHANNEL |
889                                     SBC_CHANNEL_MODE_STEREO | SBC_CHANNEL_MODE_JOINT_STEREO;
890         capabilities.frequency = SBC_SAMPLING_FREQ_16000 | SBC_SAMPLING_FREQ_32000 |
891                                  SBC_SAMPLING_FREQ_44100 | SBC_SAMPLING_FREQ_48000;
892         capabilities.allocation_method = SBC_ALLOCATION_SNR | SBC_ALLOCATION_LOUDNESS;
893         capabilities.subbands = SBC_SUBBANDS_4 | SBC_SUBBANDS_8;
894         capabilities.block_length = SBC_BLOCK_LENGTH_4 | SBC_BLOCK_LENGTH_8 |
895                                     SBC_BLOCK_LENGTH_12 | SBC_BLOCK_LENGTH_16;
896         capabilities.min_bitpool = MIN_BITPOOL;
897         capabilities.max_bitpool = MAX_BITPOOL;
898
899         pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, sizeof(capabilities));
900     }
901
902     dbus_message_iter_close_container(&i, &d);
903
904     send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
905 }
906
907 static void register_adapter_endpoints(pa_bluetooth_discovery *y, const char *path) {
908     register_endpoint(y, path, A2DP_SOURCE_ENDPOINT, A2DP_SOURCE_UUID);
909     register_endpoint(y, path, A2DP_SINK_ENDPOINT, A2DP_SINK_UUID);
910
911     /* For BlueZ 5, only A2DP is registered in the Media API */
912     if (y->version >= BLUEZ_VERSION_5)
913         return;
914
915     register_endpoint(y, path, HFP_AG_ENDPOINT, HFP_AG_UUID);
916     register_endpoint(y, path, HFP_HS_ENDPOINT, HFP_HS_UUID);
917 }
918
919 static void found_adapter(pa_bluetooth_discovery *y, const char *path) {
920     DBusMessage *m;
921
922     pa_assert_se(m = dbus_message_new_method_call("org.bluez", path, "org.bluez.Adapter", "GetProperties"));
923     send_and_add_to_pending(y, m, get_properties_reply, NULL);
924
925     register_adapter_endpoints(y, path);
926 }
927
928 static void list_adapters(pa_bluetooth_discovery *y) {
929     DBusMessage *m;
930     pa_assert(y);
931
932     pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.bluez.Manager", "GetProperties"));
933     send_and_add_to_pending(y, m, get_properties_reply, NULL);
934 }
935
936 static int parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i, bool is_property_change) {
937     DBusMessageIter element_i;
938     int ret = 0;
939
940     dbus_message_iter_recurse(i, &element_i);
941
942     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
943         DBusMessageIter dict_i;
944
945         dbus_message_iter_recurse(&element_i, &dict_i);
946
947         if (parse_device_property(d, &dict_i, is_property_change) < 0)
948             ret = -1;
949
950         dbus_message_iter_next(&element_i);
951     }
952
953     if (!d->address || !d->alias || d->paired < 0 || d->trusted < 0) {
954         pa_log_error("Non-optional information missing for device %s", d->path);
955         d->device_info_valid = -1;
956         return -1;
957     }
958
959     d->device_info_valid = 1;
960     return ret;
961 }
962
963 static int parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
964     DBusMessageIter element_i;
965     const char *path;
966
967     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
968     dbus_message_iter_get_basic(dict_i, &path);
969
970     pa_assert_se(dbus_message_iter_next(dict_i));
971     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
972
973     dbus_message_iter_recurse(dict_i, &element_i);
974
975     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
976         DBusMessageIter iface_i;
977         const char *interface;
978
979         dbus_message_iter_recurse(&element_i, &iface_i);
980
981         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
982         dbus_message_iter_get_basic(&iface_i, &interface);
983
984         pa_assert_se(dbus_message_iter_next(&iface_i));
985         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
986
987         if (pa_streq(interface, "org.bluez.Adapter1")) {
988             pa_log_debug("Adapter %s found", path);
989             register_adapter_endpoints(y, path);
990         } else if (pa_streq(interface, "org.bluez.Device1")) {
991             pa_bluetooth_device *d;
992
993             if (pa_hashmap_get(y->devices, path)) {
994                 pa_log("Found duplicated D-Bus path for device %s", path);
995                 return -1;
996             }
997
998             pa_log_debug("Device %s found", path);
999
1000             d = device_new(y, path);
1001             pa_hashmap_put(y->devices, d->path, d);
1002
1003             /* FIXME: BlueZ 5 doesn't support the old Audio interface, and thus
1004                it's not possible to know if any audio profile is about to be
1005                connected. This can introduce regressions with modules such as
1006                module-card-restore */
1007             d->audio_state = PA_BT_AUDIO_STATE_DISCONNECTED;
1008
1009             if (parse_device_properties(d, &iface_i, false) < 0)
1010                 return -1;
1011         }
1012
1013         dbus_message_iter_next(&element_i);
1014     }
1015
1016     return 0;
1017 }
1018
1019 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
1020     DBusMessage *r;
1021     pa_dbus_pending *p;
1022     pa_bluetooth_discovery *y;
1023     DBusMessageIter arg_i, element_i;
1024
1025     pa_assert_se(p = userdata);
1026     pa_assert_se(y = p->context_data);
1027     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1028
1029     if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
1030         pa_log_info("D-Bus ObjectManager not detected so falling back to BlueZ version 4 API.");
1031         y->version = BLUEZ_VERSION_4;
1032         list_adapters(y);
1033         goto finish;
1034     }
1035
1036     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1037         pa_log("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1038         goto finish;
1039     }
1040
1041     pa_log_info("D-Bus ObjectManager detected so assuming BlueZ version 5.");
1042     y->version = BLUEZ_VERSION_5;
1043
1044     if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
1045         pa_log("Invalid reply signature for GetManagedObjects().");
1046         goto finish;
1047     }
1048
1049     dbus_message_iter_recurse(&arg_i, &element_i);
1050     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1051         DBusMessageIter dict_i;
1052
1053         dbus_message_iter_recurse(&element_i, &dict_i);
1054
1055         /* Ignore errors here and proceed with next object */
1056         parse_interfaces_and_properties(y, &dict_i);
1057
1058         dbus_message_iter_next(&element_i);
1059     }
1060
1061 finish:
1062     dbus_message_unref(r);
1063
1064     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1065     pa_dbus_pending_free(p);
1066 }
1067
1068 static void init_bluez(pa_bluetooth_discovery *y) {
1069     DBusMessage *m;
1070     pa_assert(y);
1071
1072     pa_assert_se(m = dbus_message_new_method_call("org.bluez", "/", "org.freedesktop.DBus.ObjectManager",
1073                                                   "GetManagedObjects"));
1074     send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
1075 }
1076
1077 static int transport_parse_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
1078     const char *key;
1079     DBusMessageIter variant_i;
1080
1081     key = check_variant_property(i);
1082     if (key == NULL)
1083         return -1;
1084
1085     dbus_message_iter_recurse(i, &variant_i);
1086
1087     switch (dbus_message_iter_get_arg_type(&variant_i)) {
1088
1089         case DBUS_TYPE_BOOLEAN: {
1090
1091             dbus_bool_t value;
1092             dbus_message_iter_get_basic(&variant_i, &value);
1093
1094             if (pa_streq(key, "NREC") && t->nrec != value) {
1095                 t->nrec = value;
1096                 pa_log_debug("Transport %s: Property 'NREC' changed to %s.", t->path, t->nrec ? "True" : "False");
1097                 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_NREC_CHANGED], t);
1098             }
1099
1100             break;
1101          }
1102
1103         case DBUS_TYPE_STRING: {
1104
1105             const char *value;
1106             dbus_message_iter_get_basic(&variant_i, &value);
1107
1108             if (pa_streq(key, "State")) { /* Added in BlueZ 5.0 */
1109                 bool old_any_connected = pa_bluetooth_device_any_audio_connected(t->device);
1110
1111                 if (transport_state_from_string(value, &t->state) < 0) {
1112                     pa_log("Transport %s has an invalid state: '%s'", t->path, value);
1113                     return -1;
1114                 }
1115
1116                 pa_log_debug("dbus: transport %s set to state '%s'", t->path, value);
1117                 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
1118
1119                 if (old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
1120                     run_callback(t->device, old_any_connected);
1121             }
1122
1123             break;
1124         }
1125     }
1126
1127     return 0;
1128 }
1129
1130 static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
1131     DBusMessageIter element_i;
1132
1133     dbus_message_iter_recurse(i, &element_i);
1134
1135     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1136         DBusMessageIter dict_i;
1137
1138         dbus_message_iter_recurse(&element_i, &dict_i);
1139
1140         transport_parse_property(t, &dict_i);
1141
1142         dbus_message_iter_next(&element_i);
1143     }
1144
1145     return 0;
1146 }
1147
1148 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
1149     DBusError err;
1150     pa_bluetooth_discovery *y;
1151
1152     pa_assert(bus);
1153     pa_assert(m);
1154
1155     pa_assert_se(y = userdata);
1156
1157     dbus_error_init(&err);
1158
1159     pa_log_debug("dbus: interface=%s, path=%s, member=%s\n",
1160             dbus_message_get_interface(m),
1161             dbus_message_get_path(m),
1162             dbus_message_get_member(m));
1163
1164     if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceRemoved")) {
1165         const char *path;
1166         pa_bluetooth_device *d;
1167
1168         if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1169             pa_log("Failed to parse org.bluez.Adapter.DeviceRemoved: %s", err.message);
1170             goto fail;
1171         }
1172
1173         pa_log_debug("Device %s removed", path);
1174
1175         if ((d = pa_hashmap_remove(y->devices, path))) {
1176             run_callback(d, true);
1177             device_free(d);
1178         }
1179
1180         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1181
1182     } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "DeviceCreated")) {
1183         const char *path;
1184
1185         if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1186             pa_log("Failed to parse org.bluez.Adapter.DeviceCreated: %s", err.message);
1187             goto fail;
1188         }
1189
1190         pa_log_debug("Device %s created", path);
1191
1192         found_device(y, path);
1193         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1194
1195     } else if (dbus_message_is_signal(m, "org.bluez.Manager", "AdapterAdded")) {
1196         const char *path;
1197
1198         if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1199             pa_log("Failed to parse org.bluez.Manager.AdapterAdded: %s", err.message);
1200             goto fail;
1201         }
1202
1203         if (!y->adapters_listed) {
1204             pa_log_debug("Ignoring 'AdapterAdded' because initial adapter list has not been received yet.");
1205             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1206         }
1207
1208         pa_log_debug("Adapter %s created", path);
1209
1210         found_adapter(y, path);
1211         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1212
1213     } else if (dbus_message_is_signal(m, "org.bluez.Audio", "PropertyChanged") ||
1214                dbus_message_is_signal(m, "org.bluez.Headset", "PropertyChanged") ||
1215                dbus_message_is_signal(m, "org.bluez.AudioSink", "PropertyChanged") ||
1216                dbus_message_is_signal(m, "org.bluez.AudioSource", "PropertyChanged") ||
1217                dbus_message_is_signal(m, "org.bluez.HandsfreeGateway", "PropertyChanged") ||
1218                dbus_message_is_signal(m, "org.bluez.Device", "PropertyChanged")) {
1219
1220         pa_bluetooth_device *d;
1221
1222         if ((d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
1223             DBusMessageIter arg_i;
1224             bool old_any_connected = pa_bluetooth_device_any_audio_connected(d);
1225
1226             if (!dbus_message_iter_init(m, &arg_i)) {
1227                 pa_log("Failed to parse PropertyChanged for device %s", d->path);
1228                 goto fail;
1229             }
1230
1231             if (dbus_message_has_interface(m, "org.bluez.Device")) {
1232                 if (parse_device_property(d, &arg_i, true) < 0)
1233                     goto fail;
1234
1235             } else if (parse_audio_property(d, dbus_message_get_interface(m), &arg_i, true) < 0)
1236                 goto fail;
1237
1238             if (old_any_connected != pa_bluetooth_device_any_audio_connected(d))
1239                 run_callback(d, false);
1240         }
1241
1242         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1243
1244     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
1245         const char *name, *old_owner, *new_owner;
1246
1247         if (!dbus_message_get_args(m, &err,
1248                                    DBUS_TYPE_STRING, &name,
1249                                    DBUS_TYPE_STRING, &old_owner,
1250                                    DBUS_TYPE_STRING, &new_owner,
1251                                    DBUS_TYPE_INVALID)) {
1252             pa_log("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
1253             goto fail;
1254         }
1255
1256         if (pa_streq(name, "org.bluez")) {
1257             if (old_owner && *old_owner) {
1258                 pa_log_debug("Bluetooth daemon disappeared.");
1259                 remove_all_devices(y);
1260                 y->adapters_listed = false;
1261                 y->version = BLUEZ_VERSION_UNKNOWN;
1262             }
1263
1264             if (new_owner && *new_owner) {
1265                 pa_log_debug("Bluetooth daemon appeared.");
1266                 init_bluez(y);
1267             }
1268         }
1269
1270         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1271     } else if (dbus_message_is_signal(m, "org.bluez.MediaTransport", "PropertyChanged")) {
1272         pa_bluetooth_transport *t;
1273         DBusMessageIter arg_i;
1274
1275         if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
1276             goto fail;
1277
1278         if (!dbus_message_iter_init(m, &arg_i)) {
1279             pa_log("Failed to parse PropertyChanged for transport %s", t->path);
1280             goto fail;
1281         }
1282
1283         if (transport_parse_property(t, &arg_i) < 0)
1284             goto fail;
1285
1286         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1287     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
1288         DBusMessageIter arg_i;
1289
1290         if (y->version != BLUEZ_VERSION_5)
1291             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1292
1293         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
1294             pa_log("Invalid signature found in InterfacesAdded");
1295             goto fail;
1296         }
1297
1298         if (parse_interfaces_and_properties(y, &arg_i) < 0)
1299             goto fail;
1300
1301         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1302     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
1303         const char *path;
1304         DBusMessageIter arg_i;
1305         DBusMessageIter element_i;
1306
1307         if (y->version != BLUEZ_VERSION_5)
1308             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1309
1310         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
1311             pa_log("Invalid signature found in InterfacesRemoved");
1312             goto fail;
1313         }
1314
1315         dbus_message_iter_get_basic(&arg_i, &path);
1316
1317         pa_assert_se(dbus_message_iter_next(&arg_i));
1318         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
1319
1320         dbus_message_iter_recurse(&arg_i, &element_i);
1321
1322         while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
1323             const char *interface;
1324
1325             dbus_message_iter_get_basic(&element_i, &interface);
1326
1327             if (pa_streq(interface, "org.bluez.Device1")) {
1328                 pa_bluetooth_device *d;
1329
1330                 if (!(d = pa_hashmap_remove(y->devices, path)))
1331                     pa_log_warn("Unknown device removed %s", path);
1332                 else {
1333                     pa_log_debug("Device %s removed", path);
1334                     run_callback(d, true);
1335                     device_free(d);
1336                 }
1337             }
1338
1339             dbus_message_iter_next(&element_i);
1340         }
1341
1342         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1343     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
1344         DBusMessageIter arg_i;
1345         const char *interface;
1346
1347         if (y->version != BLUEZ_VERSION_5)
1348             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1349
1350         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
1351             pa_log("Invalid signature found in PropertiesChanged");
1352             goto fail;
1353         }
1354
1355         dbus_message_iter_get_basic(&arg_i, &interface);
1356
1357         pa_assert_se(dbus_message_iter_next(&arg_i));
1358         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
1359
1360         if (pa_streq(interface, "org.bluez.Device1")) {
1361             pa_bluetooth_device *d;
1362
1363             if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
1364                 pa_log_warn("Property change in unknown device %s", dbus_message_get_path(m));
1365                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1366             }
1367
1368             parse_device_properties(d, &arg_i, true);
1369         } else if (pa_streq(interface, "org.bluez.MediaTransport1")) {
1370             pa_bluetooth_transport *t;
1371
1372             if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
1373                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1374
1375             parse_transport_properties(t, &arg_i);
1376         }
1377
1378         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1379     }
1380
1381 fail:
1382     dbus_error_free(&err);
1383
1384     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1385 }
1386
1387 pa_bluetooth_device* pa_bluetooth_discovery_get_by_address(pa_bluetooth_discovery *y, const char* address) {
1388     pa_bluetooth_device *d;
1389     void *state = NULL;
1390
1391     pa_assert(y);
1392     pa_assert(PA_REFCNT_VALUE(y) > 0);
1393     pa_assert(address);
1394
1395     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
1396         if (pa_streq(d->address, address))
1397             return d->device_info_valid == 1 ? d : NULL;
1398
1399     return NULL;
1400 }
1401
1402 pa_bluetooth_device* pa_bluetooth_discovery_get_by_path(pa_bluetooth_discovery *y, const char* path) {
1403     pa_bluetooth_device *d;
1404
1405     pa_assert(y);
1406     pa_assert(PA_REFCNT_VALUE(y) > 0);
1407     pa_assert(path);
1408
1409     if ((d = pa_hashmap_get(y->devices, path)))
1410         if (d->device_info_valid == 1)
1411             return d;
1412
1413     return NULL;
1414 }
1415
1416 bool pa_bluetooth_device_any_audio_connected(const pa_bluetooth_device *d) {
1417     unsigned i;
1418
1419     pa_assert(d);
1420
1421     if (d->dead || d->device_info_valid != 1)
1422         return false;
1423
1424     if (d->audio_state == PA_BT_AUDIO_STATE_INVALID)
1425         return false;
1426
1427     /* Make sure audio_state is *not* in CONNECTING state before we fire the
1428      * hook to report the new device state. This is actually very important in
1429      * order to make module-card-restore work well with headsets: if the headset
1430      * supports both HSP and A2DP, one of those profiles is connected first and
1431      * then the other, and lastly the Audio interface becomes connected.
1432      * Checking only audio_state means that this function will return false at
1433      * the time when only the first connection has been made. This is good,
1434      * because otherwise, if the first connection is for HSP and we would
1435      * already load a new device module instance, and module-card-restore tries
1436      * to restore the A2DP profile, that would fail because A2DP is not yet
1437      * connected. Waiting until the Audio interface gets connected means that
1438      * both headset profiles will be connected when the device module is
1439      * loaded. */
1440     if (d->audio_state == PA_BT_AUDIO_STATE_CONNECTING)
1441         return false;
1442
1443     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
1444         if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
1445             return true;
1446
1447     return false;
1448 }
1449
1450 int pa_bluetooth_transport_acquire(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
1451     DBusMessage *m, *r;
1452     DBusError err;
1453     int ret;
1454     uint16_t i, o;
1455     const char *method;
1456
1457     pa_assert(t);
1458     pa_assert(t->device);
1459     pa_assert(t->device->discovery);
1460
1461     dbus_error_init(&err);
1462
1463     if (t->device->discovery->version == BLUEZ_VERSION_4) {
1464         const char *accesstype = "rw";
1465
1466         if (optional) {
1467             /* We are trying to acquire the transport only if the stream is
1468                playing, without actually initiating the stream request from our side
1469                (which is typically undesireable specially for hfgw use-cases.
1470                However this approach is racy, since the stream could have been
1471                suspended in the meantime, so we can't really guarantee that the
1472                stream will not be requested with the API in BlueZ 4.x */
1473             if (t->state < PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) {
1474                 pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
1475                 return -1;
1476             }
1477         }
1478
1479         method = "Acquire";
1480         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", method));
1481         pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
1482     } else {
1483         pa_assert(t->device->discovery->version == BLUEZ_VERSION_5);
1484
1485         method = optional ? "TryAcquire" : "Acquire";
1486         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport1", method));
1487     }
1488
1489     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
1490
1491     if (!r) {
1492         if (optional && pa_streq(err.name, "org.bluez.Error.NotAvailable"))
1493             pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
1494         else
1495             pa_log("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
1496
1497         dbus_error_free(&err);
1498         return -1;
1499     }
1500
1501     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
1502                                DBUS_TYPE_INVALID)) {
1503         pa_log("Failed to parse the media transport Acquire() reply: %s", err.message);
1504         ret = -1;
1505         dbus_error_free(&err);
1506         goto fail;
1507     }
1508
1509     if (imtu)
1510         *imtu = i;
1511
1512     if (omtu)
1513         *omtu = o;
1514
1515 fail:
1516     dbus_message_unref(r);
1517     return ret;
1518 }
1519
1520 void pa_bluetooth_transport_release(pa_bluetooth_transport *t) {
1521     DBusMessage *m;
1522     DBusError err;
1523
1524     pa_assert(t);
1525     pa_assert(t->device);
1526     pa_assert(t->device->discovery);
1527
1528     dbus_error_init(&err);
1529
1530     if (t->device->discovery->version == BLUEZ_VERSION_4) {
1531         const char *accesstype = "rw";
1532
1533         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport", "Release"));
1534         pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &accesstype, DBUS_TYPE_INVALID));
1535     } else {
1536         pa_assert(t->device->discovery->version == BLUEZ_VERSION_5);
1537         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport1", "Release"));
1538     }
1539
1540     dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
1541
1542     if (dbus_error_is_set(&err)) {
1543         pa_log("Failed to release transport %s: %s", t->path, err.message);
1544         dbus_error_free(&err);
1545     } else
1546         pa_log_info("Transport %s released", t->path);
1547 }
1548
1549 static void set_property(pa_bluetooth_discovery *y, const char *bus, const char *path, const char *interface,
1550                          const char *prop_name, int prop_type, void *prop_value) {
1551     DBusMessage *m;
1552     DBusMessageIter i;
1553
1554     pa_assert(y);
1555     pa_assert(path);
1556     pa_assert(interface);
1557     pa_assert(prop_name);
1558
1559     pa_assert_se(m = dbus_message_new_method_call(bus, path, interface, "SetProperty"));
1560     dbus_message_iter_init_append(m, &i);
1561     dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name);
1562     pa_dbus_append_basic_variant(&i, prop_type, prop_value);
1563
1564     dbus_message_set_no_reply(m, true);
1565     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
1566     dbus_message_unref(m);
1567 }
1568
1569 void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value) {
1570     dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
1571
1572     pa_assert(t);
1573     pa_assert(t->profile == PROFILE_HSP);
1574
1575     set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
1576                  "MicrophoneGain", DBUS_TYPE_UINT16, &gain);
1577 }
1578
1579 void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value) {
1580     dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
1581
1582     pa_assert(t);
1583     pa_assert(t->profile == PROFILE_HSP);
1584
1585     set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
1586                  "SpeakerGain", DBUS_TYPE_UINT16, &gain);
1587 }
1588
1589 static int setup_dbus(pa_bluetooth_discovery *y) {
1590     DBusError err;
1591
1592     dbus_error_init(&err);
1593
1594     if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
1595         pa_log("Failed to get D-Bus connection: %s", err.message);
1596         dbus_error_free(&err);
1597         return -1;
1598     }
1599
1600     return 0;
1601 }
1602
1603 static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char *owner, const char *path, enum profile p,
1604                                              const uint8_t *config, int size) {
1605     pa_bluetooth_transport *t;
1606
1607     t = pa_xnew0(pa_bluetooth_transport, 1);
1608     t->device = d;
1609     t->owner = pa_xstrdup(owner);
1610     t->path = pa_xstrdup(path);
1611     t->profile = p;
1612     t->config_size = size;
1613
1614     if (size > 0) {
1615         t->config = pa_xnew(uint8_t, size);
1616         memcpy(t->config, config, size);
1617     }
1618
1619     if (d->discovery->version == BLUEZ_VERSION_4)
1620         t->state = audio_state_to_transport_state(d->profile_state[p]);
1621     else
1622         t->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
1623
1624     return t;
1625 }
1626
1627 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1628     pa_bluetooth_discovery *y = userdata;
1629     pa_bluetooth_device *d;
1630     pa_bluetooth_transport *t;
1631     const char *sender, *path, *dev_path = NULL, *uuid = NULL;
1632     uint8_t *config = NULL;
1633     int size = 0;
1634     bool nrec = false;
1635     enum profile p;
1636     DBusMessageIter args, props;
1637     DBusMessage *r;
1638     bool old_any_connected;
1639
1640     if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
1641         pa_log("Invalid signature for method SetConfiguration");
1642         goto fail2;
1643     }
1644
1645     dbus_message_iter_get_basic(&args, &path);
1646
1647     if (pa_hashmap_get(y->transports, path)) {
1648         pa_log("Endpoint SetConfiguration: Transport %s is already configured.", path);
1649         goto fail2;
1650     }
1651
1652     pa_assert_se(dbus_message_iter_next(&args));
1653
1654     dbus_message_iter_recurse(&args, &props);
1655     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
1656         goto fail;
1657
1658     /* Read transport properties */
1659     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
1660         const char *key;
1661         DBusMessageIter value, entry;
1662         int var;
1663
1664         dbus_message_iter_recurse(&props, &entry);
1665         dbus_message_iter_get_basic(&entry, &key);
1666
1667         dbus_message_iter_next(&entry);
1668         dbus_message_iter_recurse(&entry, &value);
1669
1670         var = dbus_message_iter_get_arg_type(&value);
1671
1672         if (strcasecmp(key, "UUID") == 0) {
1673             if (var != DBUS_TYPE_STRING)
1674                 goto fail;
1675
1676             dbus_message_iter_get_basic(&value, &uuid);
1677         } else if (strcasecmp(key, "Device") == 0) {
1678             if (var != DBUS_TYPE_OBJECT_PATH)
1679                 goto fail;
1680
1681             dbus_message_iter_get_basic(&value, &dev_path);
1682         } else if (strcasecmp(key, "NREC") == 0) {
1683             dbus_bool_t tmp_boolean;
1684             if (var != DBUS_TYPE_BOOLEAN)
1685                 goto fail;
1686
1687             dbus_message_iter_get_basic(&value, &tmp_boolean);
1688             nrec = tmp_boolean;
1689         } else if (strcasecmp(key, "Configuration") == 0) {
1690             DBusMessageIter array;
1691             if (var != DBUS_TYPE_ARRAY)
1692                 goto fail;
1693
1694             dbus_message_iter_recurse(&value, &array);
1695             dbus_message_iter_get_fixed_array(&array, &config, &size);
1696         }
1697
1698         dbus_message_iter_next(&props);
1699     }
1700
1701     d = found_device(y, dev_path);
1702     if (!d)
1703         goto fail;
1704
1705     if (dbus_message_has_path(m, HFP_AG_ENDPOINT))
1706         p = PROFILE_HSP;
1707     else if (dbus_message_has_path(m, HFP_HS_ENDPOINT))
1708         p = PROFILE_HFGW;
1709     else if (dbus_message_has_path(m, A2DP_SOURCE_ENDPOINT))
1710         p = PROFILE_A2DP;
1711     else
1712         p = PROFILE_A2DP_SOURCE;
1713
1714     if (d->transports[p] != NULL) {
1715         pa_log("Cannot configure transport %s because profile %d is already used", path, p);
1716         goto fail2;
1717     }
1718
1719     old_any_connected = pa_bluetooth_device_any_audio_connected(d);
1720
1721     sender = dbus_message_get_sender(m);
1722
1723     t = transport_new(d, sender, path, p, config, size);
1724     if (nrec)
1725         t->nrec = nrec;
1726
1727     d->transports[p] = t;
1728     pa_assert_se(pa_hashmap_put(y->transports, t->path, t) >= 0);
1729
1730     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
1731
1732     pa_assert_se(r = dbus_message_new_method_return(m));
1733     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1734     dbus_message_unref(r);
1735
1736     if (old_any_connected != pa_bluetooth_device_any_audio_connected(d))
1737         run_callback(d, false);
1738
1739     return NULL;
1740
1741 fail:
1742     pa_log("Endpoint SetConfiguration: invalid arguments");
1743
1744 fail2:
1745     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
1746     return r;
1747 }
1748
1749 static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
1750     pa_bluetooth_discovery *y = userdata;
1751     pa_bluetooth_transport *t;
1752     DBusMessage *r;
1753     DBusError e;
1754     const char *path;
1755
1756     dbus_error_init(&e);
1757
1758     if (!dbus_message_get_args(m, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1759         pa_log("Endpoint ClearConfiguration: %s", e.message);
1760         dbus_error_free(&e);
1761         goto fail;
1762     }
1763
1764     if ((t = pa_hashmap_get(y->transports, path))) {
1765         bool old_any_connected = pa_bluetooth_device_any_audio_connected(t->device);
1766
1767         pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
1768         t->device->transports[t->profile] = NULL;
1769         pa_hashmap_remove(y->transports, t->path);
1770         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
1771         pa_hook_fire(&y->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
1772
1773         if (old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
1774             run_callback(t->device, false);
1775
1776         transport_free(t);
1777     }
1778
1779     pa_assert_se(r = dbus_message_new_method_return(m));
1780
1781     return r;
1782
1783 fail:
1784     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
1785     return r;
1786 }
1787
1788 static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
1789
1790     switch (freq) {
1791         case SBC_SAMPLING_FREQ_16000:
1792         case SBC_SAMPLING_FREQ_32000:
1793             return 53;
1794
1795         case SBC_SAMPLING_FREQ_44100:
1796
1797             switch (mode) {
1798                 case SBC_CHANNEL_MODE_MONO:
1799                 case SBC_CHANNEL_MODE_DUAL_CHANNEL:
1800                     return 31;
1801
1802                 case SBC_CHANNEL_MODE_STEREO:
1803                 case SBC_CHANNEL_MODE_JOINT_STEREO:
1804                     return 53;
1805
1806                 default:
1807                     pa_log_warn("Invalid channel mode %u", mode);
1808                     return 53;
1809             }
1810
1811         case SBC_SAMPLING_FREQ_48000:
1812
1813             switch (mode) {
1814                 case SBC_CHANNEL_MODE_MONO:
1815                 case SBC_CHANNEL_MODE_DUAL_CHANNEL:
1816                     return 29;
1817
1818                 case SBC_CHANNEL_MODE_STEREO:
1819                 case SBC_CHANNEL_MODE_JOINT_STEREO:
1820                     return 51;
1821
1822                 default:
1823                     pa_log_warn("Invalid channel mode %u", mode);
1824                     return 51;
1825             }
1826
1827         default:
1828             pa_log_warn("Invalid sampling freq %u", freq);
1829             return 53;
1830     }
1831 }
1832
1833 static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
1834     pa_bluetooth_discovery *y = userdata;
1835     a2dp_sbc_t *cap, config;
1836     uint8_t *pconf = (uint8_t *) &config;
1837     int i, size;
1838     DBusMessage *r;
1839     DBusError e;
1840
1841     static const struct {
1842         uint32_t rate;
1843         uint8_t cap;
1844     } freq_table[] = {
1845         { 16000U, SBC_SAMPLING_FREQ_16000 },
1846         { 32000U, SBC_SAMPLING_FREQ_32000 },
1847         { 44100U, SBC_SAMPLING_FREQ_44100 },
1848         { 48000U, SBC_SAMPLING_FREQ_48000 }
1849     };
1850
1851     dbus_error_init(&e);
1852
1853     if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
1854         pa_log("Endpoint SelectConfiguration: %s", e.message);
1855         dbus_error_free(&e);
1856         goto fail;
1857     }
1858
1859     if (dbus_message_has_path(m, HFP_AG_ENDPOINT) || dbus_message_has_path(m, HFP_HS_ENDPOINT))
1860         goto done;
1861
1862     pa_assert(size == sizeof(config));
1863
1864     memset(&config, 0, sizeof(config));
1865
1866     /* Find the lowest freq that is at least as high as the requested
1867      * sampling rate */
1868     for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
1869         if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
1870             config.frequency = freq_table[i].cap;
1871             break;
1872         }
1873
1874     if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
1875         for (--i; i >= 0; i--) {
1876             if (cap->frequency & freq_table[i].cap) {
1877                 config.frequency = freq_table[i].cap;
1878                 break;
1879             }
1880         }
1881
1882         if (i < 0) {
1883             pa_log("Not suitable sample rate");
1884             goto fail;
1885         }
1886     }
1887
1888     pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
1889
1890     if (y->core->default_sample_spec.channels <= 1) {
1891         if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
1892             config.channel_mode = SBC_CHANNEL_MODE_MONO;
1893     }
1894
1895     if (y->core->default_sample_spec.channels >= 2) {
1896         if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
1897             config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
1898         else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
1899             config.channel_mode = SBC_CHANNEL_MODE_STEREO;
1900         else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
1901             config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
1902         else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) {
1903             config.channel_mode = SBC_CHANNEL_MODE_MONO;
1904         } else {
1905             pa_log("No supported channel modes");
1906             goto fail;
1907         }
1908     }
1909
1910     if (cap->block_length & SBC_BLOCK_LENGTH_16)
1911         config.block_length = SBC_BLOCK_LENGTH_16;
1912     else if (cap->block_length & SBC_BLOCK_LENGTH_12)
1913         config.block_length = SBC_BLOCK_LENGTH_12;
1914     else if (cap->block_length & SBC_BLOCK_LENGTH_8)
1915         config.block_length = SBC_BLOCK_LENGTH_8;
1916     else if (cap->block_length & SBC_BLOCK_LENGTH_4)
1917         config.block_length = SBC_BLOCK_LENGTH_4;
1918     else {
1919         pa_log_error("No supported block lengths");
1920         goto fail;
1921     }
1922
1923     if (cap->subbands & SBC_SUBBANDS_8)
1924         config.subbands = SBC_SUBBANDS_8;
1925     else if (cap->subbands & SBC_SUBBANDS_4)
1926         config.subbands = SBC_SUBBANDS_4;
1927     else {
1928         pa_log_error("No supported subbands");
1929         goto fail;
1930     }
1931
1932     if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
1933         config.allocation_method = SBC_ALLOCATION_LOUDNESS;
1934     else if (cap->allocation_method & SBC_ALLOCATION_SNR)
1935         config.allocation_method = SBC_ALLOCATION_SNR;
1936
1937     config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
1938     config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
1939
1940 done:
1941     pa_assert_se(r = dbus_message_new_method_return(m));
1942
1943     pa_assert_se(dbus_message_append_args(
1944                                      r,
1945                                      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size,
1946                                      DBUS_TYPE_INVALID));
1947
1948     return r;
1949
1950 fail:
1951     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to select configuration"));
1952     return r;
1953 }
1954
1955 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1956     struct pa_bluetooth_discovery *y = userdata;
1957     DBusMessage *r = NULL;
1958     DBusError e;
1959     const char *path, *interface, *member;
1960
1961     pa_assert(y);
1962
1963     path = dbus_message_get_path(m);
1964     interface = dbus_message_get_interface(m);
1965     member = dbus_message_get_member(m);
1966
1967     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
1968
1969     dbus_error_init(&e);
1970
1971     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, HFP_AG_ENDPOINT) &&
1972         !pa_streq(path, HFP_HS_ENDPOINT))
1973         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1974
1975     interface = y->version == BLUEZ_VERSION_4 ? "org.bluez.MediaEndpoint" : "org.bluez.MediaEndpoint1";
1976
1977     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1978         const char *xml = y->version == BLUEZ_VERSION_4 ? ENDPOINT_INTROSPECT_XML : MEDIA_ENDPOINT_1_INTROSPECT_XML;
1979
1980         pa_assert_se(r = dbus_message_new_method_return(m));
1981         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
1982
1983     } else if (dbus_message_is_method_call(m, interface, "SetConfiguration"))
1984         r = endpoint_set_configuration(c, m, userdata);
1985     else if (dbus_message_is_method_call(m, interface, "SelectConfiguration"))
1986         r = endpoint_select_configuration(c, m, userdata);
1987     else if (dbus_message_is_method_call(m, interface, "ClearConfiguration"))
1988         r = endpoint_clear_configuration(c, m, userdata);
1989     else
1990         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1991
1992     if (r) {
1993         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1994         dbus_message_unref(r);
1995     }
1996
1997     return DBUS_HANDLER_RESULT_HANDLED;
1998 }
1999
2000 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
2001     DBusError err;
2002     pa_bluetooth_discovery *y;
2003     DBusConnection *conn;
2004     unsigned i;
2005     static const DBusObjectPathVTable vtable_endpoint = {
2006         .message_function = endpoint_handler,
2007     };
2008
2009     pa_assert(c);
2010
2011     dbus_error_init(&err);
2012
2013     if ((y = pa_shared_get(c, "bluetooth-discovery")))
2014         return pa_bluetooth_discovery_ref(y);
2015
2016     y = pa_xnew0(pa_bluetooth_discovery, 1);
2017     PA_REFCNT_INIT(y);
2018     y->core = c;
2019     y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2020     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2021     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2022
2023     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2024         pa_hook_init(&y->hooks[i], y);
2025
2026     pa_shared_set(c, "bluetooth-discovery", y);
2027
2028     if (setup_dbus(y) < 0)
2029         goto fail;
2030
2031     conn = pa_dbus_connection_get(y->connection);
2032
2033     /* dynamic detection of bluetooth audio devices */
2034     if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2035         pa_log_error("Failed to add filter function");
2036         goto fail;
2037     }
2038
2039     y->filter_added = true;
2040
2041     if (pa_dbus_add_matches(
2042                 conn, &err,
2043                 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
2044                 ",arg0='org.bluez'",
2045                 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
2046                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
2047                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
2048                 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
2049                 "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
2050                 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
2051                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
2052                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
2053                 "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
2054                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
2055                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
2056                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
2057                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2058                 ",arg0='org.bluez.Device1'",
2059                 "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2060                 ",arg0='org.bluez.MediaTransport1'",
2061                 NULL) < 0) {
2062         pa_log("Failed to add D-Bus matches: %s", err.message);
2063         goto fail;
2064     }
2065
2066     pa_assert_se(dbus_connection_register_object_path(conn, HFP_AG_ENDPOINT, &vtable_endpoint, y));
2067     pa_assert_se(dbus_connection_register_object_path(conn, HFP_HS_ENDPOINT, &vtable_endpoint, y));
2068     pa_assert_se(dbus_connection_register_object_path(conn, A2DP_SOURCE_ENDPOINT, &vtable_endpoint, y));
2069     pa_assert_se(dbus_connection_register_object_path(conn, A2DP_SINK_ENDPOINT, &vtable_endpoint, y));
2070
2071     init_bluez(y);
2072
2073     return y;
2074
2075 fail:
2076     if (y)
2077         pa_bluetooth_discovery_unref(y);
2078
2079     dbus_error_free(&err);
2080
2081     return NULL;
2082 }
2083
2084 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2085     pa_assert(y);
2086     pa_assert(PA_REFCNT_VALUE(y) > 0);
2087
2088     PA_REFCNT_INC(y);
2089
2090     return y;
2091 }
2092
2093 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2094     unsigned i;
2095
2096     pa_assert(y);
2097     pa_assert(PA_REFCNT_VALUE(y) > 0);
2098
2099     if (PA_REFCNT_DEC(y) > 0)
2100         return;
2101
2102     pa_dbus_free_pending_list(&y->pending);
2103
2104     if (y->devices) {
2105         remove_all_devices(y);
2106         pa_hashmap_free(y->devices);
2107     }
2108
2109     if (y->transports) {
2110         pa_assert(pa_hashmap_isempty(y->transports));
2111         pa_hashmap_free(y->transports);
2112     }
2113
2114     if (y->connection) {
2115         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT);
2116         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_HS_ENDPOINT);
2117         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
2118         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
2119         pa_dbus_remove_matches(
2120             pa_dbus_connection_get(y->connection),
2121             "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
2122             ",arg0='org.bluez'",
2123             "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
2124             "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
2125             "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
2126             "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
2127             "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
2128             "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
2129             "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
2130             "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
2131             "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
2132             "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
2133             "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
2134             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
2135             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
2136             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2137             ",arg0='org.bluez.Device1'",
2138             "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2139             ",arg0='org.bluez.MediaTransport1'",
2140             NULL);
2141
2142         if (y->filter_added)
2143             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
2144
2145         pa_dbus_connection_unref(y->connection);
2146     }
2147
2148     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2149         pa_hook_done(&y->hooks[i]);
2150
2151     if (y->core)
2152         pa_shared_remove(y->core, "bluetooth-discovery");
2153
2154     pa_xfree(y);
2155 }
2156
2157 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
2158     pa_assert(y);
2159     pa_assert(PA_REFCNT_VALUE(y) > 0);
2160
2161     return &y->hooks[hook];
2162 }
2163
2164 pa_bt_form_factor_t pa_bluetooth_get_form_factor(uint32_t class) {
2165     unsigned major, minor;
2166     pa_bt_form_factor_t r;
2167
2168     static const pa_bt_form_factor_t table[] = {
2169         [1] = PA_BT_FORM_FACTOR_HEADSET,
2170         [2] = PA_BT_FORM_FACTOR_HANDSFREE,
2171         [4] = PA_BT_FORM_FACTOR_MICROPHONE,
2172         [5] = PA_BT_FORM_FACTOR_SPEAKER,
2173         [6] = PA_BT_FORM_FACTOR_HEADPHONE,
2174         [7] = PA_BT_FORM_FACTOR_PORTABLE,
2175         [8] = PA_BT_FORM_FACTOR_CAR,
2176         [10] = PA_BT_FORM_FACTOR_HIFI
2177     };
2178
2179     /*
2180      * See Bluetooth Assigned Numbers:
2181      * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
2182      */
2183     major = (class >> 8) & 0x1F;
2184     minor = (class >> 2) & 0x3F;
2185
2186     switch (major) {
2187         case 2:
2188             return PA_BT_FORM_FACTOR_PHONE;
2189         case 4:
2190             break;
2191         default:
2192             pa_log_debug("Unknown Bluetooth major device class %u", major);
2193             return PA_BT_FORM_FACTOR_UNKNOWN;
2194     }
2195
2196     r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BT_FORM_FACTOR_UNKNOWN;
2197
2198     if (!r)
2199         pa_log_debug("Unknown Bluetooth minor device class %u", minor);
2200
2201     return r;
2202 }
2203
2204 const char *pa_bt_form_factor_to_string(pa_bt_form_factor_t ff) {
2205     switch (ff) {
2206         case PA_BT_FORM_FACTOR_UNKNOWN:
2207             return "unknown";
2208         case PA_BT_FORM_FACTOR_HEADSET:
2209             return "headset";
2210         case PA_BT_FORM_FACTOR_HANDSFREE:
2211             return "hands-free";
2212         case PA_BT_FORM_FACTOR_MICROPHONE:
2213             return "microphone";
2214         case PA_BT_FORM_FACTOR_SPEAKER:
2215             return "speaker";
2216         case PA_BT_FORM_FACTOR_HEADPHONE:
2217             return "headphone";
2218         case PA_BT_FORM_FACTOR_PORTABLE:
2219             return "portable";
2220         case PA_BT_FORM_FACTOR_CAR:
2221             return "car";
2222         case PA_BT_FORM_FACTOR_HIFI:
2223             return "hifi";
2224         case PA_BT_FORM_FACTOR_PHONE:
2225             return "phone";
2226     }
2227
2228     pa_assert_not_reached();
2229 }
2230
2231 char *pa_bluetooth_cleanup_name(const char *name) {
2232     char *t, *s, *d;
2233     bool space = false;
2234
2235     pa_assert(name);
2236
2237     while ((*name >= 1 && *name <= 32) || *name >= 127)
2238         name++;
2239
2240     t = pa_xstrdup(name);
2241
2242     for (s = d = t; *s; s++) {
2243
2244         if (*s <= 32 || *s >= 127 || *s == '_') {
2245             space = true;
2246             continue;
2247         }
2248
2249         if (space) {
2250             *(d++) = ' ';
2251             space = false;
2252         }
2253
2254         *(d++) = *s;
2255     }
2256
2257     *d = 0;
2258
2259     return t;
2260 }
2261
2262 bool pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid) {
2263     pa_assert(uuid);
2264
2265     while (uuids) {
2266         if (strcasecmp(uuids->uuid, uuid) == 0)
2267             return true;
2268
2269         uuids = uuids->next;
2270     }
2271
2272     return false;
2273 }