• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***
2   This file is part of PulseAudio.
3 
4   Copyright 2008-2013 João Paulo Rechi Vita
5   Copyrigth 2018-2019 Pali Rohár <pali.rohar@gmail.com>
6 
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11 
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16 
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20 
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 
25 #include <errno.h>
26 
27 #include <pulse/rtclock.h>
28 #include <pulse/timeval.h>
29 #include <pulse/xmalloc.h>
30 
31 #include <pulsecore/core.h>
32 #include <pulsecore/core-error.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/dbus-shared.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/macro.h>
37 #include <pulsecore/refcnt.h>
38 #include <pulsecore/shared.h>
39 
40 #include "a2dp-codec-api.h"
41 #include "a2dp-codec-util.h"
42 #include "a2dp-codecs.h"
43 
44 #include "bluez5-util.h"
45 
46 #define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
47 
48 #define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager"
49 
50 #define A2DP_OBJECT_MANAGER_PATH "/MediaEndpoint"
51 #define A2DP_SOURCE_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSource"
52 #define A2DP_SINK_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSink"
53 #define PULSEAUDIO_BASE_PATH "/org/pulseaudio"
54 
55 #define OBJECT_MANAGER_INTROSPECT_XML                                          \
56     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                                  \
57     "<node>\n"                                                                 \
58     " <interface name=\"" DBUS_INTERFACE_OBJECT_MANAGER "\">\n"                \
59     "  <method name=\"GetManagedObjects\">\n"                                  \
60     "   <arg name=\"objects\" direction=\"out\" type=\"a{oa{sa{sv}}}\"/>\n"    \
61     "  </method>\n"                                                            \
62     "  <signal name=\"InterfacesAdded\">\n"                                    \
63     "   <arg name=\"object\" type=\"o\"/>\n"                                   \
64     "   <arg name=\"interfaces\" type=\"a{sa{sv}}\"/>\n"                       \
65     "  </signal>\n"                                                            \
66     "  <signal name=\"InterfacesRemoved\">\n"                                  \
67     "   <arg name=\"object\" type=\"o\"/>\n"                                   \
68     "   <arg name=\"interfaces\" type=\"as\"/>\n"                              \
69     "  </signal>\n"                                                            \
70     " </interface>\n"                                                          \
71     " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"                \
72     "  <method name=\"Introspect\">\n"                                         \
73     "   <arg name=\"data\" direction=\"out\" type=\"s\"/>\n"                   \
74     "  </method>\n"                                                            \
75     " </interface>\n"                                                          \
76     " <node name=\"A2DPSink\"/>\n"                                             \
77     " <node name=\"A2DPSource\"/>\n"                                           \
78     "</node>\n"
79 
80 #define ENDPOINT_INTROSPECT_XML                                         \
81     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
82     "<node>"                                                            \
83     " <interface name=\"" BLUEZ_MEDIA_ENDPOINT_INTERFACE "\">"          \
84     "  <method name=\"SetConfiguration\">"                              \
85     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
86     "   <arg name=\"properties\" direction=\"in\" type=\"ay\"/>"        \
87     "  </method>"                                                       \
88     "  <method name=\"SelectConfiguration\">"                           \
89     "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
90     "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
91     "  </method>"                                                       \
92     "  <method name=\"ClearConfiguration\">"                            \
93     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
94     "  </method>"                                                       \
95     "  <method name=\"Release\">"                                       \
96     "  </method>"                                                       \
97     " </interface>"                                                     \
98     " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">"           \
99     "  <method name=\"Introspect\">"                                    \
100     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
101     "  </method>"                                                       \
102     " </interface>"                                                     \
103     "</node>"
104 
a2dp_gain_to_volume(uint16_t gain)105 static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
106     pa_volume_t volume = (pa_volume_t) ((
107         gain * PA_VOLUME_NORM
108         /* Round to closest by adding half the denominator */
109         + A2DP_MAX_GAIN / 2
110     ) / A2DP_MAX_GAIN);
111 
112     if (volume > PA_VOLUME_NORM)
113         volume = PA_VOLUME_NORM;
114 
115     return volume;
116 }
117 
volume_to_a2dp_gain(pa_volume_t volume)118 static uint16_t volume_to_a2dp_gain(pa_volume_t volume) {
119     uint16_t gain = (uint16_t) ((
120         volume * A2DP_MAX_GAIN
121         /* Round to closest by adding half the denominator */
122         + PA_VOLUME_NORM / 2
123     ) / PA_VOLUME_NORM);
124 
125     if (gain > A2DP_MAX_GAIN)
126         gain = A2DP_MAX_GAIN;
127 
128     return gain;
129 }
130 
131 struct pa_bluetooth_discovery {
132     PA_REFCNT_DECLARE;
133 
134     pa_core *core;
135     pa_dbus_connection *connection;
136     bool filter_added;
137     bool matches_added;
138     bool objects_listed;
139     pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
140     pa_hashmap *adapters;
141     pa_hashmap *devices;
142     pa_hashmap *transports;
143     pa_bluetooth_profile_status_t profiles_status[PA_BLUETOOTH_PROFILE_COUNT];
144 
145     int headset_backend;
146     pa_bluetooth_backend *ofono_backend, *native_backend;
147     PA_LLIST_HEAD(pa_dbus_pending, pending);
148     bool enable_native_hsp_hs;
149     bool enable_native_hfp_hf;
150     bool enable_msbc;
151 };
152 
send_and_add_to_pending(pa_bluetooth_discovery * y,DBusMessage * m,DBusPendingCallNotifyFunction func,void * call_data)153 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
154                                                                   DBusPendingCallNotifyFunction func, void *call_data) {
155     pa_dbus_pending *p;
156     DBusPendingCall *call;
157 
158     pa_assert(y);
159     pa_assert(m);
160 
161     pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
162 
163     p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
164     PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
165     dbus_pending_call_set_notify(call, func, p, NULL);
166 
167     return p;
168 }
169 
check_variant_property(DBusMessageIter * i)170 static const char *check_variant_property(DBusMessageIter *i) {
171     const char *key;
172 
173     pa_assert(i);
174 
175     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
176         pa_log_error("Property name not a string.");
177         return NULL;
178     }
179 
180     dbus_message_iter_get_basic(i, &key);
181 
182     if (!dbus_message_iter_next(i)) {
183         pa_log_error("Property value missing");
184         return NULL;
185     }
186 
187     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
188         pa_log_error("Property value not a variant.");
189         return NULL;
190     }
191 
192     return key;
193 }
194 
profile_status_get(pa_bluetooth_discovery * y,pa_bluetooth_profile_t profile)195 pa_bluetooth_profile_status_t profile_status_get(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
196     return y->profiles_status[profile];
197 }
198 
profile_status_set(pa_bluetooth_discovery * y,pa_bluetooth_profile_t profile,pa_bluetooth_profile_status_t status)199 void profile_status_set(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile, pa_bluetooth_profile_status_t status) {
200     y->profiles_status[profile] = status;
201 }
202 
pa_bluetooth_transport_new(pa_bluetooth_device * d,const char * owner,const char * path,pa_bluetooth_profile_t p,const uint8_t * config,size_t size)203 pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
204                                                    pa_bluetooth_profile_t p, const uint8_t *config, size_t size) {
205     pa_bluetooth_transport *t;
206 
207     t = pa_xnew0(pa_bluetooth_transport, 1);
208     t->device = d;
209     t->owner = pa_xstrdup(owner);
210     t->path = pa_xstrdup(path);
211     t->profile = p;
212     t->config_size = size;
213     /* Always force initial volume to be set/propagated correctly */
214     t->sink_volume = PA_VOLUME_INVALID;
215     t->source_volume = PA_VOLUME_INVALID;
216 
217     if (size > 0) {
218         t->config = pa_xnew(uint8_t, size);
219         if (config)
220             memcpy(t->config, config, size);
221         else
222             memset(t->config, 0, size);
223     }
224 
225     return t;
226 }
227 
pa_bluetooth_transport_reconfigure(pa_bluetooth_transport * t,const pa_bt_codec * bt_codec,pa_bluetooth_transport_write_cb write_cb,pa_bluetooth_transport_setsockopt_cb setsockopt_cb)228 void pa_bluetooth_transport_reconfigure(pa_bluetooth_transport *t, const pa_bt_codec *bt_codec,
229                                         pa_bluetooth_transport_write_cb write_cb, pa_bluetooth_transport_setsockopt_cb setsockopt_cb) {
230     pa_assert(t);
231 
232     t->bt_codec = bt_codec;
233 
234     t->write = write_cb;
235     t->setsockopt = setsockopt_cb;
236 
237     /* reset stream write type hint */
238     t->stream_write_type = 0;
239 
240     /* reset SCO MTU adjustment hint */
241     t->last_read_size = 0;
242 }
243 
transport_state_to_string(pa_bluetooth_transport_state_t state)244 static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
245     switch(state) {
246         case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
247             return "disconnected";
248         case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
249             return "idle";
250         case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
251             return "playing";
252     }
253 
254     return "invalid";
255 }
256 
pa_bluetooth_device_supports_profile(const pa_bluetooth_device * device,pa_bluetooth_profile_t profile)257 bool pa_bluetooth_device_supports_profile(const pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
258     bool show_hfp, show_hsp, r;
259 
260     pa_assert(device);
261 
262     /* While discovery is being released adapters will be removed from devices,
263      * and there are no profiles to support without adapter.
264      */
265     if (!device->adapter) {
266         pa_log_debug("Device %s (%s) has no adapter to support profile %s",
267                 device->alias, device->address, pa_bluetooth_profile_to_string(profile));
268         return false;
269     }
270 
271     if (device->enable_hfp_hf) {
272         show_hfp = pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
273         show_hsp = !show_hfp;
274     } else {
275         show_hfp = false;
276         show_hsp = true;
277     }
278 
279     switch (profile) {
280         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
281             r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK) &&
282                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE));
283             break;
284         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
285             r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE) &&
286                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_A2DP_SINK));
287             break;
288         case PA_BLUETOOTH_PROFILE_HSP_HS:
289             r = show_hsp
290                 && ( !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS) &&
291                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_AG)) ||
292                    !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT) &&
293                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_AG)) );
294             break;
295         case PA_BLUETOOTH_PROFILE_HSP_AG:
296             r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG) &&
297                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_HS)) ||
298                    !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG) &&
299                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT));
300             break;
301         case PA_BLUETOOTH_PROFILE_HFP_HF:
302             r = show_hfp
303                 && !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF) &&
304                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HFP_AG));
305             break;
306         case PA_BLUETOOTH_PROFILE_HFP_AG:
307             r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG) &&
308                       pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HFP_HF));
309             break;
310         case PA_BLUETOOTH_PROFILE_OFF:
311         default:
312             pa_assert_not_reached();
313             break;
314     }
315 
316     pa_log_debug("Checking if device %s (%s) supports profile %s: %s",
317                  device->alias, device->address, pa_bluetooth_profile_to_string(profile), r ? "true" : "false");
318 
319     return r;
320 }
321 
device_is_profile_connected(pa_bluetooth_device * device,pa_bluetooth_profile_t profile)322 static bool device_is_profile_connected(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
323     if (device->transports[profile] && device->transports[profile]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
324         return true;
325     else
326         return false;
327 }
328 
device_count_disconnected_profiles(pa_bluetooth_device * device)329 static unsigned device_count_disconnected_profiles(pa_bluetooth_device *device) {
330     pa_bluetooth_profile_t profile;
331     unsigned count = 0;
332 
333     for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
334         if (!pa_bluetooth_device_supports_profile(device, profile))
335             continue;
336 
337         if (!device_is_profile_connected(device, profile))
338             count++;
339     }
340 
341     return count;
342 }
343 
device_stop_waiting_for_profiles(pa_bluetooth_device * device)344 static void device_stop_waiting_for_profiles(pa_bluetooth_device *device) {
345     if (!device->wait_for_profiles_timer)
346         return;
347 
348     device->discovery->core->mainloop->time_free(device->wait_for_profiles_timer);
349     device->wait_for_profiles_timer = NULL;
350 }
351 
wait_for_profiles_cb(pa_mainloop_api * api,pa_time_event * event,const struct timeval * tv,void * userdata)352 static void wait_for_profiles_cb(pa_mainloop_api *api, pa_time_event* event, const struct timeval *tv, void *userdata) {
353     pa_bluetooth_device *device = userdata;
354     pa_strbuf *buf;
355     pa_bluetooth_profile_t profile;
356     bool first = true;
357     char *profiles_str;
358 
359     device_stop_waiting_for_profiles(device);
360 
361     buf = pa_strbuf_new();
362 
363     for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
364         if (device_is_profile_connected(device, profile))
365             continue;
366 
367         if (!pa_bluetooth_device_supports_profile(device, profile))
368             continue;
369 
370         if (first)
371             first = false;
372         else
373             pa_strbuf_puts(buf, ", ");
374 
375         pa_strbuf_puts(buf, pa_bluetooth_profile_to_string(profile));
376     }
377 
378     profiles_str = pa_strbuf_to_string_free(buf);
379     pa_log_debug("Timeout expired, and device %s still has disconnected profiles: %s",
380                  device->path, profiles_str);
381     pa_xfree(profiles_str);
382     pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
383 }
384 
device_start_waiting_for_profiles(pa_bluetooth_device * device)385 static void device_start_waiting_for_profiles(pa_bluetooth_device *device) {
386     pa_assert(!device->wait_for_profiles_timer);
387     device->wait_for_profiles_timer = pa_core_rttime_new(device->discovery->core,
388                                                          pa_rtclock_now() + WAIT_FOR_PROFILES_TIMEOUT_USEC,
389                                                          wait_for_profiles_cb, device);
390 }
391 
392 struct switch_codec_data {
393     char *pa_endpoint;
394     char *device_path;
395     pa_bluetooth_profile_t profile;
396     void (*cb)(bool, pa_bluetooth_profile_t profile, void *);
397     void *userdata;
398 };
399 
pa_bluetooth_device_switch_codec_reply(DBusPendingCall * pending,void * userdata)400 static void pa_bluetooth_device_switch_codec_reply(DBusPendingCall *pending, void *userdata) {
401     DBusMessage *r;
402     pa_dbus_pending *p;
403     pa_bluetooth_discovery *y;
404     pa_bluetooth_device *device;
405     struct switch_codec_data *data;
406 
407     pa_assert(pending);
408     pa_assert_se(p = userdata);
409     pa_assert_se(y = p->context_data);
410     pa_assert_se(data = p->call_data);
411     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
412 
413     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
414     pa_dbus_pending_free(p);
415 
416     device = pa_hashmap_get(y->devices, data->device_path);
417     if (!device) {
418         pa_log_error("Changing codec for device %s with profile %s failed. Device is not connected anymore",
419                 data->device_path, pa_bluetooth_profile_to_string(data->profile));
420         data->cb(false, data->profile, data->userdata);
421     } else if (dbus_message_get_type(r) != DBUS_MESSAGE_TYPE_ERROR) {
422         pa_log_info("Changing codec for device %s with profile %s succeeded",
423                 data->device_path, pa_bluetooth_profile_to_string(data->profile));
424         data->cb(true, data->profile, data->userdata);
425     } else if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
426         pa_log_error("Changing codec for device %s with profile %s failed. Error: %s",
427                 data->device_path, pa_bluetooth_profile_to_string(data->profile),
428                 dbus_message_get_error_name(r));
429     }
430 
431     dbus_message_unref(r);
432 
433     pa_xfree(data->pa_endpoint);
434     pa_xfree(data->device_path);
435     pa_xfree(data);
436 
437     device->codec_switching_in_progress = false;
438 }
439 
pa_bluetooth_device_switch_codec(pa_bluetooth_device * device,pa_bluetooth_profile_t profile,pa_hashmap * capabilities_hashmap,const pa_a2dp_endpoint_conf * endpoint_conf,void (* codec_switch_cb)(bool,pa_bluetooth_profile_t profile,void *),void * userdata)440 bool pa_bluetooth_device_switch_codec(pa_bluetooth_device *device, pa_bluetooth_profile_t profile,
441         pa_hashmap *capabilities_hashmap, const pa_a2dp_endpoint_conf *endpoint_conf,
442         void (*codec_switch_cb)(bool, pa_bluetooth_profile_t profile, void *), void *userdata) {
443     DBusMessageIter iter, dict;
444     DBusMessage *m;
445     struct switch_codec_data *data;
446     pa_a2dp_codec_capabilities *capabilities;
447     uint8_t config[MAX_A2DP_CAPS_SIZE];
448     uint8_t config_size;
449     bool is_a2dp_sink;
450     pa_hashmap *all_endpoints;
451     char *pa_endpoint;
452     const char *endpoint;
453 
454     pa_assert(device);
455     pa_assert(capabilities_hashmap);
456     pa_assert(endpoint_conf);
457 
458     if (device->codec_switching_in_progress) {
459         pa_log_error("Codec switching operation already in progress");
460         return false;
461     }
462 
463     is_a2dp_sink = profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
464 
465     all_endpoints = NULL;
466     all_endpoints = pa_hashmap_get(is_a2dp_sink ? device->a2dp_sink_endpoints : device->a2dp_source_endpoints,
467             &endpoint_conf->id);
468     pa_assert(all_endpoints);
469 
470     pa_assert_se(endpoint = endpoint_conf->choose_remote_endpoint(capabilities_hashmap, &device->discovery->core->default_sample_spec, is_a2dp_sink));
471     pa_assert_se(capabilities = pa_hashmap_get(all_endpoints, endpoint));
472 
473     config_size = endpoint_conf->fill_preferred_configuration(&device->discovery->core->default_sample_spec,
474             capabilities->buffer, capabilities->size, config);
475     if (config_size == 0)
476         return false;
477 
478     pa_endpoint = pa_sprintf_malloc("%s/%s", is_a2dp_sink ? A2DP_SOURCE_ENDPOINT : A2DP_SINK_ENDPOINT,
479             endpoint_conf->bt_codec.name);
480 
481     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, endpoint,
482                 BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"));
483 
484     dbus_message_iter_init_append(m, &iter);
485     pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &pa_endpoint));
486     dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
487                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
488                                      DBUS_TYPE_STRING_AS_STRING
489                                      DBUS_TYPE_VARIANT_AS_STRING
490                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
491                                      &dict);
492     pa_dbus_append_basic_array_variant_dict_entry(&dict, "Capabilities", DBUS_TYPE_BYTE, &config, config_size);
493     dbus_message_iter_close_container(&iter, &dict);
494 
495     device->codec_switching_in_progress = true;
496 
497     data = pa_xnew0(struct switch_codec_data, 1);
498     data->pa_endpoint = pa_endpoint;
499     data->device_path = pa_xstrdup(device->path);
500     data->profile = profile;
501     data->cb = codec_switch_cb;
502     data->userdata = userdata;
503 
504     send_and_add_to_pending(device->discovery, m, pa_bluetooth_device_switch_codec_reply, data);
505 
506     return true;
507 }
508 
pa_bluetooth_transport_set_state(pa_bluetooth_transport * t,pa_bluetooth_transport_state_t state)509 void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) {
510     bool old_any_connected;
511     unsigned n_disconnected_profiles;
512     bool new_device_appeared;
513     bool device_disconnected;
514 
515     pa_assert(t);
516 
517     if (t->state == state)
518         return;
519 
520     old_any_connected = pa_bluetooth_device_any_transport_connected(t->device);
521 
522     pa_log_debug("Transport %s state: %s -> %s",
523                  t->path, transport_state_to_string(t->state), transport_state_to_string(state));
524 
525     t->state = state;
526 
527     pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
528 
529     /* If there are profiles that are expected to get connected soon (based
530      * on the UUID list), we wait for a bit before announcing the new
531      * device, so that all profiles have time to get connected before the
532      * card object is created. If we didn't wait, the card would always
533      * have only one profile marked as available in the initial state,
534      * which would prevent module-card-restore from restoring the initial
535      * profile properly. */
536 
537     n_disconnected_profiles = device_count_disconnected_profiles(t->device);
538 
539     new_device_appeared = !old_any_connected && pa_bluetooth_device_any_transport_connected(t->device);
540     device_disconnected = old_any_connected && !pa_bluetooth_device_any_transport_connected(t->device);
541 
542     if (new_device_appeared) {
543         if (n_disconnected_profiles > 0)
544             device_start_waiting_for_profiles(t->device);
545         else
546             pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
547         return;
548     }
549 
550     if (device_disconnected) {
551         if (t->device->wait_for_profiles_timer) {
552             /* If the timer is still running when the device disconnects, we
553              * never sent the notification of the device getting connected, so
554              * we don't need to send a notification about the disconnection
555              * either. Let's just stop the timer. */
556             device_stop_waiting_for_profiles(t->device);
557         } else
558             pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
559         return;
560     }
561 
562     if (n_disconnected_profiles == 0 && t->device->wait_for_profiles_timer) {
563         /* All profiles are now connected, so we can stop the wait timer and
564          * send a notification of the new device. */
565         device_stop_waiting_for_profiles(t->device);
566         pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
567     }
568 }
569 
pa_bluetooth_transport_set_volume(pa_bluetooth_transport * t,pa_volume_t volume)570 static pa_volume_t pa_bluetooth_transport_set_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
571     static const char *volume_str = "Volume";
572     static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
573     DBusMessage *m;
574     DBusMessageIter iter;
575     uint16_t gain;
576 
577     pa_assert(t);
578     pa_assert(t->device);
579     pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
580     pa_assert(t->device->discovery);
581 
582     gain = volume_to_a2dp_gain(volume);
583     /* Propagate rounding and bound checks */
584     volume = a2dp_gain_to_volume(gain);
585 
586     if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE && t->source_volume == volume)
587         return volume;
588     else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && t->sink_volume == volume)
589         return volume;
590 
591     if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
592         t->source_volume = volume;
593     else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
594         t->sink_volume = volume;
595 
596     pa_log_debug("Sending A2DP volume %d/127 to peer", gain);
597 
598     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Set"));
599 
600     dbus_message_iter_init_append(m, &iter);
601     pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &mediatransport_str));
602     pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &volume_str));
603     pa_dbus_append_basic_variant(&iter, DBUS_TYPE_UINT16, &gain);
604 
605     /* Ignore replies, wait for the Volume property to change (generally arrives
606      * before this function replies).
607      *
608      * In an ideal world BlueZ exposes a function to change volume, that returns
609      * with the actual volume set by the peer as returned by the SetAbsoluteVolume
610      * AVRCP command.  That is required later to perform software volume compensation
611      * based on actual playback volume.
612      */
613     dbus_message_set_no_reply(m, true);
614     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(t->device->discovery->connection), m, NULL));
615     dbus_message_unref(m);
616 
617     return volume;
618 }
619 
pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport * t,pa_volume_t volume)620 static pa_volume_t pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
621     pa_assert(t);
622     pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
623     return pa_bluetooth_transport_set_volume(t, volume);
624 }
625 
pa_bluetooth_transport_set_source_volume(pa_bluetooth_transport * t,pa_volume_t volume)626 static pa_volume_t pa_bluetooth_transport_set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
627     pa_assert(t);
628     pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
629     return pa_bluetooth_transport_set_volume(t, volume);
630 }
631 
pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport * t,pa_volume_t volume)632 static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport *t, pa_volume_t volume) {
633     pa_bluetooth_hook_t hook;
634     bool is_source;
635     char volume_str[PA_VOLUME_SNPRINT_MAX];
636 
637     pa_assert(t);
638     pa_assert(t->device);
639 
640     if (!t->device->avrcp_absolute_volume)
641         return;
642 
643     is_source = t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
644 
645     if (is_source) {
646         if (t->source_volume == volume)
647             return;
648         t->source_volume = volume;
649         hook = PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED;
650     } else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
651         if (t->sink_volume == volume)
652             return;
653         t->sink_volume = volume;
654         hook = PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED;
655 
656         /* A2DP Absolute Volume is optional.  This callback is only
657          * attached when the peer supports it, and the hook handler
658          * further attaches the necessary hardware callback to the
659          * pa_sink and disables software attenuation.
660          */
661         if (!t->set_sink_volume) {
662             pa_log_debug("A2DP sink supports volume control");
663             t->set_sink_volume = pa_bluetooth_transport_set_sink_volume;
664         }
665     } else {
666         pa_assert_not_reached();
667     }
668 
669     pa_log_debug("Reporting volume change %s for %s",
670                  pa_volume_snprint(volume_str, sizeof(volume_str), volume),
671                  is_source ? "source" : "sink");
672 
673     pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, hook), t);
674 }
675 
pa_bluetooth_transport_put(pa_bluetooth_transport * t)676 void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
677     pa_assert(t);
678 
679     t->device->transports[t->profile] = t;
680     pa_assert_se(pa_hashmap_put(t->device->discovery->transports, t->path, t) >= 0);
681     pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
682 }
683 
pa_bluetooth_transport_unlink(pa_bluetooth_transport * t)684 void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t) {
685     pa_assert(t);
686 
687     pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED);
688     pa_hashmap_remove(t->device->discovery->transports, t->path);
689     t->device->transports[t->profile] = NULL;
690 }
691 
pa_bluetooth_transport_free(pa_bluetooth_transport * t)692 void pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
693     pa_assert(t);
694 
695     if (t->destroy)
696         t->destroy(t);
697     pa_bluetooth_transport_unlink(t);
698 
699     pa_xfree(t->owner);
700     pa_xfree(t->path);
701     pa_xfree(t->config);
702     pa_xfree(t);
703 }
704 
bluez5_transport_acquire_cb(pa_bluetooth_transport * t,bool optional,size_t * imtu,size_t * omtu)705 static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
706     DBusMessage *m, *r;
707     DBusError err;
708     int ret;
709     uint16_t i, o;
710     const char *method = optional ? "TryAcquire" : "Acquire";
711 
712     pa_assert(t);
713     pa_assert(t->device);
714     pa_assert(t->device->discovery);
715 
716     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, method));
717 
718     dbus_error_init(&err);
719 
720     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
721     dbus_message_unref(m);
722     m = NULL;
723     if (!r) {
724         if (optional && pa_streq(err.name, BLUEZ_ERROR_NOT_AVAILABLE))
725             pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
726         else
727             pa_log_error("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
728 
729         dbus_error_free(&err);
730         return -1;
731     }
732 
733     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
734                                DBUS_TYPE_INVALID)) {
735         pa_log_error("Failed to parse %s() reply: %s", method, err.message);
736         dbus_error_free(&err);
737         ret = -1;
738         goto finish;
739     }
740 
741     if (imtu)
742         *imtu = i;
743 
744     if (omtu)
745         *omtu = o;
746 
747 finish:
748     dbus_message_unref(r);
749     return ret;
750 }
751 
bluez5_transport_release_cb(pa_bluetooth_transport * t)752 static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
753     DBusMessage *m, *r;
754     DBusError err;
755 
756     pa_assert(t);
757     pa_assert(t->device);
758     pa_assert(t->device->discovery);
759 
760     dbus_error_init(&err);
761 
762     if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
763         pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
764         return;
765     }
766 
767     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, "Release"));
768     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
769     dbus_message_unref(m);
770     m = NULL;
771     if (r) {
772         dbus_message_unref(r);
773         r = NULL;
774     }
775 
776     if (dbus_error_is_set(&err)) {
777         pa_log_error("Failed to release transport %s: %s", t->path, err.message);
778         dbus_error_free(&err);
779     } else
780         pa_log_info("Transport %s released", t->path);
781 }
782 
get_volume_reply(DBusPendingCall * pending,void * userdata)783 static void get_volume_reply(DBusPendingCall *pending, void *userdata) {
784     DBusMessage *r;
785     DBusMessageIter iter, variant;
786     pa_dbus_pending *p;
787     pa_bluetooth_discovery *y;
788     pa_bluetooth_transport *t;
789     uint16_t gain;
790     pa_volume_t volume;
791     const char *error_name, *error_message;
792 
793     pa_assert(pending);
794     pa_assert_se(p = userdata);
795     pa_assert_se(y = p->context_data);
796     pa_assert_se(t = p->call_data);
797     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
798 
799     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
800         error_name = dbus_message_get_error_name(r);
801         error_message = pa_dbus_get_error_message(r);
802 
803         if (pa_streq(error_name, DBUS_ERROR_INVALID_ARGS) && pa_streq(error_message, "No such property 'Volume'")) {
804             pa_log_warn(DBUS_INTERFACE_PROPERTIES ".Get %s Volume property not (yet) available",
805                         dbus_message_get_path(p->message));
806         } else {
807             pa_log_error(DBUS_INTERFACE_PROPERTIES ".Get %s Volume failed: %s: %s",
808                          dbus_message_get_path(p->message),
809                          error_name,
810                          error_message);
811         }
812         goto finish;
813     }
814     dbus_message_iter_init(r, &iter);
815     pa_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT);
816     dbus_message_iter_recurse(&iter, &variant);
817     pa_assert(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_UINT16);
818     dbus_message_iter_get_basic(&variant, &gain);
819 
820     if (gain > A2DP_MAX_GAIN)
821         gain = A2DP_MAX_GAIN;
822 
823     pa_log_debug("Received A2DP Absolute Volume %d", gain);
824 
825     volume = a2dp_gain_to_volume(gain);
826 
827     pa_bluetooth_transport_remote_volume_changed(t, volume);
828 
829 finish:
830     dbus_message_unref(r);
831 
832     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
833     pa_dbus_pending_free(p);
834 }
835 
bluez5_transport_get_volume(pa_bluetooth_transport * t)836 static void bluez5_transport_get_volume(pa_bluetooth_transport *t) {
837     static const char *volume_str = "Volume";
838     static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
839     DBusMessage *m;
840 
841     pa_assert(t);
842     pa_assert(t->device);
843     pa_assert(t->device->discovery);
844 
845     pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
846 
847     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Get"));
848     pa_assert_se(dbus_message_append_args(m,
849         DBUS_TYPE_STRING, &mediatransport_str,
850         DBUS_TYPE_STRING, &volume_str,
851         DBUS_TYPE_INVALID));
852 
853     send_and_add_to_pending(t->device->discovery, m, get_volume_reply, t);
854 }
855 
pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport * t)856 void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t) {
857     pa_assert(t);
858     pa_assert(t->device);
859 
860     if (!t->device->avrcp_absolute_volume)
861         return;
862 
863     if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
864         /* A2DP Absolute Volume control (AVRCP 1.4) is optional */
865         bluez5_transport_get_volume(t);
866 }
867 
a2dp_transport_write(pa_bluetooth_transport * t,int fd,const void * buffer,size_t size,size_t write_mtu)868 static ssize_t a2dp_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) {
869     ssize_t l = 0;
870     size_t written = 0;
871     size_t write_size;
872 
873     pa_assert(t);
874 
875     while (written < size) {
876         write_size = PA_MIN(size - written, write_mtu);
877         l = pa_write(fd, buffer + written, write_size, &t->stream_write_type);
878         if (l < 0)
879             break;
880         written += l;
881     }
882 
883     if (l < 0) {
884         if (errno == EAGAIN) {
885             /* Hmm, apparently the socket was not writable, give up for now */
886             pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss.");
887             /* Drain write buffer */
888             written = size;
889         } else {
890             pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
891             /* Report error from write call */
892             return -1;
893         }
894     }
895 
896     /* if too much data left discard it all */
897     if (size - written >= write_mtu) {
898         pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu",
899                     written, size, write_mtu);
900         /* Drain write buffer */
901         written = size;
902     }
903 
904     return written;
905 }
906 
pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device * d)907 bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
908     unsigned i;
909 
910     pa_assert(d);
911 
912     if (!d->valid)
913         return false;
914 
915     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
916         if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
917             return true;
918 
919     return false;
920 }
921 
922 /* Returns a path containing /org/pulseaudio + /bluez/hciXX */
adapter_battery_provider_path(pa_bluetooth_adapter * d)923 static char *adapter_battery_provider_path(pa_bluetooth_adapter *d) {
924     const char *devname = d->path + sizeof("/org") - 1;
925     return pa_sprintf_malloc(PULSEAUDIO_BASE_PATH "%s", devname);
926 }
927 
928 /* Returns a path containing /org/pulseaudio + /bluez/hciXX/dev_XX_XX_XX_XX_XX_XX */
device_battery_provider_path(pa_bluetooth_device * d)929 static char *device_battery_provider_path(pa_bluetooth_device *d) {
930     const char *devname = d->path + sizeof("/org") - 1;
931     return pa_sprintf_malloc(PULSEAUDIO_BASE_PATH "%s", devname);
932 }
933 
934 static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object);
935 static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *object, bool only_percentage);
936 
pa_bluetooth_device_report_battery_level(pa_bluetooth_device * d,uint8_t level,const char * reporting_source)937 void pa_bluetooth_device_report_battery_level(pa_bluetooth_device *d, uint8_t level, const char *reporting_source) {
938     bool had_battery_provider = d->has_battery_level;
939     d->has_battery_level = true;
940     d->battery_level = level;
941     pa_assert_se(d->battery_source = reporting_source);
942 
943     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED], d);
944 
945     if (!had_battery_provider) {
946         DBusMessage *m;
947         DBusMessageIter iter;
948         char *provider_path;
949 
950         if (!d->adapter->battery_provider_registered) {
951             pa_log_debug("No battery provider registered on adapter of %s", d->path);
952             return;
953         }
954 
955         provider_path = adapter_battery_provider_path(d->adapter);
956 
957         pa_log_debug("Registering new battery for %s with level %d", d->path, level);
958 
959         pa_assert_se(m = dbus_message_new_signal(provider_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"));
960         dbus_message_iter_init_append(m, &iter);
961         append_battery_provider(d, &iter);
962         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
963 
964         pa_xfree(provider_path);
965     } else {
966         DBusMessage *m;
967         DBusMessageIter iter;
968         char *battery_path = device_battery_provider_path(d);
969 
970         pa_log_debug("Notifying battery Percentage for %s changed %d", battery_path, level);
971 
972         pa_assert_se(m = dbus_message_new_signal(battery_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"));
973         dbus_message_iter_init_append(m, &iter);
974         append_battery_provider_properties(d, &iter, true);
975         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
976         pa_xfree(battery_path);
977     }
978 }
979 
980 /* Notify BlueZ that we're no longer providing battery info for this device */
pa_bluetooth_device_deregister_battery(pa_bluetooth_device * d)981 void pa_bluetooth_device_deregister_battery(pa_bluetooth_device *d) {
982     static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
983     DBusMessage *m;
984     DBusMessageIter iter, array;
985     char *battery_path, *provider_path;
986 
987     if (!d->has_battery_level)
988         return;
989 
990     d->has_battery_level = false;
991     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED], d);
992 
993     if (!d->adapter->battery_provider_registered)
994         return;
995 
996     battery_path = device_battery_provider_path(d);
997     provider_path = adapter_battery_provider_path(d->adapter);
998 
999     pa_log_debug("Deregistering battery provider %s", battery_path);
1000 
1001     pa_assert_se(m = dbus_message_new_signal(provider_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"));
1002     dbus_message_iter_init_append(m, &iter);
1003     pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &battery_path));
1004     pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array));
1005     pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &interface_name));
1006     pa_assert_se(dbus_message_iter_close_container(&iter, &array));
1007 
1008     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
1009     d->has_battery_level = false;
1010 
1011     pa_xfree(battery_path);
1012     pa_xfree(provider_path);
1013 }
1014 
1015 
transport_state_from_string(const char * value,pa_bluetooth_transport_state_t * state)1016 static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
1017     pa_assert(value);
1018     pa_assert(state);
1019 
1020     if (pa_streq(value, "idle"))
1021         *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
1022     else if (pa_streq(value, "pending") || pa_streq(value, "active"))
1023         *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
1024     else
1025         return -1;
1026 
1027     return 0;
1028 }
1029 
parse_transport_property(pa_bluetooth_transport * t,DBusMessageIter * i)1030 static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
1031     const char *key;
1032     DBusMessageIter variant_i;
1033 
1034     key = check_variant_property(i);
1035     if (key == NULL)
1036         return;
1037 
1038     pa_log_debug("Transport property %s changed", key);
1039 
1040     dbus_message_iter_recurse(i, &variant_i);
1041 
1042     switch (dbus_message_iter_get_arg_type(&variant_i)) {
1043 
1044         case DBUS_TYPE_STRING: {
1045 
1046             const char *value;
1047             dbus_message_iter_get_basic(&variant_i, &value);
1048 
1049             if (pa_streq(key, "State")) {
1050                 pa_bluetooth_transport_state_t state;
1051 
1052                 if (transport_state_from_string(value, &state) < 0) {
1053                     pa_log_error("Invalid state received: %s", value);
1054                     return;
1055                 }
1056 
1057                 pa_bluetooth_transport_set_state(t, state);
1058             }
1059 
1060             break;
1061         }
1062 
1063         case DBUS_TYPE_UINT16: {
1064             uint16_t value;
1065             dbus_message_iter_get_basic(&variant_i, &value);
1066 
1067             if (pa_streq(key, "Volume")) {
1068                 pa_volume_t volume = a2dp_gain_to_volume(value);
1069                 pa_bluetooth_transport_remote_volume_changed(t, volume);
1070             }
1071             break;
1072         }
1073     }
1074 
1075     return;
1076 }
1077 
parse_transport_properties(pa_bluetooth_transport * t,DBusMessageIter * i)1078 static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
1079     DBusMessageIter element_i;
1080 
1081     dbus_message_iter_recurse(i, &element_i);
1082 
1083     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1084         DBusMessageIter dict_i;
1085 
1086         dbus_message_iter_recurse(&element_i, &dict_i);
1087 
1088         parse_transport_property(t, &dict_i);
1089 
1090         dbus_message_iter_next(&element_i);
1091     }
1092 
1093     return 0;
1094 }
1095 
pa_a2dp_codec_id_hash_func(const void * _p)1096 static unsigned pa_a2dp_codec_id_hash_func(const void *_p) {
1097     unsigned hash;
1098     const pa_a2dp_codec_id *p = _p;
1099 
1100     hash = p->codec_id;
1101     hash = 31 * hash + ((p->vendor_id >>  0) & 0xFF);
1102     hash = 31 * hash + ((p->vendor_id >>  8) & 0xFF);
1103     hash = 31 * hash + ((p->vendor_id >> 16) & 0xFF);
1104     hash = 31 * hash + ((p->vendor_id >> 24) & 0xFF);
1105     hash = 31 * hash + ((p->vendor_codec_id >> 0) & 0xFF);
1106     hash = 31 * hash + ((p->vendor_codec_id >> 8) & 0xFF);
1107     return hash;
1108 }
1109 
pa_a2dp_codec_id_compare_func(const void * _a,const void * _b)1110 static int pa_a2dp_codec_id_compare_func(const void *_a, const void *_b) {
1111     const pa_a2dp_codec_id *a = _a;
1112     const pa_a2dp_codec_id *b = _b;
1113 
1114     if (a->codec_id < b->codec_id)
1115         return -1;
1116     if (a->codec_id > b->codec_id)
1117         return 1;
1118 
1119     if (a->vendor_id < b->vendor_id)
1120         return -1;
1121     if (a->vendor_id > b->vendor_id)
1122         return 1;
1123 
1124     if (a->vendor_codec_id < b->vendor_codec_id)
1125         return -1;
1126     if (a->vendor_codec_id > b->vendor_codec_id)
1127         return 1;
1128 
1129     return 0;
1130 }
1131 
remote_endpoint_remove(pa_bluetooth_discovery * y,const char * path)1132 static void remote_endpoint_remove(pa_bluetooth_discovery *y, const char *path) {
1133     pa_bluetooth_device *device;
1134     pa_hashmap *endpoints;
1135     void *devices_state;
1136     void *state;
1137 
1138     PA_HASHMAP_FOREACH(device, y->devices, devices_state) {
1139         PA_HASHMAP_FOREACH(endpoints, device->a2dp_sink_endpoints, state)
1140             pa_hashmap_remove_and_free(endpoints, path);
1141 
1142         PA_HASHMAP_FOREACH(endpoints, device->a2dp_source_endpoints, state)
1143             pa_hashmap_remove_and_free(endpoints, path);
1144     }
1145 
1146     pa_log_debug("Remote endpoint %s was removed", path);
1147 }
1148 
device_create(pa_bluetooth_discovery * y,const char * path)1149 static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char *path) {
1150     pa_bluetooth_device *d;
1151 
1152     pa_assert(y);
1153     pa_assert(path);
1154 
1155     d = pa_xnew0(pa_bluetooth_device, 1);
1156     d->discovery = y;
1157     d->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y);
1158     d->path = pa_xstrdup(path);
1159     d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1160     d->a2dp_sink_endpoints = pa_hashmap_new_full(pa_a2dp_codec_id_hash_func, pa_a2dp_codec_id_compare_func, pa_xfree, (pa_free_cb_t)pa_hashmap_free);
1161     d->a2dp_source_endpoints = pa_hashmap_new_full(pa_a2dp_codec_id_hash_func, pa_a2dp_codec_id_compare_func, pa_xfree, (pa_free_cb_t)pa_hashmap_free);
1162 
1163     pa_hashmap_put(y->devices, d->path, d);
1164 
1165     return d;
1166 }
1167 
pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery * y,const char * path)1168 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path) {
1169     pa_bluetooth_device *d;
1170 
1171     pa_assert(y);
1172     pa_assert(PA_REFCNT_VALUE(y) > 0);
1173     pa_assert(path);
1174 
1175     if ((d = pa_hashmap_get(y->devices, path)) && d->valid)
1176         return d;
1177 
1178     return NULL;
1179 }
1180 
pa_bluetooth_discovery_get_enable_native_hsp_hs(pa_bluetooth_discovery * y)1181 bool pa_bluetooth_discovery_get_enable_native_hsp_hs(pa_bluetooth_discovery *y)
1182 {
1183     pa_assert(y);
1184     pa_assert(PA_REFCNT_VALUE(y) > 0);
1185 
1186     return y->enable_native_hsp_hs;
1187 }
1188 
pa_bluetooth_discovery_get_enable_native_hfp_hf(pa_bluetooth_discovery * y)1189 bool pa_bluetooth_discovery_get_enable_native_hfp_hf(pa_bluetooth_discovery *y)
1190 {
1191     pa_assert(y);
1192     pa_assert(PA_REFCNT_VALUE(y) > 0);
1193 
1194     return y->enable_native_hfp_hf;
1195 }
1196 
pa_bluetooth_discovery_get_enable_msbc(pa_bluetooth_discovery * y)1197 bool pa_bluetooth_discovery_get_enable_msbc(pa_bluetooth_discovery *y)
1198 {
1199     pa_assert(y);
1200     pa_assert(PA_REFCNT_VALUE(y) > 0);
1201 
1202     return y->enable_msbc;
1203 }
1204 
pa_bluetooth_discovery_get_transports(pa_bluetooth_discovery * y)1205 pa_hashmap* pa_bluetooth_discovery_get_transports(pa_bluetooth_discovery *y) {
1206     pa_assert(y);
1207     pa_assert(PA_REFCNT_VALUE(y) > 0);
1208 
1209     return y->transports;
1210 }
1211 
pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery * y,const char * remote,const char * local)1212 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
1213     pa_bluetooth_device *d;
1214     void *state = NULL;
1215 
1216     pa_assert(y);
1217     pa_assert(PA_REFCNT_VALUE(y) > 0);
1218     pa_assert(remote);
1219     pa_assert(local);
1220 
1221     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
1222         if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
1223             return d;
1224 
1225     return NULL;
1226 }
1227 
device_free(pa_bluetooth_device * d)1228 static void device_free(pa_bluetooth_device *d) {
1229     unsigned i;
1230 
1231     pa_assert(d);
1232 
1233     device_stop_waiting_for_profiles(d);
1234 
1235     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UNLINK], d);
1236 
1237     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
1238         pa_bluetooth_transport *t;
1239 
1240         if (!(t = d->transports[i]))
1241             continue;
1242 
1243         pa_bluetooth_transport_free(t);
1244     }
1245 
1246     if (d->uuids)
1247         pa_hashmap_free(d->uuids);
1248     if (d->a2dp_sink_endpoints)
1249         pa_hashmap_free(d->a2dp_sink_endpoints);
1250     if (d->a2dp_source_endpoints)
1251         pa_hashmap_free(d->a2dp_source_endpoints);
1252 
1253     pa_xfree(d->path);
1254     pa_xfree(d->alias);
1255     pa_xfree(d->address);
1256     pa_xfree(d->adapter_path);
1257     pa_xfree(d);
1258 }
1259 
device_remove(pa_bluetooth_discovery * y,const char * path)1260 static void device_remove(pa_bluetooth_discovery *y, const char *path) {
1261     pa_bluetooth_device *d;
1262 
1263     if (!(d = pa_hashmap_remove(y->devices, path)))
1264         pa_log_warn("Unknown device removed %s", path);
1265     else {
1266         pa_log_debug("Device %s removed", path);
1267         device_free(d);
1268     }
1269 }
1270 
device_set_valid(pa_bluetooth_device * device,bool valid)1271 static void device_set_valid(pa_bluetooth_device *device, bool valid) {
1272     bool old_any_connected;
1273 
1274     pa_assert(device);
1275 
1276     if (valid == device->valid)
1277         return;
1278 
1279     old_any_connected = pa_bluetooth_device_any_transport_connected(device);
1280     device->valid = valid;
1281 
1282     if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
1283         pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
1284 }
1285 
device_update_valid(pa_bluetooth_device * d)1286 static void device_update_valid(pa_bluetooth_device *d) {
1287     pa_assert(d);
1288 
1289     if (!d->properties_received) {
1290         pa_assert(!d->valid);
1291         return;
1292     }
1293 
1294     /* Check if mandatory properties are set. */
1295     if (!d->address || !d->adapter_path || !d->alias) {
1296         device_set_valid(d, false);
1297         return;
1298     }
1299 
1300     if (!d->adapter || !d->adapter->valid) {
1301         device_set_valid(d, false);
1302         return;
1303     }
1304 
1305     device_set_valid(d, true);
1306 }
1307 
device_set_adapter(pa_bluetooth_device * device,pa_bluetooth_adapter * adapter)1308 static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
1309     pa_assert(device);
1310 
1311     if (adapter == device->adapter)
1312         return;
1313 
1314     device->adapter = adapter;
1315 
1316     device_update_valid(device);
1317 }
1318 
append_battery_provider_properties(pa_bluetooth_device * d,DBusMessageIter * entry,bool only_percentage)1319 static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *entry, bool only_percentage) {
1320     static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
1321     DBusMessageIter dict;
1322 
1323     pa_assert_se(dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &interface_name));
1324 
1325     pa_assert_se(dbus_message_iter_open_container(entry, DBUS_TYPE_ARRAY,
1326                                                  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1327                                                  DBUS_TYPE_STRING_AS_STRING
1328                                                  DBUS_TYPE_VARIANT_AS_STRING
1329                                                  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1330                                                  &dict));
1331 
1332     pa_dbus_append_basic_variant_dict_entry(&dict, "Percentage", DBUS_TYPE_BYTE, &d->battery_level);
1333 
1334     if (!only_percentage) {
1335         pa_assert(d->battery_source);
1336         pa_dbus_append_basic_variant_dict_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &d->path);
1337         pa_dbus_append_basic_variant_dict_entry(&dict, "Source", DBUS_TYPE_STRING, &d->battery_source);
1338     }
1339 
1340     pa_assert_se(dbus_message_iter_close_container(entry, &dict));
1341 }
1342 
append_battery_provider(pa_bluetooth_device * d,DBusMessageIter * object)1343 static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object) {
1344     char *battery_path = device_battery_provider_path(d);
1345     DBusMessageIter array, entry;
1346 
1347     pa_assert_se(dbus_message_iter_append_basic(object, DBUS_TYPE_OBJECT_PATH, &battery_path));
1348 
1349     pa_assert_se(dbus_message_iter_open_container(object, DBUS_TYPE_ARRAY,
1350                                                   DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1351                                                   DBUS_TYPE_STRING_AS_STRING
1352                                                   DBUS_TYPE_ARRAY_AS_STRING
1353                                                   DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1354                                                   DBUS_TYPE_STRING_AS_STRING
1355                                                   DBUS_TYPE_VARIANT_AS_STRING
1356                                                   DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1357                                                   DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1358                                                   &array));
1359 
1360     pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry));
1361     append_battery_provider_properties(d, &entry, false);
1362     pa_assert_se(dbus_message_iter_close_container(&array, &entry));
1363     pa_assert_se(dbus_message_iter_close_container(object, &array));
1364 
1365     pa_xfree(battery_path);
1366 }
1367 
battery_provider_handler(DBusConnection * c,DBusMessage * m,void * userdata)1368 static DBusHandlerResult battery_provider_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1369     pa_bluetooth_adapter *a = userdata;
1370     DBusMessage *r = NULL;
1371     const char *path, *interface, *member;
1372 
1373     pa_assert(a);
1374 
1375     path = dbus_message_get_path(m);
1376     interface = dbus_message_get_interface(m);
1377     member = dbus_message_get_member(m);
1378 
1379     pa_log_debug("%s %s %s", path, interface, member);
1380 
1381     if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
1382         DBusMessageIter iter, array, object;
1383         pa_bluetooth_device *d;
1384         void *state;
1385 
1386         pa_assert_se(r = dbus_message_new_method_return(m));
1387 
1388         dbus_message_iter_init_append(r, &iter);
1389         pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
1390                                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1391                                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
1392                                                       DBUS_TYPE_ARRAY_AS_STRING
1393                                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1394                                                       DBUS_TYPE_STRING_AS_STRING
1395                                                       DBUS_TYPE_ARRAY_AS_STRING
1396                                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1397                                                       DBUS_TYPE_STRING_AS_STRING
1398                                                       DBUS_TYPE_VARIANT_AS_STRING
1399                                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1400                                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1401                                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1402                                                       &array));
1403 
1404         PA_HASHMAP_FOREACH(d, a->discovery->devices, state) {
1405 
1406             if (d->has_battery_level) {
1407                 pa_log_debug("%s: battery level  = %d", d->path, d->battery_level);
1408                 pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &object));
1409                 append_battery_provider(d, &object);
1410                 pa_assert_se(dbus_message_iter_close_container(&array, &object));
1411             }
1412         }
1413 
1414         pa_assert_se(dbus_message_iter_close_container(&iter, &array));
1415     } else
1416         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1417 
1418     pa_assert_se(dbus_connection_send(c, r, NULL));
1419     dbus_message_unref(r);
1420 
1421     return DBUS_HANDLER_RESULT_HANDLED;
1422 }
1423 
adapter_register_battery_provider(pa_bluetooth_adapter * a)1424 static void adapter_register_battery_provider(pa_bluetooth_adapter *a) {
1425     DBusMessage *m, *r;
1426     DBusError error;
1427 
1428     static const DBusObjectPathVTable vtable_profile = {
1429         .message_function = battery_provider_handler,
1430     };
1431 
1432     char *provider_path = adapter_battery_provider_path(a);
1433 
1434     pa_log_debug("Registering battery provider for %s at %s", a->path, provider_path);
1435 
1436     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path, &vtable_profile, a));
1437 
1438     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "RegisterBatteryProvider"));
1439     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1440 
1441     dbus_error_init(&error);
1442     if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1443         if (dbus_error_has_name(&error, DBUS_ERROR_UNKNOWN_METHOD))
1444             pa_log_notice("Could not find " BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE
1445                           ".RegisterBatteryProvider(), is bluetoothd started with experimental features enabled (-E flag)?");
1446         else
1447             pa_log_warn(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".RegisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1448         dbus_error_free(&error);
1449         dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1450     } else {
1451         dbus_message_unref(r);
1452         a->battery_provider_registered = true;
1453     }
1454 
1455     dbus_message_unref(m);
1456     pa_xfree(provider_path);
1457 }
1458 
adapter_deregister_battery_provider(pa_bluetooth_adapter * a)1459 static void adapter_deregister_battery_provider(pa_bluetooth_adapter *a) {
1460     DBusMessage *m, *r;
1461     DBusError error;
1462     char *provider_path;
1463 
1464     if (!a->battery_provider_registered) {
1465         pa_log_debug("No battery provider registered for %s", a->path);
1466         return;
1467     }
1468 
1469     provider_path = adapter_battery_provider_path(a);
1470 
1471     pa_log_debug("Deregistering battery provider at %s", provider_path);
1472 
1473     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "UnregisterBatteryProvider"));
1474     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1475 
1476     dbus_error_init(&error);
1477     if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1478         pa_log_error(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".UnregisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1479         dbus_error_free(&error);
1480     } else {
1481         dbus_message_unref(r);
1482         a->battery_provider_registered = false;
1483     }
1484 
1485     dbus_message_unref(m);
1486 
1487     dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1488 
1489     pa_xfree(provider_path);
1490 }
1491 
adapter_create(pa_bluetooth_discovery * y,const char * path)1492 static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
1493     pa_bluetooth_adapter *a;
1494 
1495     pa_assert(y);
1496     pa_assert(path);
1497 
1498     a = pa_xnew0(pa_bluetooth_adapter, 1);
1499     a->discovery = y;
1500     a->path = pa_xstrdup(path);
1501     a->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1502 
1503     pa_hashmap_put(y->adapters, a->path, a);
1504 
1505     return a;
1506 }
1507 
adapter_free(pa_bluetooth_adapter * a)1508 static void adapter_free(pa_bluetooth_adapter *a) {
1509     pa_bluetooth_device *d;
1510     void *state;
1511 
1512     pa_assert(a);
1513     pa_assert(a->discovery);
1514 
1515     adapter_deregister_battery_provider(a);
1516 
1517     PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
1518         if (d->adapter == a)
1519             device_set_adapter(d, NULL);
1520 
1521     pa_hashmap_free(a->uuids);
1522     pa_xfree(a->path);
1523     pa_xfree(a->address);
1524     pa_xfree(a);
1525 }
1526 
adapter_remove(pa_bluetooth_discovery * y,const char * path)1527 static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
1528     pa_bluetooth_adapter *a;
1529 
1530     if (!(a = pa_hashmap_remove(y->adapters, path)))
1531         pa_log_warn("Unknown adapter removed %s", path);
1532     else {
1533         pa_log_debug("Adapter %s removed", path);
1534         adapter_free(a);
1535     }
1536 }
1537 
parse_device_property(pa_bluetooth_device * d,DBusMessageIter * i)1538 static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
1539     const char *key;
1540     DBusMessageIter variant_i;
1541 
1542     pa_assert(d);
1543 
1544     key = check_variant_property(i);
1545     if (key == NULL) {
1546         pa_log_error("Received invalid property for device %s", d->path);
1547         return;
1548     }
1549 
1550     dbus_message_iter_recurse(i, &variant_i);
1551 
1552     switch (dbus_message_iter_get_arg_type(&variant_i)) {
1553 
1554         case DBUS_TYPE_STRING: {
1555             const char *value;
1556             dbus_message_iter_get_basic(&variant_i, &value);
1557 
1558             if (pa_streq(key, "Alias")) {
1559                 pa_xfree(d->alias);
1560                 d->alias = pa_xstrdup(value);
1561                 pa_log_debug("%s: %s", key, value);
1562             } else if (pa_streq(key, "Address")) {
1563                 if (d->properties_received) {
1564                     pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
1565                     return;
1566                 }
1567 
1568                 if (d->address) {
1569                     pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
1570                     return;
1571                 }
1572 
1573                 d->address = pa_xstrdup(value);
1574                 pa_log_debug("%s: %s", key, value);
1575             }
1576 
1577             break;
1578         }
1579 
1580         case DBUS_TYPE_OBJECT_PATH: {
1581             const char *value;
1582             dbus_message_iter_get_basic(&variant_i, &value);
1583 
1584             if (pa_streq(key, "Adapter")) {
1585 
1586                 if (d->properties_received) {
1587                     pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
1588                     return;
1589                 }
1590 
1591                 if (d->adapter_path) {
1592                     pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
1593                     return;
1594                 }
1595 
1596                 d->adapter_path = pa_xstrdup(value);
1597                 pa_log_debug("%s: %s", key, value);
1598             }
1599 
1600             break;
1601         }
1602 
1603         case DBUS_TYPE_UINT32: {
1604             uint32_t value;
1605             dbus_message_iter_get_basic(&variant_i, &value);
1606 
1607             if (pa_streq(key, "Class")) {
1608                 d->class_of_device = value;
1609                 pa_log_debug("%s: %d", key, value);
1610             }
1611 
1612             break;
1613         }
1614 
1615         case DBUS_TYPE_ARRAY: {
1616             DBusMessageIter ai;
1617             dbus_message_iter_recurse(&variant_i, &ai);
1618 
1619             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1620                 /* bluetoothd never removes UUIDs from a device object so we
1621                  * don't need to check for disappeared UUIDs here. */
1622                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1623                     const char *value;
1624                     char *uuid;
1625 
1626                     dbus_message_iter_get_basic(&ai, &value);
1627 
1628                     if (pa_hashmap_get(d->uuids, value)) {
1629                         dbus_message_iter_next(&ai);
1630                         continue;
1631                     }
1632 
1633                     uuid = pa_xstrdup(value);
1634                     pa_hashmap_put(d->uuids, uuid, uuid);
1635 
1636                     pa_log_debug("%s: %s", key, value);
1637                     dbus_message_iter_next(&ai);
1638                 }
1639             }
1640 
1641             break;
1642         }
1643     }
1644 }
1645 
parse_device_properties(pa_bluetooth_device * d,DBusMessageIter * i)1646 static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
1647     DBusMessageIter element_i;
1648 
1649     dbus_message_iter_recurse(i, &element_i);
1650 
1651     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1652         DBusMessageIter dict_i;
1653 
1654         dbus_message_iter_recurse(&element_i, &dict_i);
1655         parse_device_property(d, &dict_i);
1656         dbus_message_iter_next(&element_i);
1657     }
1658 
1659     if (!d->properties_received) {
1660         d->properties_received = true;
1661         device_update_valid(d);
1662 
1663         if (!d->address || !d->adapter_path || !d->alias)
1664             pa_log_error("Non-optional information missing for device %s", d->path);
1665     }
1666 }
1667 
parse_adapter_properties(pa_bluetooth_adapter * a,DBusMessageIter * i,bool is_property_change)1668 static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
1669     DBusMessageIter element_i;
1670 
1671     pa_assert(a);
1672 
1673     dbus_message_iter_recurse(i, &element_i);
1674 
1675     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1676         DBusMessageIter dict_i, variant_i;
1677         const char *key;
1678 
1679         dbus_message_iter_recurse(&element_i, &dict_i);
1680 
1681         key = check_variant_property(&dict_i);
1682         if (key == NULL) {
1683             pa_log_error("Received invalid property for adapter %s", a->path);
1684             return;
1685         }
1686 
1687         dbus_message_iter_recurse(&dict_i, &variant_i);
1688 
1689         if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
1690             const char *value;
1691 
1692             if (is_property_change) {
1693                 pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
1694                 return;
1695             }
1696 
1697             if (a->address) {
1698                 pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
1699                 return;
1700             }
1701 
1702             dbus_message_iter_get_basic(&variant_i, &value);
1703             a->address = pa_xstrdup(value);
1704             a->valid = true;
1705         } else if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_ARRAY) {
1706             DBusMessageIter ai;
1707             dbus_message_iter_recurse(&variant_i, &ai);
1708 
1709             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1710                 pa_hashmap_remove_all(a->uuids);
1711                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1712                     const char *value;
1713                     char *uuid;
1714 
1715                     dbus_message_iter_get_basic(&ai, &value);
1716 
1717                     if (pa_hashmap_get(a->uuids, value)) {
1718                         dbus_message_iter_next(&ai);
1719                         continue;
1720                     }
1721 
1722                     uuid = pa_xstrdup(value);
1723                     pa_hashmap_put(a->uuids, uuid, uuid);
1724 
1725                     pa_log_debug("%s: %s", key, value);
1726                     dbus_message_iter_next(&ai);
1727                 }
1728                 pa_hook_fire(pa_bluetooth_discovery_hook(a->discovery, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), a);
1729             }
1730         }
1731 
1732         dbus_message_iter_next(&element_i);
1733     }
1734 }
1735 
register_legacy_sbc_endpoint_reply(DBusPendingCall * pending,void * userdata)1736 static void register_legacy_sbc_endpoint_reply(DBusPendingCall *pending, void *userdata) {
1737     DBusMessage *r;
1738     pa_dbus_pending *p;
1739     pa_bluetooth_discovery *y;
1740     char *endpoint;
1741 
1742     pa_assert(pending);
1743     pa_assert_se(p = userdata);
1744     pa_assert_se(y = p->context_data);
1745     pa_assert_se(endpoint = p->call_data);
1746     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1747 
1748     if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1749         pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
1750         goto finish;
1751     }
1752 
1753     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1754         pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
1755                      pa_dbus_get_error_message(r));
1756         goto finish;
1757     }
1758 
1759 finish:
1760     dbus_message_unref(r);
1761 
1762     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1763     pa_dbus_pending_free(p);
1764 
1765     pa_xfree(endpoint);
1766 }
1767 
register_legacy_sbc_endpoint(pa_bluetooth_discovery * y,const pa_a2dp_endpoint_conf * endpoint_conf,const char * path,const char * endpoint,const char * uuid)1768 static void register_legacy_sbc_endpoint(pa_bluetooth_discovery *y, const pa_a2dp_endpoint_conf *endpoint_conf, const char *path, const char *endpoint, const char *uuid) {
1769     DBusMessage *m;
1770     DBusMessageIter i, d;
1771     uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
1772     size_t capabilities_size;
1773     uint8_t codec_id;
1774 
1775     pa_log_debug("Registering %s on adapter %s", endpoint, path);
1776 
1777     codec_id = endpoint_conf->id.codec_id;
1778     capabilities_size = endpoint_conf->fill_capabilities(capabilities);
1779     pa_assert(capabilities_size != 0);
1780 
1781     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
1782 
1783     dbus_message_iter_init_append(m, &i);
1784     pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
1785     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1786                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1787                                      DBUS_TYPE_STRING_AS_STRING
1788                                      DBUS_TYPE_VARIANT_AS_STRING
1789                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1790                                      &d);
1791     pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
1792     pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
1793     pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
1794 
1795     dbus_message_iter_close_container(&i, &d);
1796 
1797     send_and_add_to_pending(y, m, register_legacy_sbc_endpoint_reply, pa_xstrdup(endpoint));
1798 }
1799 
register_application_reply(DBusPendingCall * pending,void * userdata)1800 static void register_application_reply(DBusPendingCall *pending, void *userdata) {
1801     DBusMessage *r;
1802     pa_dbus_pending *p;
1803     pa_bluetooth_adapter *a;
1804     pa_bluetooth_discovery *y;
1805     char *path;
1806     bool fallback = true;
1807 
1808     pa_assert(pending);
1809     pa_assert_se(p = userdata);
1810     pa_assert_se(y = p->context_data);
1811     pa_assert_se(path = p->call_data);
1812     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1813 
1814     if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1815         pa_log_info("Couldn't register media application for adapter %s because it is disabled in BlueZ", path);
1816         goto finish;
1817     }
1818 
1819     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1820         pa_log_warn(BLUEZ_MEDIA_INTERFACE ".RegisterApplication() failed: %s: %s",
1821                 dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1822         pa_log_warn("Couldn't register media application for adapter %s", path);
1823         goto finish;
1824     }
1825 
1826     a = pa_hashmap_get(y->adapters, path);
1827     if (!a) {
1828         pa_log_error("Couldn't register media application for adapter %s because it does not exist anymore", path);
1829         goto finish;
1830     }
1831 
1832     fallback = false;
1833     a->application_registered = true;
1834     pa_log_debug("Media application for adapter %s was successfully registered", path);
1835 
1836 finish:
1837     dbus_message_unref(r);
1838 
1839     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1840     pa_dbus_pending_free(p);
1841 
1842     if (fallback) {
1843         /* If bluez does not support RegisterApplication, fallback to old legacy API with just one SBC codec */
1844         const pa_a2dp_endpoint_conf *endpoint_conf;
1845         endpoint_conf = pa_bluetooth_get_a2dp_endpoint_conf("sbc");
1846         pa_assert(endpoint_conf);
1847         register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SINK_ENDPOINT "/sbc",
1848                 PA_BLUETOOTH_UUID_A2DP_SINK);
1849         register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SOURCE_ENDPOINT "/sbc",
1850                 PA_BLUETOOTH_UUID_A2DP_SOURCE);
1851         pa_log_warn("Only SBC codec is available for A2DP profiles");
1852     }
1853 
1854     pa_xfree(path);
1855 }
1856 
register_application(pa_bluetooth_adapter * a)1857 static void register_application(pa_bluetooth_adapter *a) {
1858     DBusMessage *m;
1859     DBusMessageIter i, d;
1860     const char *object_manager_path = A2DP_OBJECT_MANAGER_PATH;
1861 
1862     if (a->application_registered) {
1863         pa_log_info("Media application is already registered for adapter %s", a->path);
1864         return;
1865     }
1866 
1867     pa_log_debug("Registering media application for adapter %s", a->path);
1868 
1869     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path,
1870                 BLUEZ_MEDIA_INTERFACE, "RegisterApplication"));
1871 
1872     dbus_message_iter_init_append(m, &i);
1873     pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object_manager_path));
1874     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1875                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1876                                      DBUS_TYPE_STRING_AS_STRING
1877                                      DBUS_TYPE_VARIANT_AS_STRING
1878                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1879                                      &d);
1880     dbus_message_iter_close_container(&i, &d);
1881 
1882     send_and_add_to_pending(a->discovery, m, register_application_reply, pa_xstrdup(a->path));
1883 }
1884 
parse_remote_endpoint_properties(pa_bluetooth_discovery * y,const char * endpoint,DBusMessageIter * i)1885 static void parse_remote_endpoint_properties(pa_bluetooth_discovery *y, const char *endpoint, DBusMessageIter *i) {
1886     DBusMessageIter element_i;
1887     pa_bluetooth_device *device;
1888     pa_hashmap *codec_endpoints;
1889     pa_hashmap *endpoints;
1890     pa_a2dp_codec_id *a2dp_codec_id;
1891     pa_a2dp_codec_capabilities *a2dp_codec_capabilities;
1892     const char *uuid = NULL;
1893     const char *device_path = NULL;
1894     uint8_t codec_id = 0;
1895     bool have_codec_id = false;
1896     const uint8_t *capabilities = NULL;
1897     int capabilities_size = 0;
1898 
1899     pa_log_debug("Parsing remote endpoint %s", endpoint);
1900 
1901     dbus_message_iter_recurse(i, &element_i);
1902 
1903     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1904         DBusMessageIter dict_i, variant_i;
1905         const char *key;
1906 
1907         dbus_message_iter_recurse(&element_i, &dict_i);
1908 
1909         key = check_variant_property(&dict_i);
1910         if (key == NULL) {
1911             pa_log_error("Received invalid property for remote endpoint %s", endpoint);
1912             return;
1913         }
1914 
1915         dbus_message_iter_recurse(&dict_i, &variant_i);
1916 
1917         if (pa_streq(key, "UUID")) {
1918             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_STRING) {
1919                 pa_log_warn("Remote endpoint %s property 'UUID' is not string, ignoring", endpoint);
1920                 return;
1921             }
1922 
1923             dbus_message_iter_get_basic(&variant_i, &uuid);
1924         } else if (pa_streq(key, "Codec")) {
1925             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BYTE) {
1926                 pa_log_warn("Remote endpoint %s property 'Codec' is not byte, ignoring", endpoint);
1927                 return;
1928             }
1929 
1930             dbus_message_iter_get_basic(&variant_i, &codec_id);
1931             have_codec_id = true;
1932         } else if (pa_streq(key, "Capabilities")) {
1933             DBusMessageIter array;
1934 
1935             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_ARRAY) {
1936                 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array, ignoring", endpoint);
1937                 return;
1938             }
1939 
1940             dbus_message_iter_recurse(&variant_i, &array);
1941             if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) {
1942                 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array of bytes, ignoring", endpoint);
1943                 return;
1944             }
1945 
1946             dbus_message_iter_get_fixed_array(&array, &capabilities, &capabilities_size);
1947         } else if (pa_streq(key, "Device")) {
1948             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_OBJECT_PATH) {
1949                 pa_log_warn("Remote endpoint %s property 'Device' is not path, ignoring", endpoint);
1950                 return;
1951             }
1952 
1953             dbus_message_iter_get_basic(&variant_i, &device_path);
1954         }
1955 
1956         dbus_message_iter_next(&element_i);
1957     }
1958 
1959     if (!uuid) {
1960         pa_log_warn("Remote endpoint %s does not have property 'UUID', ignoring", endpoint);
1961         return;
1962     }
1963 
1964     if (!have_codec_id) {
1965         pa_log_warn("Remote endpoint %s does not have property 'Codec', ignoring", endpoint);
1966         return;
1967     }
1968 
1969     if (!capabilities || !capabilities_size) {
1970         pa_log_warn("Remote endpoint %s does not have property 'Capabilities', ignoring", endpoint);
1971         return;
1972     }
1973 
1974     if (!device_path) {
1975         pa_log_warn("Remote endpoint %s does not have property 'Device', ignoring", endpoint);
1976         return;
1977     }
1978 
1979     device = pa_hashmap_get(y->devices, device_path);
1980     if (!device) {
1981         pa_log_warn("Device for remote endpoint %s was not found", endpoint);
1982         return;
1983     }
1984 
1985     if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
1986         codec_endpoints = device->a2dp_sink_endpoints;
1987     } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) {
1988         codec_endpoints = device->a2dp_source_endpoints;
1989     } else {
1990         pa_log_warn("Remote endpoint %s does not have valid property 'UUID', ignoring", endpoint);
1991         return;
1992     }
1993 
1994     if (capabilities_size < 0 || capabilities_size > MAX_A2DP_CAPS_SIZE) {
1995         pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
1996         return;
1997     }
1998 
1999     a2dp_codec_id = pa_xmalloc0(sizeof(*a2dp_codec_id));
2000     a2dp_codec_id->codec_id = codec_id;
2001     if (codec_id == A2DP_CODEC_VENDOR) {
2002         if ((size_t)capabilities_size < sizeof(a2dp_vendor_codec_t)) {
2003             pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
2004             pa_xfree(a2dp_codec_id);
2005             return;
2006         }
2007         a2dp_codec_id->vendor_id = A2DP_GET_VENDOR_ID(*(a2dp_vendor_codec_t *)capabilities);
2008         a2dp_codec_id->vendor_codec_id = A2DP_GET_CODEC_ID(*(a2dp_vendor_codec_t *)capabilities);
2009     } else {
2010         a2dp_codec_id->vendor_id = 0;
2011         a2dp_codec_id->vendor_codec_id = 0;
2012     }
2013 
2014     if (!pa_bluetooth_a2dp_codec_is_available(a2dp_codec_id, pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))) {
2015         pa_xfree(a2dp_codec_id);
2016         return;
2017     }
2018 
2019     a2dp_codec_capabilities = pa_xmalloc0(sizeof(*a2dp_codec_capabilities) + capabilities_size);
2020     a2dp_codec_capabilities->size = capabilities_size;
2021     memcpy(a2dp_codec_capabilities->buffer, capabilities, capabilities_size);
2022 
2023     endpoints = pa_hashmap_get(codec_endpoints, a2dp_codec_id);
2024     if (!endpoints) {
2025         endpoints = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, pa_xfree);
2026         pa_hashmap_put(codec_endpoints, a2dp_codec_id, endpoints);
2027     }
2028 
2029     if (pa_hashmap_remove_and_free(endpoints, endpoint) >= 0)
2030         pa_log_debug("Replacing existing remote endpoint %s", endpoint);
2031     pa_hashmap_put(endpoints, pa_xstrdup(endpoint), a2dp_codec_capabilities);
2032 }
2033 
parse_interfaces_and_properties(pa_bluetooth_discovery * y,DBusMessageIter * dict_i)2034 static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
2035     DBusMessageIter element_i;
2036     const char *path;
2037     void *state;
2038     pa_bluetooth_device *d;
2039 
2040     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
2041     dbus_message_iter_get_basic(dict_i, &path);
2042 
2043     pa_assert_se(dbus_message_iter_next(dict_i));
2044     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
2045 
2046     dbus_message_iter_recurse(dict_i, &element_i);
2047 
2048     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2049         DBusMessageIter iface_i;
2050         const char *interface;
2051 
2052         dbus_message_iter_recurse(&element_i, &iface_i);
2053 
2054         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
2055         dbus_message_iter_get_basic(&iface_i, &interface);
2056 
2057         pa_assert_se(dbus_message_iter_next(&iface_i));
2058         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
2059 
2060         if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
2061             pa_bluetooth_adapter *a;
2062 
2063             if ((a = pa_hashmap_get(y->adapters, path))) {
2064                 pa_log_error("Found duplicated D-Bus path for adapter %s", path);
2065                 return;
2066             } else
2067                 a = adapter_create(y, path);
2068 
2069             pa_log_debug("Adapter %s found", path);
2070 
2071             parse_adapter_properties(a, &iface_i, false);
2072 
2073             if (!a->valid)
2074                 return;
2075 
2076             register_application(a);
2077             adapter_register_battery_provider(a);
2078         } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
2079 
2080             if ((d = pa_hashmap_get(y->devices, path))) {
2081                 if (d->properties_received) {
2082                     pa_log_error("Found duplicated D-Bus path for device %s", path);
2083                     return;
2084                 }
2085             } else
2086                 d = device_create(y, path);
2087 
2088             pa_log_debug("Device %s found", d->path);
2089 
2090             parse_device_properties(d, &iface_i);
2091         } else if (pa_streq(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2092             parse_remote_endpoint_properties(y, path, &iface_i);
2093         } else
2094             pa_log_debug("Unknown interface %s found, skipping", interface);
2095 
2096         dbus_message_iter_next(&element_i);
2097     }
2098 
2099     PA_HASHMAP_FOREACH(d, y->devices, state) {
2100         if (d->properties_received && !d->tried_to_link_with_adapter) {
2101             if (d->adapter_path) {
2102                 device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
2103 
2104                 if (!d->adapter)
2105                     pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
2106                 else if (!d->adapter->valid)
2107                     pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
2108             }
2109 
2110             d->tried_to_link_with_adapter = true;
2111         }
2112     }
2113 
2114     return;
2115 }
2116 
pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery * y,bool is_running)2117 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
2118     pa_assert(y);
2119 
2120     pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
2121     if (y->headset_backend != HEADSET_BACKEND_AUTO)
2122         return;
2123 
2124     pa_bluetooth_native_backend_enable_shared_profiles(y->native_backend, !is_running);
2125 
2126     /* If ofono starts running, all devices that might be connected to the HS roles or HFP AG role
2127      * need to be disconnected, so that the devices can be handled by ofono */
2128     if (is_running) {
2129         void *state;
2130         pa_bluetooth_device *d;
2131 
2132         PA_HASHMAP_FOREACH(d, y->devices, state) {
2133             if (pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_AG) || pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_HF)) {
2134                 DBusMessage *m;
2135 
2136                 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, BLUEZ_DEVICE_INTERFACE, "Disconnect"));
2137                 dbus_message_set_no_reply(m, true);
2138                 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
2139                 dbus_message_unref(m);
2140             }
2141         }
2142     }
2143 }
2144 
get_managed_objects_reply(DBusPendingCall * pending,void * userdata)2145 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
2146     pa_dbus_pending *p;
2147     pa_bluetooth_discovery *y;
2148     DBusMessage *r;
2149     DBusMessageIter arg_i, element_i;
2150 
2151     pa_assert_se(p = userdata);
2152     pa_assert_se(y = p->context_data);
2153     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
2154 
2155     if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
2156         pa_log_warn("BlueZ D-Bus ObjectManager not available");
2157         goto finish;
2158     }
2159 
2160     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
2161         pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
2162         goto finish;
2163     }
2164 
2165     if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
2166         pa_log_error("Invalid reply signature for GetManagedObjects()");
2167         goto finish;
2168     }
2169 
2170     dbus_message_iter_recurse(&arg_i, &element_i);
2171     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2172         DBusMessageIter dict_i;
2173 
2174         dbus_message_iter_recurse(&element_i, &dict_i);
2175 
2176         parse_interfaces_and_properties(y, &dict_i);
2177 
2178         dbus_message_iter_next(&element_i);
2179     }
2180 
2181     y->objects_listed = true;
2182 
2183     if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
2184         y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
2185     if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
2186         y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
2187 
2188 finish:
2189     dbus_message_unref(r);
2190 
2191     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
2192     pa_dbus_pending_free(p);
2193 }
2194 
get_managed_objects(pa_bluetooth_discovery * y)2195 static void get_managed_objects(pa_bluetooth_discovery *y) {
2196     DBusMessage *m;
2197 
2198     pa_assert(y);
2199 
2200     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", DBUS_INTERFACE_OBJECT_MANAGER,
2201                                                   "GetManagedObjects"));
2202     send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
2203 }
2204 
pa_bluetooth_discovery_hook(pa_bluetooth_discovery * y,pa_bluetooth_hook_t hook)2205 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
2206     pa_assert(y);
2207     pa_assert(PA_REFCNT_VALUE(y) > 0);
2208 
2209     return &y->hooks[hook];
2210 }
2211 
filter_cb(DBusConnection * bus,DBusMessage * m,void * userdata)2212 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
2213     pa_bluetooth_discovery *y;
2214     DBusError err;
2215 
2216     pa_assert(bus);
2217     pa_assert(m);
2218     pa_assert_se(y = userdata);
2219 
2220     dbus_error_init(&err);
2221 
2222     if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
2223         const char *name, *old_owner, *new_owner;
2224 
2225         if (!dbus_message_get_args(m, &err,
2226                                    DBUS_TYPE_STRING, &name,
2227                                    DBUS_TYPE_STRING, &old_owner,
2228                                    DBUS_TYPE_STRING, &new_owner,
2229                                    DBUS_TYPE_INVALID)) {
2230             pa_log_error("Failed to parse " DBUS_INTERFACE_DBUS ".NameOwnerChanged: %s", err.message);
2231             goto fail;
2232         }
2233 
2234         if (pa_streq(name, BLUEZ_SERVICE)) {
2235             if (old_owner && *old_owner) {
2236                 pa_log_debug("Bluetooth daemon disappeared");
2237                 pa_hashmap_remove_all(y->devices);
2238                 pa_hashmap_remove_all(y->adapters);
2239                 y->objects_listed = false;
2240                 if (y->ofono_backend) {
2241                     pa_bluetooth_ofono_backend_free(y->ofono_backend);
2242                     y->ofono_backend = NULL;
2243                 }
2244                 if (y->native_backend) {
2245                     pa_bluetooth_native_backend_free(y->native_backend);
2246                     y->native_backend = NULL;
2247                 }
2248             }
2249 
2250             if (new_owner && *new_owner) {
2251                 pa_log_debug("Bluetooth daemon appeared");
2252                 get_managed_objects(y);
2253             }
2254         }
2255 
2256         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2257     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded")) {
2258         DBusMessageIter arg_i;
2259 
2260         if (!y->objects_listed)
2261             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2262 
2263         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
2264             pa_log_error("Invalid signature found in InterfacesAdded");
2265             goto fail;
2266         }
2267 
2268         parse_interfaces_and_properties(y, &arg_i);
2269 
2270         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2271     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved")) {
2272         const char *p;
2273         DBusMessageIter arg_i;
2274         DBusMessageIter element_i;
2275 
2276         if (!y->objects_listed)
2277             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2278 
2279         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
2280             pa_log_error("Invalid signature found in InterfacesRemoved");
2281             goto fail;
2282         }
2283 
2284         dbus_message_iter_get_basic(&arg_i, &p);
2285 
2286         pa_assert_se(dbus_message_iter_next(&arg_i));
2287         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2288 
2289         dbus_message_iter_recurse(&arg_i, &element_i);
2290 
2291         while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
2292             const char *iface;
2293 
2294             dbus_message_iter_get_basic(&element_i, &iface);
2295 
2296             if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
2297                 device_remove(y, p);
2298             else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
2299                 adapter_remove(y, p);
2300             else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
2301                 remote_endpoint_remove(y, p);
2302 
2303             dbus_message_iter_next(&element_i);
2304         }
2305 
2306         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2307 
2308     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
2309         DBusMessageIter arg_i;
2310         const char *iface;
2311 
2312         if (!y->objects_listed)
2313             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2314 
2315         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
2316             pa_log_error("Invalid signature found in PropertiesChanged");
2317             goto fail;
2318         }
2319 
2320         dbus_message_iter_get_basic(&arg_i, &iface);
2321 
2322         pa_assert_se(dbus_message_iter_next(&arg_i));
2323         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2324 
2325         if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
2326             pa_bluetooth_adapter *a;
2327 
2328             pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
2329 
2330             if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
2331                 pa_log_warn("Properties changed in unknown adapter");
2332                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2333             }
2334 
2335             parse_adapter_properties(a, &arg_i, true);
2336 
2337         } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
2338             pa_bluetooth_device *d;
2339 
2340             pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
2341 
2342             if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
2343                 pa_log_warn("Properties changed in unknown device");
2344                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2345             }
2346 
2347             if (!d->properties_received)
2348                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2349 
2350             parse_device_properties(d, &arg_i);
2351         } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
2352             pa_bluetooth_transport *t;
2353 
2354             pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
2355 
2356             if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
2357                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2358 
2359             parse_transport_properties(t, &arg_i);
2360         } else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2361             pa_log_info("Properties changed in remote endpoint %s", dbus_message_get_path(m));
2362 
2363             parse_remote_endpoint_properties(y, dbus_message_get_path(m), &arg_i);
2364         }
2365 
2366         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2367     }
2368 
2369 fail:
2370     dbus_error_free(&err);
2371 
2372     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2373 }
2374 
pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile)2375 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
2376     switch(profile) {
2377         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2378             return "a2dp_sink";
2379         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2380             return "a2dp_source";
2381         case PA_BLUETOOTH_PROFILE_HSP_HS:
2382             return "headset_head_unit";
2383         case PA_BLUETOOTH_PROFILE_HSP_AG:
2384             return "headset_audio_gateway";
2385         case PA_BLUETOOTH_PROFILE_HFP_HF:
2386             return "handsfree_head_unit";
2387         case PA_BLUETOOTH_PROFILE_HFP_AG:
2388             return "handsfree_audio_gateway";
2389         case PA_BLUETOOTH_PROFILE_OFF:
2390             return "off";
2391     }
2392 
2393     return NULL;
2394 }
2395 
2396 /* Returns true when PA has to perform attenuation, false if this is the
2397  * responsibility of the peer.
2398  *
2399  * `peer_profile` is the profile of the peer.
2400  *
2401  * When the peer is in the HFP/HSP Audio Gateway role (PA is in headset role) PA
2402  * has to perform attenuation on both the incoming and outgoing stream. In the
2403  * HandsFree/HeadSet role both are attenuated on the peer.
2404  */
pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile)2405 bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile) {
2406     switch(peer_profile) {
2407         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2408             return false;
2409         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2410             return true;
2411         case PA_BLUETOOTH_PROFILE_HFP_HF:
2412         case PA_BLUETOOTH_PROFILE_HSP_HS:
2413             return false;
2414         case PA_BLUETOOTH_PROFILE_HFP_AG:
2415         case PA_BLUETOOTH_PROFILE_HSP_AG:
2416             return true;
2417         case PA_BLUETOOTH_PROFILE_OFF:
2418             pa_assert_not_reached();
2419     }
2420     pa_assert_not_reached();
2421 }
2422 
pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile)2423 bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile) {
2424     return profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2425 }
2426 
a2dp_sep_to_a2dp_endpoint_conf(const char * endpoint)2427 static const pa_a2dp_endpoint_conf *a2dp_sep_to_a2dp_endpoint_conf(const char *endpoint) {
2428     const char *codec_name;
2429 
2430     if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
2431         codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
2432     else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
2433         codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
2434     else
2435         return NULL;
2436 
2437     return pa_bluetooth_get_a2dp_endpoint_conf(codec_name);
2438 }
2439 
endpoint_set_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2440 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2441     pa_bluetooth_discovery *y = userdata;
2442     pa_bluetooth_device *d;
2443     pa_bluetooth_transport *t;
2444     const pa_a2dp_endpoint_conf *endpoint_conf = NULL;
2445     const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
2446     const uint8_t *config = NULL;
2447     int size = 0;
2448     pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
2449     DBusMessageIter args, props;
2450     DBusMessage *r;
2451 
2452     if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
2453         pa_log_error("Invalid signature for method SetConfiguration()");
2454         goto fail2;
2455     }
2456 
2457     dbus_message_iter_get_basic(&args, &path);
2458 
2459     if (pa_hashmap_get(y->transports, path)) {
2460         pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
2461         goto fail2;
2462     }
2463 
2464     pa_assert_se(dbus_message_iter_next(&args));
2465 
2466     dbus_message_iter_recurse(&args, &props);
2467     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
2468         goto fail;
2469 
2470     endpoint_path = dbus_message_get_path(m);
2471 
2472     /* Read transport properties */
2473     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
2474         const char *key;
2475         DBusMessageIter value, entry;
2476         int var;
2477 
2478         dbus_message_iter_recurse(&props, &entry);
2479         dbus_message_iter_get_basic(&entry, &key);
2480 
2481         dbus_message_iter_next(&entry);
2482         dbus_message_iter_recurse(&entry, &value);
2483 
2484         var = dbus_message_iter_get_arg_type(&value);
2485 
2486         if (pa_streq(key, "UUID")) {
2487             if (var != DBUS_TYPE_STRING) {
2488                 pa_log_error("Property %s of wrong type %c", key, (char)var);
2489                 goto fail;
2490             }
2491 
2492             dbus_message_iter_get_basic(&value, &uuid);
2493 
2494             if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
2495                 p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2496             else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
2497                 p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
2498 
2499             if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
2500                 (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
2501                 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
2502                 goto fail;
2503             }
2504         } else if (pa_streq(key, "Device")) {
2505             if (var != DBUS_TYPE_OBJECT_PATH) {
2506                 pa_log_error("Property %s of wrong type %c", key, (char)var);
2507                 goto fail;
2508             }
2509 
2510             dbus_message_iter_get_basic(&value, &dev_path);
2511         } else if (pa_streq(key, "Configuration")) {
2512             DBusMessageIter array;
2513 
2514             if (var != DBUS_TYPE_ARRAY) {
2515                 pa_log_error("Property %s of wrong type %c", key, (char)var);
2516                 goto fail;
2517             }
2518 
2519             dbus_message_iter_recurse(&value, &array);
2520             var = dbus_message_iter_get_arg_type(&array);
2521             if (var != DBUS_TYPE_BYTE) {
2522                 pa_log_error("%s is an array of wrong type %c", key, (char)var);
2523                 goto fail;
2524             }
2525 
2526             dbus_message_iter_get_fixed_array(&array, &config, &size);
2527 
2528             endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2529             pa_assert(endpoint_conf);
2530 
2531             if (!endpoint_conf->is_configuration_valid(config, size))
2532                 goto fail;
2533         }
2534 
2535         dbus_message_iter_next(&props);
2536     }
2537 
2538     if (!endpoint_conf)
2539         goto fail2;
2540 
2541     if ((d = pa_hashmap_get(y->devices, dev_path))) {
2542         if (!d->valid) {
2543             pa_log_error("Information about device %s is invalid", dev_path);
2544             goto fail2;
2545         }
2546     } else {
2547         /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
2548         pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
2549         d = device_create(y, dev_path);
2550     }
2551 
2552     if (d->transports[p] != NULL) {
2553         pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
2554         goto fail2;
2555     }
2556 
2557     sender = dbus_message_get_sender(m);
2558 
2559     pa_assert_se(r = dbus_message_new_method_return(m));
2560     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2561     dbus_message_unref(r);
2562 
2563     t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
2564     t->acquire = bluez5_transport_acquire_cb;
2565     t->release = bluez5_transport_release_cb;
2566     /* A2DP Absolute Volume is optional but BlueZ unconditionally reports
2567      * feature category 2, meaning supporting it is mandatory.
2568      * PulseAudio can and should perform the attenuation anyway in
2569      * the source role as it is the audio rendering device.
2570      */
2571     t->set_source_volume = pa_bluetooth_transport_set_source_volume;
2572 
2573     pa_bluetooth_transport_reconfigure(t, &endpoint_conf->bt_codec, a2dp_transport_write, NULL);
2574     pa_bluetooth_transport_put(t);
2575 
2576     pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2577     pa_log_info("Selected codec: %s", endpoint_conf->bt_codec.name);
2578 
2579     return NULL;
2580 
2581 fail:
2582     pa_log_error("Endpoint SetConfiguration(): invalid arguments");
2583 
2584 fail2:
2585     pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to set configuration"));
2586     return r;
2587 }
2588 
endpoint_select_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2589 static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2590     pa_bluetooth_discovery *y = userdata;
2591     const char *endpoint_path;
2592     uint8_t *cap;
2593     int size;
2594     const pa_a2dp_endpoint_conf *endpoint_conf;
2595     uint8_t config[MAX_A2DP_CAPS_SIZE];
2596     uint8_t *config_ptr = config;
2597     size_t config_size;
2598     DBusMessage *r;
2599     DBusError err;
2600 
2601     endpoint_path = dbus_message_get_path(m);
2602 
2603     dbus_error_init(&err);
2604 
2605     if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
2606         pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
2607         dbus_error_free(&err);
2608         goto fail;
2609     }
2610 
2611     endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2612     pa_assert(endpoint_conf);
2613 
2614     config_size = endpoint_conf->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
2615     if (config_size == 0)
2616         goto fail;
2617 
2618     pa_assert_se(r = dbus_message_new_method_return(m));
2619     pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
2620 
2621     return r;
2622 
2623 fail:
2624     pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to select configuration"));
2625     return r;
2626 }
2627 
endpoint_clear_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2628 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2629     pa_bluetooth_discovery *y = userdata;
2630     pa_bluetooth_transport *t;
2631     DBusMessage *r = NULL;
2632     DBusError err;
2633     const char *path;
2634 
2635     dbus_error_init(&err);
2636 
2637     if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
2638         pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
2639         dbus_error_free(&err);
2640         goto fail;
2641     }
2642 
2643     if ((t = pa_hashmap_get(y->transports, path))) {
2644         pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2645         pa_bluetooth_transport_free(t);
2646     }
2647 
2648     if (!dbus_message_get_no_reply(m))
2649         pa_assert_se(r = dbus_message_new_method_return(m));
2650 
2651     return r;
2652 
2653 fail:
2654     if (!dbus_message_get_no_reply(m))
2655         pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to clear configuration"));
2656     return r;
2657 }
2658 
endpoint_release(DBusConnection * conn,DBusMessage * m,void * userdata)2659 static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
2660     DBusMessage *r = NULL;
2661 
2662     /* From doc/media-api.txt in bluez:
2663      *
2664      *    This method gets called when the service daemon
2665      *    unregisters the endpoint. An endpoint can use it to do
2666      *    cleanup tasks. There is no need to unregister the
2667      *    endpoint, because when this method gets called it has
2668      *    already been unregistered.
2669      *
2670      * We don't have any cleanup to do. */
2671 
2672     /* Reply only if requested. Generally bluetoothd doesn't request a reply
2673      * to the Release() call. Sending replies when not requested on the system
2674      * bus tends to cause errors in syslog from dbus-daemon, because it
2675      * doesn't let unexpected replies through, so it's important to have this
2676      * check here. */
2677     if (!dbus_message_get_no_reply(m))
2678         pa_assert_se(r = dbus_message_new_method_return(m));
2679 
2680     return r;
2681 }
2682 
endpoint_handler(DBusConnection * c,DBusMessage * m,void * userdata)2683 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2684     struct pa_bluetooth_discovery *y = userdata;
2685     DBusMessage *r = NULL;
2686     const char *path, *interface, *member;
2687 
2688     pa_assert(y);
2689 
2690     path = dbus_message_get_path(m);
2691     interface = dbus_message_get_interface(m);
2692     member = dbus_message_get_member(m);
2693 
2694     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2695 
2696     if (!a2dp_sep_to_a2dp_endpoint_conf(path))
2697         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2698 
2699     if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2700         const char *xml = ENDPOINT_INTROSPECT_XML;
2701 
2702         pa_assert_se(r = dbus_message_new_method_return(m));
2703         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2704 
2705     } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
2706         r = endpoint_set_configuration(c, m, userdata);
2707     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
2708         r = endpoint_select_configuration(c, m, userdata);
2709     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
2710         r = endpoint_clear_configuration(c, m, userdata);
2711     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
2712         r = endpoint_release(c, m, userdata);
2713     else
2714         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2715 
2716     if (r) {
2717         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2718         dbus_message_unref(r);
2719     }
2720 
2721     return DBUS_HANDLER_RESULT_HANDLED;
2722 }
2723 
endpoint_init(pa_bluetooth_discovery * y,const char * endpoint)2724 static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
2725     static const DBusObjectPathVTable vtable_endpoint = {
2726         .message_function = endpoint_handler,
2727     };
2728 
2729     pa_assert(y);
2730     pa_assert(endpoint);
2731 
2732     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
2733                                                       &vtable_endpoint, y));
2734 }
2735 
endpoint_done(pa_bluetooth_discovery * y,const char * endpoint)2736 static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
2737     pa_assert(y);
2738     pa_assert(endpoint);
2739 
2740     dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
2741 }
2742 
append_a2dp_object(DBusMessageIter * iter,const char * endpoint,const char * uuid,uint8_t codec_id,uint8_t * capabilities,uint8_t capabilities_size)2743 static void append_a2dp_object(DBusMessageIter *iter, const char *endpoint, const char *uuid, uint8_t codec_id, uint8_t *capabilities, uint8_t capabilities_size) {
2744     const char *interface_name = BLUEZ_MEDIA_ENDPOINT_INTERFACE;
2745     DBusMessageIter object, array, entry, dict;
2746 
2747     dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &object);
2748     pa_assert_se(dbus_message_iter_append_basic(&object, DBUS_TYPE_OBJECT_PATH, &endpoint));
2749 
2750     dbus_message_iter_open_container(&object, DBUS_TYPE_ARRAY,
2751                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2752                                      DBUS_TYPE_STRING_AS_STRING
2753                                      DBUS_TYPE_ARRAY_AS_STRING
2754                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2755                                      DBUS_TYPE_STRING_AS_STRING
2756                                      DBUS_TYPE_VARIANT_AS_STRING
2757                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2758                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2759                                      &array);
2760 
2761     dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
2762     pa_assert_se(dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &interface_name));
2763 
2764     dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
2765                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2766                                      DBUS_TYPE_STRING_AS_STRING
2767                                      DBUS_TYPE_VARIANT_AS_STRING
2768                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2769                                      &dict);
2770 
2771     pa_dbus_append_basic_variant_dict_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
2772     pa_dbus_append_basic_variant_dict_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec_id);
2773     pa_dbus_append_basic_array_variant_dict_entry(&dict, "Capabilities", DBUS_TYPE_BYTE,
2774             capabilities, capabilities_size);
2775 
2776     dbus_message_iter_close_container(&entry, &dict);
2777     dbus_message_iter_close_container(&array, &entry);
2778     dbus_message_iter_close_container(&object, &array);
2779     dbus_message_iter_close_container(iter, &object);
2780 }
2781 
object_manager_handler(DBusConnection * c,DBusMessage * m,void * userdata)2782 static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2783     struct pa_bluetooth_discovery *y = userdata;
2784     DBusMessage *r;
2785     const char *path, *interface, *member;
2786 
2787     pa_assert(y);
2788 
2789     path = dbus_message_get_path(m);
2790     interface = dbus_message_get_interface(m);
2791     member = dbus_message_get_member(m);
2792 
2793     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2794 
2795     if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2796         const char *xml = OBJECT_MANAGER_INTROSPECT_XML;
2797 
2798         pa_assert_se(r = dbus_message_new_method_return(m));
2799         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2800     } else if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
2801         DBusMessageIter iter, array;
2802         int i;
2803 
2804         pa_assert_se(r = dbus_message_new_method_return(m));
2805 
2806         dbus_message_iter_init_append(r, &iter);
2807         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
2808                                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2809                                          DBUS_TYPE_OBJECT_PATH_AS_STRING
2810                                          DBUS_TYPE_ARRAY_AS_STRING
2811                                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2812                                          DBUS_TYPE_STRING_AS_STRING
2813                                          DBUS_TYPE_ARRAY_AS_STRING
2814                                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2815                                          DBUS_TYPE_STRING_AS_STRING
2816                                          DBUS_TYPE_VARIANT_AS_STRING
2817                                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2818                                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2819                                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2820                                          &array);
2821 
2822         for (i = 0; i < pa_bluetooth_a2dp_endpoint_conf_count(); i++) {
2823             const pa_a2dp_endpoint_conf *endpoint_conf;
2824             uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
2825             uint8_t capabilities_size;
2826             uint8_t codec_id;
2827             char *endpoint;
2828 
2829             endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2830 
2831             codec_id = endpoint_conf->id.codec_id;
2832 
2833             if (endpoint_conf->can_be_supported(false)) {
2834                 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2835                 pa_assert(capabilities_size != 0);
2836                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2837                 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK, codec_id,
2838                         capabilities, capabilities_size);
2839                 pa_xfree(endpoint);
2840             }
2841 
2842             if (endpoint_conf->can_be_supported(true)) {
2843                 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2844                 pa_assert(capabilities_size != 0);
2845                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2846                 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE, codec_id,
2847                         capabilities, capabilities_size);
2848                 pa_xfree(endpoint);
2849             }
2850         }
2851 
2852         dbus_message_iter_close_container(&iter, &array);
2853     } else
2854         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2855 
2856     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2857     dbus_message_unref(r);
2858 
2859     return DBUS_HANDLER_RESULT_HANDLED;
2860 }
2861 
object_manager_init(pa_bluetooth_discovery * y)2862 static void object_manager_init(pa_bluetooth_discovery *y) {
2863     static const DBusObjectPathVTable vtable = {
2864         .message_function = object_manager_handler,
2865     };
2866 
2867     pa_assert(y);
2868     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection),
2869                 A2DP_OBJECT_MANAGER_PATH, &vtable, y));
2870 }
2871 
object_manager_done(pa_bluetooth_discovery * y)2872 static void object_manager_done(pa_bluetooth_discovery *y) {
2873     pa_assert(y);
2874     dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection),
2875             A2DP_OBJECT_MANAGER_PATH);
2876 }
2877 
pa_bluetooth_discovery_get(pa_core * c,int headset_backend,bool enable_native_hsp_hs,bool enable_native_hfp_hf,bool enable_msbc)2878 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend, bool enable_native_hsp_hs, bool enable_native_hfp_hf, bool enable_msbc) {
2879     pa_bluetooth_discovery *y;
2880     DBusError err;
2881     DBusConnection *conn;
2882     unsigned i, count;
2883     const pa_a2dp_endpoint_conf *endpoint_conf;
2884     char *endpoint;
2885 
2886     pa_bluetooth_a2dp_codec_gst_init();
2887     y = pa_xnew0(pa_bluetooth_discovery, 1);
2888     PA_REFCNT_INIT(y);
2889     y->core = c;
2890     y->headset_backend = headset_backend;
2891     y->enable_native_hsp_hs = enable_native_hsp_hs;
2892     y->enable_native_hfp_hf = enable_native_hfp_hf;
2893     y->enable_msbc = enable_msbc;
2894     y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2895                                       (pa_free_cb_t) adapter_free);
2896     y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2897                                      (pa_free_cb_t) device_free);
2898     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2899     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2900 
2901     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2902         pa_hook_init(&y->hooks[i], y);
2903 
2904     pa_shared_set(c, "bluetooth-discovery", y);
2905 
2906     dbus_error_init(&err);
2907 
2908     if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
2909         pa_log_error("Failed to get D-Bus connection: %s", err.message);
2910         goto fail;
2911     }
2912 
2913     conn = pa_dbus_connection_get(y->connection);
2914 
2915     /* dynamic detection of bluetooth audio devices */
2916     if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2917         pa_log_error("Failed to add filter function");
2918         goto fail;
2919     }
2920     y->filter_added = true;
2921 
2922     if (pa_dbus_add_matches(conn, &err,
2923             "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'"
2924             ",arg0='" BLUEZ_SERVICE "'",
2925             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',member='InterfacesAdded'",
2926             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
2927             "member='InterfacesRemoved'",
2928             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2929             ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
2930             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2931             ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
2932             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2933             ",arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
2934             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2935             ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
2936             NULL) < 0) {
2937         pa_log_error("Failed to add D-Bus matches: %s", err.message);
2938         goto fail;
2939     }
2940     y->matches_added = true;
2941 
2942     object_manager_init(y);
2943 
2944     count = pa_bluetooth_a2dp_endpoint_conf_count();
2945     for (i = 0; i < count; i++) {
2946         endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2947         if (endpoint_conf->can_be_supported(false)) {
2948             endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2949             endpoint_init(y, endpoint);
2950             pa_xfree(endpoint);
2951         }
2952 
2953         if (endpoint_conf->can_be_supported(true)) {
2954             endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2955             endpoint_init(y, endpoint);
2956             pa_xfree(endpoint);
2957         }
2958     }
2959 
2960     get_managed_objects(y);
2961 
2962     return y;
2963 
2964 fail:
2965     pa_bluetooth_discovery_unref(y);
2966     dbus_error_free(&err);
2967 
2968     return NULL;
2969 }
2970 
pa_bluetooth_discovery_ref(pa_bluetooth_discovery * y)2971 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2972     pa_assert(y);
2973     pa_assert(PA_REFCNT_VALUE(y) > 0);
2974 
2975     PA_REFCNT_INC(y);
2976 
2977     return y;
2978 }
2979 
pa_bluetooth_discovery_unref(pa_bluetooth_discovery * y)2980 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2981     unsigned i, count;
2982     const pa_a2dp_endpoint_conf *endpoint_conf;
2983     char *endpoint;
2984 
2985     pa_assert(y);
2986     pa_assert(PA_REFCNT_VALUE(y) > 0);
2987 
2988     if (PA_REFCNT_DEC(y) > 0)
2989         return;
2990 
2991     pa_dbus_free_pending_list(&y->pending);
2992 
2993     if (y->ofono_backend)
2994         pa_bluetooth_ofono_backend_free(y->ofono_backend);
2995     if (y->native_backend)
2996         pa_bluetooth_native_backend_free(y->native_backend);
2997 
2998     if (y->adapters)
2999         pa_hashmap_free(y->adapters);
3000 
3001     if (y->devices)
3002         pa_hashmap_free(y->devices);
3003 
3004     if (y->transports) {
3005         pa_assert(pa_hashmap_isempty(y->transports));
3006         pa_hashmap_free(y->transports);
3007     }
3008 
3009     if (y->connection) {
3010 
3011         if (y->matches_added)
3012             pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
3013                 "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',"
3014                 "arg0='" BLUEZ_SERVICE "'",
3015                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3016                 "member='InterfacesAdded'",
3017                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3018                 "member='InterfacesRemoved'",
3019                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3020                 "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
3021                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3022                 "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
3023                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3024                 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
3025                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3026                 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
3027                 NULL);
3028 
3029         if (y->filter_added)
3030             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
3031 
3032         object_manager_done(y);
3033 
3034         count = pa_bluetooth_a2dp_endpoint_conf_count();
3035         for (i = 0; i < count; i++) {
3036             endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
3037 
3038             if (endpoint_conf->can_be_supported(false)) {
3039                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
3040                 endpoint_done(y, endpoint);
3041                 pa_xfree(endpoint);
3042             }
3043 
3044             if (endpoint_conf->can_be_supported(true)) {
3045                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
3046                 endpoint_done(y, endpoint);
3047                 pa_xfree(endpoint);
3048             }
3049         }
3050 
3051         pa_dbus_connection_unref(y->connection);
3052     }
3053 
3054     pa_shared_remove(y->core, "bluetooth-discovery");
3055     pa_xfree(y);
3056 }
3057