hashmap: Add the ability to free keys
[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
1538         if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
1539             pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
1540             return;
1541         }
1542
1543         pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, "org.bluez.MediaTransport1", "Release"));
1544     }
1545
1546     dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
1547
1548     if (dbus_error_is_set(&err)) {
1549         pa_log("Failed to release transport %s: %s", t->path, err.message);
1550         dbus_error_free(&err);
1551     } else
1552         pa_log_info("Transport %s released", t->path);
1553 }
1554
1555 static void set_property(pa_bluetooth_discovery *y, const char *bus, const char *path, const char *interface,
1556                          const char *prop_name, int prop_type, void *prop_value) {
1557     DBusMessage *m;
1558     DBusMessageIter i;
1559
1560     pa_assert(y);
1561     pa_assert(path);
1562     pa_assert(interface);
1563     pa_assert(prop_name);
1564
1565     pa_assert_se(m = dbus_message_new_method_call(bus, path, interface, "SetProperty"));
1566     dbus_message_iter_init_append(m, &i);
1567     dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &prop_name);
1568     pa_dbus_append_basic_variant(&i, prop_type, prop_value);
1569
1570     dbus_message_set_no_reply(m, true);
1571     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
1572     dbus_message_unref(m);
1573 }
1574
1575 void pa_bluetooth_transport_set_microphone_gain(pa_bluetooth_transport *t, uint16_t value) {
1576     dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
1577
1578     pa_assert(t);
1579     pa_assert(t->profile == PROFILE_HSP);
1580
1581     set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
1582                  "MicrophoneGain", DBUS_TYPE_UINT16, &gain);
1583 }
1584
1585 void pa_bluetooth_transport_set_speaker_gain(pa_bluetooth_transport *t, uint16_t value) {
1586     dbus_uint16_t gain = PA_MIN(value, HSP_MAX_GAIN);
1587
1588     pa_assert(t);
1589     pa_assert(t->profile == PROFILE_HSP);
1590
1591     set_property(t->device->discovery, "org.bluez", t->device->path, "org.bluez.Headset",
1592                  "SpeakerGain", DBUS_TYPE_UINT16, &gain);
1593 }
1594
1595 static int setup_dbus(pa_bluetooth_discovery *y) {
1596     DBusError err;
1597
1598     dbus_error_init(&err);
1599
1600     if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
1601         pa_log("Failed to get D-Bus connection: %s", err.message);
1602         dbus_error_free(&err);
1603         return -1;
1604     }
1605
1606     return 0;
1607 }
1608
1609 static pa_bluetooth_transport *transport_new(pa_bluetooth_device *d, const char *owner, const char *path, enum profile p,
1610                                              const uint8_t *config, int size) {
1611     pa_bluetooth_transport *t;
1612
1613     t = pa_xnew0(pa_bluetooth_transport, 1);
1614     t->device = d;
1615     t->owner = pa_xstrdup(owner);
1616     t->path = pa_xstrdup(path);
1617     t->profile = p;
1618     t->config_size = size;
1619
1620     if (size > 0) {
1621         t->config = pa_xnew(uint8_t, size);
1622         memcpy(t->config, config, size);
1623     }
1624
1625     if (d->discovery->version == BLUEZ_VERSION_4)
1626         t->state = audio_state_to_transport_state(d->profile_state[p]);
1627     else
1628         t->state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
1629
1630     return t;
1631 }
1632
1633 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1634     pa_bluetooth_discovery *y = userdata;
1635     pa_bluetooth_device *d;
1636     pa_bluetooth_transport *t;
1637     const char *sender, *path, *dev_path = NULL, *uuid = NULL;
1638     uint8_t *config = NULL;
1639     int size = 0;
1640     bool nrec = false;
1641     enum profile p;
1642     DBusMessageIter args, props;
1643     DBusMessage *r;
1644     bool old_any_connected;
1645
1646     if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
1647         pa_log("Invalid signature for method SetConfiguration");
1648         goto fail2;
1649     }
1650
1651     dbus_message_iter_get_basic(&args, &path);
1652
1653     if (pa_hashmap_get(y->transports, path)) {
1654         pa_log("Endpoint SetConfiguration: Transport %s is already configured.", path);
1655         goto fail2;
1656     }
1657
1658     pa_assert_se(dbus_message_iter_next(&args));
1659
1660     dbus_message_iter_recurse(&args, &props);
1661     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
1662         goto fail;
1663
1664     /* Read transport properties */
1665     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
1666         const char *key;
1667         DBusMessageIter value, entry;
1668         int var;
1669
1670         dbus_message_iter_recurse(&props, &entry);
1671         dbus_message_iter_get_basic(&entry, &key);
1672
1673         dbus_message_iter_next(&entry);
1674         dbus_message_iter_recurse(&entry, &value);
1675
1676         var = dbus_message_iter_get_arg_type(&value);
1677
1678         if (strcasecmp(key, "UUID") == 0) {
1679             if (var != DBUS_TYPE_STRING)
1680                 goto fail;
1681
1682             dbus_message_iter_get_basic(&value, &uuid);
1683         } else if (strcasecmp(key, "Device") == 0) {
1684             if (var != DBUS_TYPE_OBJECT_PATH)
1685                 goto fail;
1686
1687             dbus_message_iter_get_basic(&value, &dev_path);
1688         } else if (strcasecmp(key, "NREC") == 0) {
1689             dbus_bool_t tmp_boolean;
1690             if (var != DBUS_TYPE_BOOLEAN)
1691                 goto fail;
1692
1693             dbus_message_iter_get_basic(&value, &tmp_boolean);
1694             nrec = tmp_boolean;
1695         } else if (strcasecmp(key, "Configuration") == 0) {
1696             DBusMessageIter array;
1697             if (var != DBUS_TYPE_ARRAY)
1698                 goto fail;
1699
1700             dbus_message_iter_recurse(&value, &array);
1701             dbus_message_iter_get_fixed_array(&array, &config, &size);
1702         }
1703
1704         dbus_message_iter_next(&props);
1705     }
1706
1707     d = found_device(y, dev_path);
1708     if (!d)
1709         goto fail;
1710
1711     if (dbus_message_has_path(m, HFP_AG_ENDPOINT))
1712         p = PROFILE_HSP;
1713     else if (dbus_message_has_path(m, HFP_HS_ENDPOINT))
1714         p = PROFILE_HFGW;
1715     else if (dbus_message_has_path(m, A2DP_SOURCE_ENDPOINT))
1716         p = PROFILE_A2DP;
1717     else
1718         p = PROFILE_A2DP_SOURCE;
1719
1720     if (d->transports[p] != NULL) {
1721         pa_log("Cannot configure transport %s because profile %d is already used", path, p);
1722         goto fail2;
1723     }
1724
1725     old_any_connected = pa_bluetooth_device_any_audio_connected(d);
1726
1727     sender = dbus_message_get_sender(m);
1728
1729     t = transport_new(d, sender, path, p, config, size);
1730     if (nrec)
1731         t->nrec = nrec;
1732
1733     d->transports[p] = t;
1734     pa_assert_se(pa_hashmap_put(y->transports, t->path, t) >= 0);
1735
1736     pa_log_debug("Transport %s profile %d available", t->path, t->profile);
1737
1738     pa_assert_se(r = dbus_message_new_method_return(m));
1739     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1740     dbus_message_unref(r);
1741
1742     if (old_any_connected != pa_bluetooth_device_any_audio_connected(d))
1743         run_callback(d, false);
1744
1745     return NULL;
1746
1747 fail:
1748     pa_log("Endpoint SetConfiguration: invalid arguments");
1749
1750 fail2:
1751     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
1752     return r;
1753 }
1754
1755 static DBusMessage *endpoint_clear_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
1756     pa_bluetooth_discovery *y = userdata;
1757     pa_bluetooth_transport *t;
1758     DBusMessage *r;
1759     DBusError e;
1760     const char *path;
1761
1762     dbus_error_init(&e);
1763
1764     if (!dbus_message_get_args(m, &e, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1765         pa_log("Endpoint ClearConfiguration: %s", e.message);
1766         dbus_error_free(&e);
1767         goto fail;
1768     }
1769
1770     if ((t = pa_hashmap_get(y->transports, path))) {
1771         bool old_any_connected = pa_bluetooth_device_any_audio_connected(t->device);
1772
1773         pa_log_debug("Clearing transport %s profile %d", t->path, t->profile);
1774         t->device->transports[t->profile] = NULL;
1775         pa_hashmap_remove(y->transports, t->path);
1776         t->state = PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED;
1777         pa_hook_fire(&y->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
1778
1779         if (old_any_connected != pa_bluetooth_device_any_audio_connected(t->device))
1780             run_callback(t->device, false);
1781
1782         transport_free(t);
1783     }
1784
1785     pa_assert_se(r = dbus_message_new_method_return(m));
1786
1787     return r;
1788
1789 fail:
1790     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
1791     return r;
1792 }
1793
1794 static uint8_t a2dp_default_bitpool(uint8_t freq, uint8_t mode) {
1795
1796     switch (freq) {
1797         case SBC_SAMPLING_FREQ_16000:
1798         case SBC_SAMPLING_FREQ_32000:
1799             return 53;
1800
1801         case SBC_SAMPLING_FREQ_44100:
1802
1803             switch (mode) {
1804                 case SBC_CHANNEL_MODE_MONO:
1805                 case SBC_CHANNEL_MODE_DUAL_CHANNEL:
1806                     return 31;
1807
1808                 case SBC_CHANNEL_MODE_STEREO:
1809                 case SBC_CHANNEL_MODE_JOINT_STEREO:
1810                     return 53;
1811
1812                 default:
1813                     pa_log_warn("Invalid channel mode %u", mode);
1814                     return 53;
1815             }
1816
1817         case SBC_SAMPLING_FREQ_48000:
1818
1819             switch (mode) {
1820                 case SBC_CHANNEL_MODE_MONO:
1821                 case SBC_CHANNEL_MODE_DUAL_CHANNEL:
1822                     return 29;
1823
1824                 case SBC_CHANNEL_MODE_STEREO:
1825                 case SBC_CHANNEL_MODE_JOINT_STEREO:
1826                     return 51;
1827
1828                 default:
1829                     pa_log_warn("Invalid channel mode %u", mode);
1830                     return 51;
1831             }
1832
1833         default:
1834             pa_log_warn("Invalid sampling freq %u", freq);
1835             return 53;
1836     }
1837 }
1838
1839 static DBusMessage *endpoint_select_configuration(DBusConnection *c, DBusMessage *m, void *userdata) {
1840     pa_bluetooth_discovery *y = userdata;
1841     a2dp_sbc_t *cap, config;
1842     uint8_t *pconf = (uint8_t *) &config;
1843     int i, size;
1844     DBusMessage *r;
1845     DBusError e;
1846
1847     static const struct {
1848         uint32_t rate;
1849         uint8_t cap;
1850     } freq_table[] = {
1851         { 16000U, SBC_SAMPLING_FREQ_16000 },
1852         { 32000U, SBC_SAMPLING_FREQ_32000 },
1853         { 44100U, SBC_SAMPLING_FREQ_44100 },
1854         { 48000U, SBC_SAMPLING_FREQ_48000 }
1855     };
1856
1857     dbus_error_init(&e);
1858
1859     if (!dbus_message_get_args(m, &e, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
1860         pa_log("Endpoint SelectConfiguration: %s", e.message);
1861         dbus_error_free(&e);
1862         goto fail;
1863     }
1864
1865     if (dbus_message_has_path(m, HFP_AG_ENDPOINT) || dbus_message_has_path(m, HFP_HS_ENDPOINT))
1866         goto done;
1867
1868     pa_assert(size == sizeof(config));
1869
1870     memset(&config, 0, sizeof(config));
1871
1872     /* Find the lowest freq that is at least as high as the requested
1873      * sampling rate */
1874     for (i = 0; (unsigned) i < PA_ELEMENTSOF(freq_table); i++)
1875         if (freq_table[i].rate >= y->core->default_sample_spec.rate && (cap->frequency & freq_table[i].cap)) {
1876             config.frequency = freq_table[i].cap;
1877             break;
1878         }
1879
1880     if ((unsigned) i == PA_ELEMENTSOF(freq_table)) {
1881         for (--i; i >= 0; i--) {
1882             if (cap->frequency & freq_table[i].cap) {
1883                 config.frequency = freq_table[i].cap;
1884                 break;
1885             }
1886         }
1887
1888         if (i < 0) {
1889             pa_log("Not suitable sample rate");
1890             goto fail;
1891         }
1892     }
1893
1894     pa_assert((unsigned) i < PA_ELEMENTSOF(freq_table));
1895
1896     if (y->core->default_sample_spec.channels <= 1) {
1897         if (cap->channel_mode & SBC_CHANNEL_MODE_MONO)
1898             config.channel_mode = SBC_CHANNEL_MODE_MONO;
1899     }
1900
1901     if (y->core->default_sample_spec.channels >= 2) {
1902         if (cap->channel_mode & SBC_CHANNEL_MODE_JOINT_STEREO)
1903             config.channel_mode = SBC_CHANNEL_MODE_JOINT_STEREO;
1904         else if (cap->channel_mode & SBC_CHANNEL_MODE_STEREO)
1905             config.channel_mode = SBC_CHANNEL_MODE_STEREO;
1906         else if (cap->channel_mode & SBC_CHANNEL_MODE_DUAL_CHANNEL)
1907             config.channel_mode = SBC_CHANNEL_MODE_DUAL_CHANNEL;
1908         else if (cap->channel_mode & SBC_CHANNEL_MODE_MONO) {
1909             config.channel_mode = SBC_CHANNEL_MODE_MONO;
1910         } else {
1911             pa_log("No supported channel modes");
1912             goto fail;
1913         }
1914     }
1915
1916     if (cap->block_length & SBC_BLOCK_LENGTH_16)
1917         config.block_length = SBC_BLOCK_LENGTH_16;
1918     else if (cap->block_length & SBC_BLOCK_LENGTH_12)
1919         config.block_length = SBC_BLOCK_LENGTH_12;
1920     else if (cap->block_length & SBC_BLOCK_LENGTH_8)
1921         config.block_length = SBC_BLOCK_LENGTH_8;
1922     else if (cap->block_length & SBC_BLOCK_LENGTH_4)
1923         config.block_length = SBC_BLOCK_LENGTH_4;
1924     else {
1925         pa_log_error("No supported block lengths");
1926         goto fail;
1927     }
1928
1929     if (cap->subbands & SBC_SUBBANDS_8)
1930         config.subbands = SBC_SUBBANDS_8;
1931     else if (cap->subbands & SBC_SUBBANDS_4)
1932         config.subbands = SBC_SUBBANDS_4;
1933     else {
1934         pa_log_error("No supported subbands");
1935         goto fail;
1936     }
1937
1938     if (cap->allocation_method & SBC_ALLOCATION_LOUDNESS)
1939         config.allocation_method = SBC_ALLOCATION_LOUDNESS;
1940     else if (cap->allocation_method & SBC_ALLOCATION_SNR)
1941         config.allocation_method = SBC_ALLOCATION_SNR;
1942
1943     config.min_bitpool = (uint8_t) PA_MAX(MIN_BITPOOL, cap->min_bitpool);
1944     config.max_bitpool = (uint8_t) PA_MIN(a2dp_default_bitpool(config.frequency, config.channel_mode), cap->max_bitpool);
1945
1946 done:
1947     pa_assert_se(r = dbus_message_new_method_return(m));
1948
1949     pa_assert_se(dbus_message_append_args(
1950                                      r,
1951                                      DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &pconf, size,
1952                                      DBUS_TYPE_INVALID));
1953
1954     return r;
1955
1956 fail:
1957     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to select configuration"));
1958     return r;
1959 }
1960
1961 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1962     struct pa_bluetooth_discovery *y = userdata;
1963     DBusMessage *r = NULL;
1964     DBusError e;
1965     const char *path, *interface, *member;
1966
1967     pa_assert(y);
1968
1969     path = dbus_message_get_path(m);
1970     interface = dbus_message_get_interface(m);
1971     member = dbus_message_get_member(m);
1972
1973     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
1974
1975     dbus_error_init(&e);
1976
1977     if (!pa_streq(path, A2DP_SOURCE_ENDPOINT) && !pa_streq(path, A2DP_SINK_ENDPOINT) && !pa_streq(path, HFP_AG_ENDPOINT) &&
1978         !pa_streq(path, HFP_HS_ENDPOINT))
1979         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1980
1981     interface = y->version == BLUEZ_VERSION_4 ? "org.bluez.MediaEndpoint" : "org.bluez.MediaEndpoint1";
1982
1983     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1984         const char *xml = y->version == BLUEZ_VERSION_4 ? ENDPOINT_INTROSPECT_XML : MEDIA_ENDPOINT_1_INTROSPECT_XML;
1985
1986         pa_assert_se(r = dbus_message_new_method_return(m));
1987         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
1988
1989     } else if (dbus_message_is_method_call(m, interface, "SetConfiguration"))
1990         r = endpoint_set_configuration(c, m, userdata);
1991     else if (dbus_message_is_method_call(m, interface, "SelectConfiguration"))
1992         r = endpoint_select_configuration(c, m, userdata);
1993     else if (dbus_message_is_method_call(m, interface, "ClearConfiguration"))
1994         r = endpoint_clear_configuration(c, m, userdata);
1995     else
1996         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1997
1998     if (r) {
1999         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2000         dbus_message_unref(r);
2001     }
2002
2003     return DBUS_HANDLER_RESULT_HANDLED;
2004 }
2005
2006 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c) {
2007     DBusError err;
2008     pa_bluetooth_discovery *y;
2009     DBusConnection *conn;
2010     unsigned i;
2011     static const DBusObjectPathVTable vtable_endpoint = {
2012         .message_function = endpoint_handler,
2013     };
2014
2015     pa_assert(c);
2016
2017     dbus_error_init(&err);
2018
2019     if ((y = pa_shared_get(c, "bluetooth-discovery")))
2020         return pa_bluetooth_discovery_ref(y);
2021
2022     y = pa_xnew0(pa_bluetooth_discovery, 1);
2023     PA_REFCNT_INIT(y);
2024     y->core = c;
2025     y->devices = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2026     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2027     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2028
2029     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2030         pa_hook_init(&y->hooks[i], y);
2031
2032     pa_shared_set(c, "bluetooth-discovery", y);
2033
2034     if (setup_dbus(y) < 0)
2035         goto fail;
2036
2037     conn = pa_dbus_connection_get(y->connection);
2038
2039     /* dynamic detection of bluetooth audio devices */
2040     if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2041         pa_log_error("Failed to add filter function");
2042         goto fail;
2043     }
2044
2045     y->filter_added = true;
2046
2047     if (pa_dbus_add_matches(
2048                 conn, &err,
2049                 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
2050                 ",arg0='org.bluez'",
2051                 "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
2052                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
2053                 "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
2054                 "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
2055                 "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
2056                 "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
2057                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
2058                 "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
2059                 "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
2060                 "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
2061                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
2062                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
2063                 "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2064                 ",arg0='org.bluez.Device1'",
2065                 "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2066                 ",arg0='org.bluez.MediaTransport1'",
2067                 NULL) < 0) {
2068         pa_log("Failed to add D-Bus matches: %s", err.message);
2069         goto fail;
2070     }
2071
2072     pa_assert_se(dbus_connection_register_object_path(conn, HFP_AG_ENDPOINT, &vtable_endpoint, y));
2073     pa_assert_se(dbus_connection_register_object_path(conn, HFP_HS_ENDPOINT, &vtable_endpoint, y));
2074     pa_assert_se(dbus_connection_register_object_path(conn, A2DP_SOURCE_ENDPOINT, &vtable_endpoint, y));
2075     pa_assert_se(dbus_connection_register_object_path(conn, A2DP_SINK_ENDPOINT, &vtable_endpoint, y));
2076
2077     init_bluez(y);
2078
2079     return y;
2080
2081 fail:
2082     if (y)
2083         pa_bluetooth_discovery_unref(y);
2084
2085     dbus_error_free(&err);
2086
2087     return NULL;
2088 }
2089
2090 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2091     pa_assert(y);
2092     pa_assert(PA_REFCNT_VALUE(y) > 0);
2093
2094     PA_REFCNT_INC(y);
2095
2096     return y;
2097 }
2098
2099 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2100     unsigned i;
2101
2102     pa_assert(y);
2103     pa_assert(PA_REFCNT_VALUE(y) > 0);
2104
2105     if (PA_REFCNT_DEC(y) > 0)
2106         return;
2107
2108     pa_dbus_free_pending_list(&y->pending);
2109
2110     if (y->devices) {
2111         remove_all_devices(y);
2112         pa_hashmap_free(y->devices);
2113     }
2114
2115     if (y->transports) {
2116         pa_assert(pa_hashmap_isempty(y->transports));
2117         pa_hashmap_free(y->transports);
2118     }
2119
2120     if (y->connection) {
2121         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_AG_ENDPOINT);
2122         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), HFP_HS_ENDPOINT);
2123         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SOURCE_ENDPOINT);
2124         dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), A2DP_SINK_ENDPOINT);
2125         pa_dbus_remove_matches(
2126             pa_dbus_connection_get(y->connection),
2127             "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
2128             ",arg0='org.bluez'",
2129             "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterAdded'",
2130             "type='signal',sender='org.bluez',interface='org.bluez.Manager',member='AdapterRemoved'",
2131             "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceRemoved'",
2132             "type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='DeviceCreated'",
2133             "type='signal',sender='org.bluez',interface='org.bluez.Device',member='PropertyChanged'",
2134             "type='signal',sender='org.bluez',interface='org.bluez.Audio',member='PropertyChanged'",
2135             "type='signal',sender='org.bluez',interface='org.bluez.Headset',member='PropertyChanged'",
2136             "type='signal',sender='org.bluez',interface='org.bluez.AudioSink',member='PropertyChanged'",
2137             "type='signal',sender='org.bluez',interface='org.bluez.AudioSource',member='PropertyChanged'",
2138             "type='signal',sender='org.bluez',interface='org.bluez.HandsfreeGateway',member='PropertyChanged'",
2139             "type='signal',sender='org.bluez',interface='org.bluez.MediaTransport',member='PropertyChanged'",
2140             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
2141             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesRemoved'",
2142             "type='signal',sender='org.bluez',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2143             ",arg0='org.bluez.Device1'",
2144             "type='signal',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
2145             ",arg0='org.bluez.MediaTransport1'",
2146             NULL);
2147
2148         if (y->filter_added)
2149             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
2150
2151         pa_dbus_connection_unref(y->connection);
2152     }
2153
2154     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2155         pa_hook_done(&y->hooks[i]);
2156
2157     if (y->core)
2158         pa_shared_remove(y->core, "bluetooth-discovery");
2159
2160     pa_xfree(y);
2161 }
2162
2163 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
2164     pa_assert(y);
2165     pa_assert(PA_REFCNT_VALUE(y) > 0);
2166
2167     return &y->hooks[hook];
2168 }
2169
2170 pa_bt_form_factor_t pa_bluetooth_get_form_factor(uint32_t class) {
2171     unsigned major, minor;
2172     pa_bt_form_factor_t r;
2173
2174     static const pa_bt_form_factor_t table[] = {
2175         [1] = PA_BT_FORM_FACTOR_HEADSET,
2176         [2] = PA_BT_FORM_FACTOR_HANDSFREE,
2177         [4] = PA_BT_FORM_FACTOR_MICROPHONE,
2178         [5] = PA_BT_FORM_FACTOR_SPEAKER,
2179         [6] = PA_BT_FORM_FACTOR_HEADPHONE,
2180         [7] = PA_BT_FORM_FACTOR_PORTABLE,
2181         [8] = PA_BT_FORM_FACTOR_CAR,
2182         [10] = PA_BT_FORM_FACTOR_HIFI
2183     };
2184
2185     /*
2186      * See Bluetooth Assigned Numbers:
2187      * https://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
2188      */
2189     major = (class >> 8) & 0x1F;
2190     minor = (class >> 2) & 0x3F;
2191
2192     switch (major) {
2193         case 2:
2194             return PA_BT_FORM_FACTOR_PHONE;
2195         case 4:
2196             break;
2197         default:
2198             pa_log_debug("Unknown Bluetooth major device class %u", major);
2199             return PA_BT_FORM_FACTOR_UNKNOWN;
2200     }
2201
2202     r = minor < PA_ELEMENTSOF(table) ? table[minor] : PA_BT_FORM_FACTOR_UNKNOWN;
2203
2204     if (!r)
2205         pa_log_debug("Unknown Bluetooth minor device class %u", minor);
2206
2207     return r;
2208 }
2209
2210 const char *pa_bt_form_factor_to_string(pa_bt_form_factor_t ff) {
2211     switch (ff) {
2212         case PA_BT_FORM_FACTOR_UNKNOWN:
2213             return "unknown";
2214         case PA_BT_FORM_FACTOR_HEADSET:
2215             return "headset";
2216         case PA_BT_FORM_FACTOR_HANDSFREE:
2217             return "hands-free";
2218         case PA_BT_FORM_FACTOR_MICROPHONE:
2219             return "microphone";
2220         case PA_BT_FORM_FACTOR_SPEAKER:
2221             return "speaker";
2222         case PA_BT_FORM_FACTOR_HEADPHONE:
2223             return "headphone";
2224         case PA_BT_FORM_FACTOR_PORTABLE:
2225             return "portable";
2226         case PA_BT_FORM_FACTOR_CAR:
2227             return "car";
2228         case PA_BT_FORM_FACTOR_HIFI:
2229             return "hifi";
2230         case PA_BT_FORM_FACTOR_PHONE:
2231             return "phone";
2232     }
2233
2234     pa_assert_not_reached();
2235 }
2236
2237 char *pa_bluetooth_cleanup_name(const char *name) {
2238     char *t, *s, *d;
2239     bool space = false;
2240
2241     pa_assert(name);
2242
2243     while ((*name >= 1 && *name <= 32) || *name >= 127)
2244         name++;
2245
2246     t = pa_xstrdup(name);
2247
2248     for (s = d = t; *s; s++) {
2249
2250         if (*s <= 32 || *s >= 127 || *s == '_') {
2251             space = true;
2252             continue;
2253         }
2254
2255         if (space) {
2256             *(d++) = ' ';
2257             space = false;
2258         }
2259
2260         *(d++) = *s;
2261     }
2262
2263     *d = 0;
2264
2265     return t;
2266 }
2267
2268 bool pa_bluetooth_uuid_has(pa_bluetooth_uuid *uuids, const char *uuid) {
2269     pa_assert(uuid);
2270
2271     while (uuids) {
2272         if (strcasecmp(uuids->uuid, uuid) == 0)
2273             return true;
2274
2275         uuids = uuids->next;
2276     }
2277
2278     return false;
2279 }