• 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_device_by_address(pa_bluetooth_discovery * y,const char * remote,const char * local)1205 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
1206     pa_bluetooth_device *d;
1207     void *state = NULL;
1208 
1209     pa_assert(y);
1210     pa_assert(PA_REFCNT_VALUE(y) > 0);
1211     pa_assert(remote);
1212     pa_assert(local);
1213 
1214     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
1215         if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
1216             return d;
1217 
1218     return NULL;
1219 }
1220 
device_free(pa_bluetooth_device * d)1221 static void device_free(pa_bluetooth_device *d) {
1222     unsigned i;
1223 
1224     pa_assert(d);
1225 
1226     device_stop_waiting_for_profiles(d);
1227 
1228     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UNLINK], d);
1229 
1230     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
1231         pa_bluetooth_transport *t;
1232 
1233         if (!(t = d->transports[i]))
1234             continue;
1235 
1236         pa_bluetooth_transport_free(t);
1237     }
1238 
1239     if (d->uuids)
1240         pa_hashmap_free(d->uuids);
1241     if (d->a2dp_sink_endpoints)
1242         pa_hashmap_free(d->a2dp_sink_endpoints);
1243     if (d->a2dp_source_endpoints)
1244         pa_hashmap_free(d->a2dp_source_endpoints);
1245 
1246     pa_xfree(d->path);
1247     pa_xfree(d->alias);
1248     pa_xfree(d->address);
1249     pa_xfree(d->adapter_path);
1250     pa_xfree(d);
1251 }
1252 
device_remove(pa_bluetooth_discovery * y,const char * path)1253 static void device_remove(pa_bluetooth_discovery *y, const char *path) {
1254     pa_bluetooth_device *d;
1255 
1256     if (!(d = pa_hashmap_remove(y->devices, path)))
1257         pa_log_warn("Unknown device removed %s", path);
1258     else {
1259         pa_log_debug("Device %s removed", path);
1260         device_free(d);
1261     }
1262 }
1263 
device_set_valid(pa_bluetooth_device * device,bool valid)1264 static void device_set_valid(pa_bluetooth_device *device, bool valid) {
1265     bool old_any_connected;
1266 
1267     pa_assert(device);
1268 
1269     if (valid == device->valid)
1270         return;
1271 
1272     old_any_connected = pa_bluetooth_device_any_transport_connected(device);
1273     device->valid = valid;
1274 
1275     if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
1276         pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
1277 }
1278 
device_update_valid(pa_bluetooth_device * d)1279 static void device_update_valid(pa_bluetooth_device *d) {
1280     pa_assert(d);
1281 
1282     if (!d->properties_received) {
1283         pa_assert(!d->valid);
1284         return;
1285     }
1286 
1287     /* Check if mandatory properties are set. */
1288     if (!d->address || !d->adapter_path || !d->alias) {
1289         device_set_valid(d, false);
1290         return;
1291     }
1292 
1293     if (!d->adapter || !d->adapter->valid) {
1294         device_set_valid(d, false);
1295         return;
1296     }
1297 
1298     device_set_valid(d, true);
1299 }
1300 
device_set_adapter(pa_bluetooth_device * device,pa_bluetooth_adapter * adapter)1301 static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
1302     pa_assert(device);
1303 
1304     if (adapter == device->adapter)
1305         return;
1306 
1307     device->adapter = adapter;
1308 
1309     device_update_valid(device);
1310 }
1311 
append_battery_provider_properties(pa_bluetooth_device * d,DBusMessageIter * entry,bool only_percentage)1312 static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *entry, bool only_percentage) {
1313     static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
1314     DBusMessageIter dict;
1315 
1316     pa_assert_se(dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &interface_name));
1317 
1318     pa_assert_se(dbus_message_iter_open_container(entry, DBUS_TYPE_ARRAY,
1319                                                  DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1320                                                  DBUS_TYPE_STRING_AS_STRING
1321                                                  DBUS_TYPE_VARIANT_AS_STRING
1322                                                  DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1323                                                  &dict));
1324 
1325     pa_dbus_append_basic_variant_dict_entry(&dict, "Percentage", DBUS_TYPE_BYTE, &d->battery_level);
1326 
1327     if (!only_percentage) {
1328         pa_assert(d->battery_source);
1329         pa_dbus_append_basic_variant_dict_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &d->path);
1330         pa_dbus_append_basic_variant_dict_entry(&dict, "Source", DBUS_TYPE_STRING, &d->battery_source);
1331     }
1332 
1333     pa_assert_se(dbus_message_iter_close_container(entry, &dict));
1334 }
1335 
append_battery_provider(pa_bluetooth_device * d,DBusMessageIter * object)1336 static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object) {
1337     char *battery_path = device_battery_provider_path(d);
1338     DBusMessageIter array, entry;
1339 
1340     pa_assert_se(dbus_message_iter_append_basic(object, DBUS_TYPE_OBJECT_PATH, &battery_path));
1341 
1342     pa_assert_se(dbus_message_iter_open_container(object, DBUS_TYPE_ARRAY,
1343                                                   DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1344                                                   DBUS_TYPE_STRING_AS_STRING
1345                                                   DBUS_TYPE_ARRAY_AS_STRING
1346                                                   DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1347                                                   DBUS_TYPE_STRING_AS_STRING
1348                                                   DBUS_TYPE_VARIANT_AS_STRING
1349                                                   DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1350                                                   DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1351                                                   &array));
1352 
1353     pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry));
1354     append_battery_provider_properties(d, &entry, false);
1355     pa_assert_se(dbus_message_iter_close_container(&array, &entry));
1356     pa_assert_se(dbus_message_iter_close_container(object, &array));
1357 
1358     pa_xfree(battery_path);
1359 }
1360 
battery_provider_handler(DBusConnection * c,DBusMessage * m,void * userdata)1361 static DBusHandlerResult battery_provider_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1362     pa_bluetooth_adapter *a = userdata;
1363     DBusMessage *r = NULL;
1364     const char *path, *interface, *member;
1365 
1366     pa_assert(a);
1367 
1368     path = dbus_message_get_path(m);
1369     interface = dbus_message_get_interface(m);
1370     member = dbus_message_get_member(m);
1371 
1372     pa_log_debug("%s %s %s", path, interface, member);
1373 
1374     if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
1375         DBusMessageIter iter, array, object;
1376         pa_bluetooth_device *d;
1377         void *state;
1378 
1379         pa_assert_se(r = dbus_message_new_method_return(m));
1380 
1381         dbus_message_iter_init_append(r, &iter);
1382         pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
1383                                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1384                                                       DBUS_TYPE_OBJECT_PATH_AS_STRING
1385                                                       DBUS_TYPE_ARRAY_AS_STRING
1386                                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1387                                                       DBUS_TYPE_STRING_AS_STRING
1388                                                       DBUS_TYPE_ARRAY_AS_STRING
1389                                                       DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1390                                                       DBUS_TYPE_STRING_AS_STRING
1391                                                       DBUS_TYPE_VARIANT_AS_STRING
1392                                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1393                                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1394                                                       DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1395                                                       &array));
1396 
1397         PA_HASHMAP_FOREACH(d, a->discovery->devices, state) {
1398 
1399             if (d->has_battery_level) {
1400                 pa_log_debug("%s: battery level  = %d", d->path, d->battery_level);
1401                 pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &object));
1402                 append_battery_provider(d, &object);
1403                 pa_assert_se(dbus_message_iter_close_container(&array, &object));
1404             }
1405         }
1406 
1407         pa_assert_se(dbus_message_iter_close_container(&iter, &array));
1408     } else
1409         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1410 
1411     pa_assert_se(dbus_connection_send(c, r, NULL));
1412     dbus_message_unref(r);
1413 
1414     return DBUS_HANDLER_RESULT_HANDLED;
1415 }
1416 
adapter_register_battery_provider(pa_bluetooth_adapter * a)1417 static void adapter_register_battery_provider(pa_bluetooth_adapter *a) {
1418     DBusMessage *m, *r;
1419     DBusError error;
1420 
1421     static const DBusObjectPathVTable vtable_profile = {
1422         .message_function = battery_provider_handler,
1423     };
1424 
1425     char *provider_path = adapter_battery_provider_path(a);
1426 
1427     pa_log_debug("Registering battery provider for %s at %s", a->path, provider_path);
1428 
1429     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path, &vtable_profile, a));
1430 
1431     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "RegisterBatteryProvider"));
1432     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1433 
1434     dbus_error_init(&error);
1435     if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1436         if (dbus_error_has_name(&error, DBUS_ERROR_UNKNOWN_METHOD))
1437             pa_log_notice("Could not find " BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE
1438                           ".RegisterBatteryProvider(), is bluetoothd started with experimental features enabled (-E flag)?");
1439         else
1440             pa_log_warn(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".RegisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1441         dbus_error_free(&error);
1442         dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1443     } else {
1444         dbus_message_unref(r);
1445         a->battery_provider_registered = true;
1446     }
1447 
1448     dbus_message_unref(m);
1449     pa_xfree(provider_path);
1450 }
1451 
adapter_deregister_battery_provider(pa_bluetooth_adapter * a)1452 static void adapter_deregister_battery_provider(pa_bluetooth_adapter *a) {
1453     DBusMessage *m, *r;
1454     DBusError error;
1455     char *provider_path;
1456 
1457     if (!a->battery_provider_registered) {
1458         pa_log_debug("No battery provider registered for %s", a->path);
1459         return;
1460     }
1461 
1462     provider_path = adapter_battery_provider_path(a);
1463 
1464     pa_log_debug("Deregistering battery provider at %s", provider_path);
1465 
1466     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "UnregisterBatteryProvider"));
1467     pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1468 
1469     dbus_error_init(&error);
1470     if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1471         pa_log_error(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".UnregisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1472         dbus_error_free(&error);
1473     } else {
1474         dbus_message_unref(r);
1475         a->battery_provider_registered = false;
1476     }
1477 
1478     dbus_message_unref(m);
1479 
1480     dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1481 
1482     pa_xfree(provider_path);
1483 }
1484 
adapter_create(pa_bluetooth_discovery * y,const char * path)1485 static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
1486     pa_bluetooth_adapter *a;
1487 
1488     pa_assert(y);
1489     pa_assert(path);
1490 
1491     a = pa_xnew0(pa_bluetooth_adapter, 1);
1492     a->discovery = y;
1493     a->path = pa_xstrdup(path);
1494     a->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1495 
1496     pa_hashmap_put(y->adapters, a->path, a);
1497 
1498     return a;
1499 }
1500 
adapter_free(pa_bluetooth_adapter * a)1501 static void adapter_free(pa_bluetooth_adapter *a) {
1502     pa_bluetooth_device *d;
1503     void *state;
1504 
1505     pa_assert(a);
1506     pa_assert(a->discovery);
1507 
1508     adapter_deregister_battery_provider(a);
1509 
1510     PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
1511         if (d->adapter == a)
1512             device_set_adapter(d, NULL);
1513 
1514     pa_hashmap_free(a->uuids);
1515     pa_xfree(a->path);
1516     pa_xfree(a->address);
1517     pa_xfree(a);
1518 }
1519 
adapter_remove(pa_bluetooth_discovery * y,const char * path)1520 static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
1521     pa_bluetooth_adapter *a;
1522 
1523     if (!(a = pa_hashmap_remove(y->adapters, path)))
1524         pa_log_warn("Unknown adapter removed %s", path);
1525     else {
1526         pa_log_debug("Adapter %s removed", path);
1527         adapter_free(a);
1528     }
1529 }
1530 
parse_device_property(pa_bluetooth_device * d,DBusMessageIter * i)1531 static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
1532     const char *key;
1533     DBusMessageIter variant_i;
1534 
1535     pa_assert(d);
1536 
1537     key = check_variant_property(i);
1538     if (key == NULL) {
1539         pa_log_error("Received invalid property for device %s", d->path);
1540         return;
1541     }
1542 
1543     dbus_message_iter_recurse(i, &variant_i);
1544 
1545     switch (dbus_message_iter_get_arg_type(&variant_i)) {
1546 
1547         case DBUS_TYPE_STRING: {
1548             const char *value;
1549             dbus_message_iter_get_basic(&variant_i, &value);
1550 
1551             if (pa_streq(key, "Alias")) {
1552                 pa_xfree(d->alias);
1553                 d->alias = pa_xstrdup(value);
1554                 pa_log_debug("%s: %s", key, value);
1555             } else if (pa_streq(key, "Address")) {
1556                 if (d->properties_received) {
1557                     pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
1558                     return;
1559                 }
1560 
1561                 if (d->address) {
1562                     pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
1563                     return;
1564                 }
1565 
1566                 d->address = pa_xstrdup(value);
1567                 pa_log_debug("%s: %s", key, value);
1568             }
1569 
1570             break;
1571         }
1572 
1573         case DBUS_TYPE_OBJECT_PATH: {
1574             const char *value;
1575             dbus_message_iter_get_basic(&variant_i, &value);
1576 
1577             if (pa_streq(key, "Adapter")) {
1578 
1579                 if (d->properties_received) {
1580                     pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
1581                     return;
1582                 }
1583 
1584                 if (d->adapter_path) {
1585                     pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
1586                     return;
1587                 }
1588 
1589                 d->adapter_path = pa_xstrdup(value);
1590                 pa_log_debug("%s: %s", key, value);
1591             }
1592 
1593             break;
1594         }
1595 
1596         case DBUS_TYPE_UINT32: {
1597             uint32_t value;
1598             dbus_message_iter_get_basic(&variant_i, &value);
1599 
1600             if (pa_streq(key, "Class")) {
1601                 d->class_of_device = value;
1602                 pa_log_debug("%s: %d", key, value);
1603             }
1604 
1605             break;
1606         }
1607 
1608         case DBUS_TYPE_ARRAY: {
1609             DBusMessageIter ai;
1610             dbus_message_iter_recurse(&variant_i, &ai);
1611 
1612             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1613                 /* bluetoothd never removes UUIDs from a device object so we
1614                  * don't need to check for disappeared UUIDs here. */
1615                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1616                     const char *value;
1617                     char *uuid;
1618 
1619                     dbus_message_iter_get_basic(&ai, &value);
1620 
1621                     if (pa_hashmap_get(d->uuids, value)) {
1622                         dbus_message_iter_next(&ai);
1623                         continue;
1624                     }
1625 
1626                     uuid = pa_xstrdup(value);
1627                     pa_hashmap_put(d->uuids, uuid, uuid);
1628 
1629                     pa_log_debug("%s: %s", key, value);
1630                     dbus_message_iter_next(&ai);
1631                 }
1632             }
1633 
1634             break;
1635         }
1636     }
1637 }
1638 
parse_device_properties(pa_bluetooth_device * d,DBusMessageIter * i)1639 static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
1640     DBusMessageIter element_i;
1641 
1642     dbus_message_iter_recurse(i, &element_i);
1643 
1644     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1645         DBusMessageIter dict_i;
1646 
1647         dbus_message_iter_recurse(&element_i, &dict_i);
1648         parse_device_property(d, &dict_i);
1649         dbus_message_iter_next(&element_i);
1650     }
1651 
1652     if (!d->properties_received) {
1653         d->properties_received = true;
1654         device_update_valid(d);
1655 
1656         if (!d->address || !d->adapter_path || !d->alias)
1657             pa_log_error("Non-optional information missing for device %s", d->path);
1658     }
1659 }
1660 
parse_adapter_properties(pa_bluetooth_adapter * a,DBusMessageIter * i,bool is_property_change)1661 static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
1662     DBusMessageIter element_i;
1663 
1664     pa_assert(a);
1665 
1666     dbus_message_iter_recurse(i, &element_i);
1667 
1668     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1669         DBusMessageIter dict_i, variant_i;
1670         const char *key;
1671 
1672         dbus_message_iter_recurse(&element_i, &dict_i);
1673 
1674         key = check_variant_property(&dict_i);
1675         if (key == NULL) {
1676             pa_log_error("Received invalid property for adapter %s", a->path);
1677             return;
1678         }
1679 
1680         dbus_message_iter_recurse(&dict_i, &variant_i);
1681 
1682         if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
1683             const char *value;
1684 
1685             if (is_property_change) {
1686                 pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
1687                 return;
1688             }
1689 
1690             if (a->address) {
1691                 pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
1692                 return;
1693             }
1694 
1695             dbus_message_iter_get_basic(&variant_i, &value);
1696             a->address = pa_xstrdup(value);
1697             a->valid = true;
1698         } else if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_ARRAY) {
1699             DBusMessageIter ai;
1700             dbus_message_iter_recurse(&variant_i, &ai);
1701 
1702             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1703                 pa_hashmap_remove_all(a->uuids);
1704                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1705                     const char *value;
1706                     char *uuid;
1707 
1708                     dbus_message_iter_get_basic(&ai, &value);
1709 
1710                     if (pa_hashmap_get(a->uuids, value)) {
1711                         dbus_message_iter_next(&ai);
1712                         continue;
1713                     }
1714 
1715                     uuid = pa_xstrdup(value);
1716                     pa_hashmap_put(a->uuids, uuid, uuid);
1717 
1718                     pa_log_debug("%s: %s", key, value);
1719                     dbus_message_iter_next(&ai);
1720                 }
1721                 pa_hook_fire(pa_bluetooth_discovery_hook(a->discovery, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), a);
1722             }
1723         }
1724 
1725         dbus_message_iter_next(&element_i);
1726     }
1727 }
1728 
register_legacy_sbc_endpoint_reply(DBusPendingCall * pending,void * userdata)1729 static void register_legacy_sbc_endpoint_reply(DBusPendingCall *pending, void *userdata) {
1730     DBusMessage *r;
1731     pa_dbus_pending *p;
1732     pa_bluetooth_discovery *y;
1733     char *endpoint;
1734 
1735     pa_assert(pending);
1736     pa_assert_se(p = userdata);
1737     pa_assert_se(y = p->context_data);
1738     pa_assert_se(endpoint = p->call_data);
1739     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1740 
1741     if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1742         pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
1743         goto finish;
1744     }
1745 
1746     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1747         pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
1748                      pa_dbus_get_error_message(r));
1749         goto finish;
1750     }
1751 
1752 finish:
1753     dbus_message_unref(r);
1754 
1755     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1756     pa_dbus_pending_free(p);
1757 
1758     pa_xfree(endpoint);
1759 }
1760 
register_legacy_sbc_endpoint(pa_bluetooth_discovery * y,const pa_a2dp_endpoint_conf * endpoint_conf,const char * path,const char * endpoint,const char * uuid)1761 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) {
1762     DBusMessage *m;
1763     DBusMessageIter i, d;
1764     uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
1765     size_t capabilities_size;
1766     uint8_t codec_id;
1767 
1768     pa_log_debug("Registering %s on adapter %s", endpoint, path);
1769 
1770     codec_id = endpoint_conf->id.codec_id;
1771     capabilities_size = endpoint_conf->fill_capabilities(capabilities);
1772     pa_assert(capabilities_size != 0);
1773 
1774     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
1775 
1776     dbus_message_iter_init_append(m, &i);
1777     pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
1778     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1779                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1780                                      DBUS_TYPE_STRING_AS_STRING
1781                                      DBUS_TYPE_VARIANT_AS_STRING
1782                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1783                                      &d);
1784     pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
1785     pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
1786     pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
1787 
1788     dbus_message_iter_close_container(&i, &d);
1789 
1790     send_and_add_to_pending(y, m, register_legacy_sbc_endpoint_reply, pa_xstrdup(endpoint));
1791 }
1792 
register_application_reply(DBusPendingCall * pending,void * userdata)1793 static void register_application_reply(DBusPendingCall *pending, void *userdata) {
1794     DBusMessage *r;
1795     pa_dbus_pending *p;
1796     pa_bluetooth_adapter *a;
1797     pa_bluetooth_discovery *y;
1798     char *path;
1799     bool fallback = true;
1800 
1801     pa_assert(pending);
1802     pa_assert_se(p = userdata);
1803     pa_assert_se(y = p->context_data);
1804     pa_assert_se(path = p->call_data);
1805     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1806 
1807     if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1808         pa_log_info("Couldn't register media application for adapter %s because it is disabled in BlueZ", path);
1809         goto finish;
1810     }
1811 
1812     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1813         pa_log_warn(BLUEZ_MEDIA_INTERFACE ".RegisterApplication() failed: %s: %s",
1814                 dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1815         pa_log_warn("Couldn't register media application for adapter %s", path);
1816         goto finish;
1817     }
1818 
1819     a = pa_hashmap_get(y->adapters, path);
1820     if (!a) {
1821         pa_log_error("Couldn't register media application for adapter %s because it does not exist anymore", path);
1822         goto finish;
1823     }
1824 
1825     fallback = false;
1826     a->application_registered = true;
1827     pa_log_debug("Media application for adapter %s was successfully registered", path);
1828 
1829 finish:
1830     dbus_message_unref(r);
1831 
1832     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1833     pa_dbus_pending_free(p);
1834 
1835     if (fallback) {
1836         /* If bluez does not support RegisterApplication, fallback to old legacy API with just one SBC codec */
1837         const pa_a2dp_endpoint_conf *endpoint_conf;
1838         endpoint_conf = pa_bluetooth_get_a2dp_endpoint_conf("sbc");
1839         pa_assert(endpoint_conf);
1840         register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SINK_ENDPOINT "/sbc",
1841                 PA_BLUETOOTH_UUID_A2DP_SINK);
1842         register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SOURCE_ENDPOINT "/sbc",
1843                 PA_BLUETOOTH_UUID_A2DP_SOURCE);
1844         pa_log_warn("Only SBC codec is available for A2DP profiles");
1845     }
1846 
1847     pa_xfree(path);
1848 }
1849 
register_application(pa_bluetooth_adapter * a)1850 static void register_application(pa_bluetooth_adapter *a) {
1851     DBusMessage *m;
1852     DBusMessageIter i, d;
1853     const char *object_manager_path = A2DP_OBJECT_MANAGER_PATH;
1854 
1855     if (a->application_registered) {
1856         pa_log_info("Media application is already registered for adapter %s", a->path);
1857         return;
1858     }
1859 
1860     pa_log_debug("Registering media application for adapter %s", a->path);
1861 
1862     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path,
1863                 BLUEZ_MEDIA_INTERFACE, "RegisterApplication"));
1864 
1865     dbus_message_iter_init_append(m, &i);
1866     pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object_manager_path));
1867     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1868                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1869                                      DBUS_TYPE_STRING_AS_STRING
1870                                      DBUS_TYPE_VARIANT_AS_STRING
1871                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1872                                      &d);
1873     dbus_message_iter_close_container(&i, &d);
1874 
1875     send_and_add_to_pending(a->discovery, m, register_application_reply, pa_xstrdup(a->path));
1876 }
1877 
parse_remote_endpoint_properties(pa_bluetooth_discovery * y,const char * endpoint,DBusMessageIter * i)1878 static void parse_remote_endpoint_properties(pa_bluetooth_discovery *y, const char *endpoint, DBusMessageIter *i) {
1879     DBusMessageIter element_i;
1880     pa_bluetooth_device *device;
1881     pa_hashmap *codec_endpoints;
1882     pa_hashmap *endpoints;
1883     pa_a2dp_codec_id *a2dp_codec_id;
1884     pa_a2dp_codec_capabilities *a2dp_codec_capabilities;
1885     const char *uuid = NULL;
1886     const char *device_path = NULL;
1887     uint8_t codec_id = 0;
1888     bool have_codec_id = false;
1889     const uint8_t *capabilities = NULL;
1890     int capabilities_size = 0;
1891 
1892     pa_log_debug("Parsing remote endpoint %s", endpoint);
1893 
1894     dbus_message_iter_recurse(i, &element_i);
1895 
1896     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1897         DBusMessageIter dict_i, variant_i;
1898         const char *key;
1899 
1900         dbus_message_iter_recurse(&element_i, &dict_i);
1901 
1902         key = check_variant_property(&dict_i);
1903         if (key == NULL) {
1904             pa_log_error("Received invalid property for remote endpoint %s", endpoint);
1905             return;
1906         }
1907 
1908         dbus_message_iter_recurse(&dict_i, &variant_i);
1909 
1910         if (pa_streq(key, "UUID")) {
1911             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_STRING) {
1912                 pa_log_warn("Remote endpoint %s property 'UUID' is not string, ignoring", endpoint);
1913                 return;
1914             }
1915 
1916             dbus_message_iter_get_basic(&variant_i, &uuid);
1917         } else if (pa_streq(key, "Codec")) {
1918             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BYTE) {
1919                 pa_log_warn("Remote endpoint %s property 'Codec' is not byte, ignoring", endpoint);
1920                 return;
1921             }
1922 
1923             dbus_message_iter_get_basic(&variant_i, &codec_id);
1924             have_codec_id = true;
1925         } else if (pa_streq(key, "Capabilities")) {
1926             DBusMessageIter array;
1927 
1928             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_ARRAY) {
1929                 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array, ignoring", endpoint);
1930                 return;
1931             }
1932 
1933             dbus_message_iter_recurse(&variant_i, &array);
1934             if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) {
1935                 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array of bytes, ignoring", endpoint);
1936                 return;
1937             }
1938 
1939             dbus_message_iter_get_fixed_array(&array, &capabilities, &capabilities_size);
1940         } else if (pa_streq(key, "Device")) {
1941             if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_OBJECT_PATH) {
1942                 pa_log_warn("Remote endpoint %s property 'Device' is not path, ignoring", endpoint);
1943                 return;
1944             }
1945 
1946             dbus_message_iter_get_basic(&variant_i, &device_path);
1947         }
1948 
1949         dbus_message_iter_next(&element_i);
1950     }
1951 
1952     if (!uuid) {
1953         pa_log_warn("Remote endpoint %s does not have property 'UUID', ignoring", endpoint);
1954         return;
1955     }
1956 
1957     if (!have_codec_id) {
1958         pa_log_warn("Remote endpoint %s does not have property 'Codec', ignoring", endpoint);
1959         return;
1960     }
1961 
1962     if (!capabilities || !capabilities_size) {
1963         pa_log_warn("Remote endpoint %s does not have property 'Capabilities', ignoring", endpoint);
1964         return;
1965     }
1966 
1967     if (!device_path) {
1968         pa_log_warn("Remote endpoint %s does not have property 'Device', ignoring", endpoint);
1969         return;
1970     }
1971 
1972     device = pa_hashmap_get(y->devices, device_path);
1973     if (!device) {
1974         pa_log_warn("Device for remote endpoint %s was not found", endpoint);
1975         return;
1976     }
1977 
1978     if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
1979         codec_endpoints = device->a2dp_sink_endpoints;
1980     } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) {
1981         codec_endpoints = device->a2dp_source_endpoints;
1982     } else {
1983         pa_log_warn("Remote endpoint %s does not have valid property 'UUID', ignoring", endpoint);
1984         return;
1985     }
1986 
1987     if (capabilities_size < 0 || capabilities_size > MAX_A2DP_CAPS_SIZE) {
1988         pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
1989         return;
1990     }
1991 
1992     a2dp_codec_id = pa_xmalloc0(sizeof(*a2dp_codec_id));
1993     a2dp_codec_id->codec_id = codec_id;
1994     if (codec_id == A2DP_CODEC_VENDOR) {
1995         if ((size_t)capabilities_size < sizeof(a2dp_vendor_codec_t)) {
1996             pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
1997             pa_xfree(a2dp_codec_id);
1998             return;
1999         }
2000         a2dp_codec_id->vendor_id = A2DP_GET_VENDOR_ID(*(a2dp_vendor_codec_t *)capabilities);
2001         a2dp_codec_id->vendor_codec_id = A2DP_GET_CODEC_ID(*(a2dp_vendor_codec_t *)capabilities);
2002     } else {
2003         a2dp_codec_id->vendor_id = 0;
2004         a2dp_codec_id->vendor_codec_id = 0;
2005     }
2006 
2007     if (!pa_bluetooth_a2dp_codec_is_available(a2dp_codec_id, pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))) {
2008         pa_xfree(a2dp_codec_id);
2009         return;
2010     }
2011 
2012     a2dp_codec_capabilities = pa_xmalloc0(sizeof(*a2dp_codec_capabilities) + capabilities_size);
2013     a2dp_codec_capabilities->size = capabilities_size;
2014     memcpy(a2dp_codec_capabilities->buffer, capabilities, capabilities_size);
2015 
2016     endpoints = pa_hashmap_get(codec_endpoints, a2dp_codec_id);
2017     if (!endpoints) {
2018         endpoints = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, pa_xfree);
2019         pa_hashmap_put(codec_endpoints, a2dp_codec_id, endpoints);
2020     }
2021 
2022     if (pa_hashmap_remove_and_free(endpoints, endpoint) >= 0)
2023         pa_log_debug("Replacing existing remote endpoint %s", endpoint);
2024     pa_hashmap_put(endpoints, pa_xstrdup(endpoint), a2dp_codec_capabilities);
2025 }
2026 
parse_interfaces_and_properties(pa_bluetooth_discovery * y,DBusMessageIter * dict_i)2027 static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
2028     DBusMessageIter element_i;
2029     const char *path;
2030     void *state;
2031     pa_bluetooth_device *d;
2032 
2033     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
2034     dbus_message_iter_get_basic(dict_i, &path);
2035 
2036     pa_assert_se(dbus_message_iter_next(dict_i));
2037     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
2038 
2039     dbus_message_iter_recurse(dict_i, &element_i);
2040 
2041     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2042         DBusMessageIter iface_i;
2043         const char *interface;
2044 
2045         dbus_message_iter_recurse(&element_i, &iface_i);
2046 
2047         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
2048         dbus_message_iter_get_basic(&iface_i, &interface);
2049 
2050         pa_assert_se(dbus_message_iter_next(&iface_i));
2051         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
2052 
2053         if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
2054             pa_bluetooth_adapter *a;
2055 
2056             if ((a = pa_hashmap_get(y->adapters, path))) {
2057                 pa_log_error("Found duplicated D-Bus path for adapter %s", path);
2058                 return;
2059             } else
2060                 a = adapter_create(y, path);
2061 
2062             pa_log_debug("Adapter %s found", path);
2063 
2064             parse_adapter_properties(a, &iface_i, false);
2065 
2066             if (!a->valid)
2067                 return;
2068 
2069             register_application(a);
2070             adapter_register_battery_provider(a);
2071         } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
2072 
2073             if ((d = pa_hashmap_get(y->devices, path))) {
2074                 if (d->properties_received) {
2075                     pa_log_error("Found duplicated D-Bus path for device %s", path);
2076                     return;
2077                 }
2078             } else
2079                 d = device_create(y, path);
2080 
2081             pa_log_debug("Device %s found", d->path);
2082 
2083             parse_device_properties(d, &iface_i);
2084         } else if (pa_streq(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2085             parse_remote_endpoint_properties(y, path, &iface_i);
2086         } else
2087             pa_log_debug("Unknown interface %s found, skipping", interface);
2088 
2089         dbus_message_iter_next(&element_i);
2090     }
2091 
2092     PA_HASHMAP_FOREACH(d, y->devices, state) {
2093         if (d->properties_received && !d->tried_to_link_with_adapter) {
2094             if (d->adapter_path) {
2095                 device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
2096 
2097                 if (!d->adapter)
2098                     pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
2099                 else if (!d->adapter->valid)
2100                     pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
2101             }
2102 
2103             d->tried_to_link_with_adapter = true;
2104         }
2105     }
2106 
2107     return;
2108 }
2109 
pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery * y,bool is_running)2110 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
2111     pa_assert(y);
2112 
2113     pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
2114     if (y->headset_backend != HEADSET_BACKEND_AUTO)
2115         return;
2116 
2117     pa_bluetooth_native_backend_enable_shared_profiles(y->native_backend, !is_running);
2118 
2119     /* If ofono starts running, all devices that might be connected to the HS roles or HFP AG role
2120      * need to be disconnected, so that the devices can be handled by ofono */
2121     if (is_running) {
2122         void *state;
2123         pa_bluetooth_device *d;
2124 
2125         PA_HASHMAP_FOREACH(d, y->devices, state) {
2126             if (pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_AG) || pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_HF)) {
2127                 DBusMessage *m;
2128 
2129                 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, BLUEZ_DEVICE_INTERFACE, "Disconnect"));
2130                 dbus_message_set_no_reply(m, true);
2131                 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
2132                 dbus_message_unref(m);
2133             }
2134         }
2135     }
2136 }
2137 
get_managed_objects_reply(DBusPendingCall * pending,void * userdata)2138 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
2139     pa_dbus_pending *p;
2140     pa_bluetooth_discovery *y;
2141     DBusMessage *r;
2142     DBusMessageIter arg_i, element_i;
2143 
2144     pa_assert_se(p = userdata);
2145     pa_assert_se(y = p->context_data);
2146     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
2147 
2148     if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
2149         pa_log_warn("BlueZ D-Bus ObjectManager not available");
2150         goto finish;
2151     }
2152 
2153     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
2154         pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
2155         goto finish;
2156     }
2157 
2158     if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
2159         pa_log_error("Invalid reply signature for GetManagedObjects()");
2160         goto finish;
2161     }
2162 
2163     dbus_message_iter_recurse(&arg_i, &element_i);
2164     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2165         DBusMessageIter dict_i;
2166 
2167         dbus_message_iter_recurse(&element_i, &dict_i);
2168 
2169         parse_interfaces_and_properties(y, &dict_i);
2170 
2171         dbus_message_iter_next(&element_i);
2172     }
2173 
2174     y->objects_listed = true;
2175 
2176     if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
2177         y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
2178     if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
2179         y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
2180 
2181 finish:
2182     dbus_message_unref(r);
2183 
2184     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
2185     pa_dbus_pending_free(p);
2186 }
2187 
get_managed_objects(pa_bluetooth_discovery * y)2188 static void get_managed_objects(pa_bluetooth_discovery *y) {
2189     DBusMessage *m;
2190 
2191     pa_assert(y);
2192 
2193     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", DBUS_INTERFACE_OBJECT_MANAGER,
2194                                                   "GetManagedObjects"));
2195     send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
2196 }
2197 
pa_bluetooth_discovery_hook(pa_bluetooth_discovery * y,pa_bluetooth_hook_t hook)2198 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
2199     pa_assert(y);
2200     pa_assert(PA_REFCNT_VALUE(y) > 0);
2201 
2202     return &y->hooks[hook];
2203 }
2204 
filter_cb(DBusConnection * bus,DBusMessage * m,void * userdata)2205 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
2206     pa_bluetooth_discovery *y;
2207     DBusError err;
2208 
2209     pa_assert(bus);
2210     pa_assert(m);
2211     pa_assert_se(y = userdata);
2212 
2213     dbus_error_init(&err);
2214 
2215     if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
2216         const char *name, *old_owner, *new_owner;
2217 
2218         if (!dbus_message_get_args(m, &err,
2219                                    DBUS_TYPE_STRING, &name,
2220                                    DBUS_TYPE_STRING, &old_owner,
2221                                    DBUS_TYPE_STRING, &new_owner,
2222                                    DBUS_TYPE_INVALID)) {
2223             pa_log_error("Failed to parse " DBUS_INTERFACE_DBUS ".NameOwnerChanged: %s", err.message);
2224             goto fail;
2225         }
2226 
2227         if (pa_streq(name, BLUEZ_SERVICE)) {
2228             if (old_owner && *old_owner) {
2229                 pa_log_debug("Bluetooth daemon disappeared");
2230                 pa_hashmap_remove_all(y->devices);
2231                 pa_hashmap_remove_all(y->adapters);
2232                 y->objects_listed = false;
2233                 if (y->ofono_backend) {
2234                     pa_bluetooth_ofono_backend_free(y->ofono_backend);
2235                     y->ofono_backend = NULL;
2236                 }
2237                 if (y->native_backend) {
2238                     pa_bluetooth_native_backend_free(y->native_backend);
2239                     y->native_backend = NULL;
2240                 }
2241             }
2242 
2243             if (new_owner && *new_owner) {
2244                 pa_log_debug("Bluetooth daemon appeared");
2245                 get_managed_objects(y);
2246             }
2247         }
2248 
2249         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2250     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded")) {
2251         DBusMessageIter arg_i;
2252 
2253         if (!y->objects_listed)
2254             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2255 
2256         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
2257             pa_log_error("Invalid signature found in InterfacesAdded");
2258             goto fail;
2259         }
2260 
2261         parse_interfaces_and_properties(y, &arg_i);
2262 
2263         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2264     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved")) {
2265         const char *p;
2266         DBusMessageIter arg_i;
2267         DBusMessageIter element_i;
2268 
2269         if (!y->objects_listed)
2270             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2271 
2272         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
2273             pa_log_error("Invalid signature found in InterfacesRemoved");
2274             goto fail;
2275         }
2276 
2277         dbus_message_iter_get_basic(&arg_i, &p);
2278 
2279         pa_assert_se(dbus_message_iter_next(&arg_i));
2280         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2281 
2282         dbus_message_iter_recurse(&arg_i, &element_i);
2283 
2284         while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
2285             const char *iface;
2286 
2287             dbus_message_iter_get_basic(&element_i, &iface);
2288 
2289             if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
2290                 device_remove(y, p);
2291             else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
2292                 adapter_remove(y, p);
2293             else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
2294                 remote_endpoint_remove(y, p);
2295 
2296             dbus_message_iter_next(&element_i);
2297         }
2298 
2299         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2300 
2301     } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
2302         DBusMessageIter arg_i;
2303         const char *iface;
2304 
2305         if (!y->objects_listed)
2306             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2307 
2308         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
2309             pa_log_error("Invalid signature found in PropertiesChanged");
2310             goto fail;
2311         }
2312 
2313         dbus_message_iter_get_basic(&arg_i, &iface);
2314 
2315         pa_assert_se(dbus_message_iter_next(&arg_i));
2316         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2317 
2318         if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
2319             pa_bluetooth_adapter *a;
2320 
2321             pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
2322 
2323             if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
2324                 pa_log_warn("Properties changed in unknown adapter");
2325                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2326             }
2327 
2328             parse_adapter_properties(a, &arg_i, true);
2329 
2330         } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
2331             pa_bluetooth_device *d;
2332 
2333             pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
2334 
2335             if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
2336                 pa_log_warn("Properties changed in unknown device");
2337                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2338             }
2339 
2340             if (!d->properties_received)
2341                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2342 
2343             parse_device_properties(d, &arg_i);
2344         } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
2345             pa_bluetooth_transport *t;
2346 
2347             pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
2348 
2349             if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
2350                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2351 
2352             parse_transport_properties(t, &arg_i);
2353         } else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2354             pa_log_info("Properties changed in remote endpoint %s", dbus_message_get_path(m));
2355 
2356             parse_remote_endpoint_properties(y, dbus_message_get_path(m), &arg_i);
2357         }
2358 
2359         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2360     }
2361 
2362 fail:
2363     dbus_error_free(&err);
2364 
2365     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2366 }
2367 
pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile)2368 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
2369     switch(profile) {
2370         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2371             return "a2dp_sink";
2372         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2373             return "a2dp_source";
2374         case PA_BLUETOOTH_PROFILE_HSP_HS:
2375             return "headset_head_unit";
2376         case PA_BLUETOOTH_PROFILE_HSP_AG:
2377             return "headset_audio_gateway";
2378         case PA_BLUETOOTH_PROFILE_HFP_HF:
2379             return "handsfree_head_unit";
2380         case PA_BLUETOOTH_PROFILE_HFP_AG:
2381             return "handsfree_audio_gateway";
2382         case PA_BLUETOOTH_PROFILE_OFF:
2383             return "off";
2384     }
2385 
2386     return NULL;
2387 }
2388 
2389 /* Returns true when PA has to perform attenuation, false if this is the
2390  * responsibility of the peer.
2391  *
2392  * `peer_profile` is the profile of the peer.
2393  *
2394  * When the peer is in the HFP/HSP Audio Gateway role (PA is in headset role) PA
2395  * has to perform attenuation on both the incoming and outgoing stream. In the
2396  * HandsFree/HeadSet role both are attenuated on the peer.
2397  */
pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile)2398 bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile) {
2399     switch(peer_profile) {
2400         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2401             return false;
2402         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2403             return true;
2404         case PA_BLUETOOTH_PROFILE_HFP_HF:
2405         case PA_BLUETOOTH_PROFILE_HSP_HS:
2406             return false;
2407         case PA_BLUETOOTH_PROFILE_HFP_AG:
2408         case PA_BLUETOOTH_PROFILE_HSP_AG:
2409             return true;
2410         case PA_BLUETOOTH_PROFILE_OFF:
2411             pa_assert_not_reached();
2412     }
2413     pa_assert_not_reached();
2414 }
2415 
pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile)2416 bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile) {
2417     return profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2418 }
2419 
a2dp_sep_to_a2dp_endpoint_conf(const char * endpoint)2420 static const pa_a2dp_endpoint_conf *a2dp_sep_to_a2dp_endpoint_conf(const char *endpoint) {
2421     const char *codec_name;
2422 
2423     if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
2424         codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
2425     else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
2426         codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
2427     else
2428         return NULL;
2429 
2430     return pa_bluetooth_get_a2dp_endpoint_conf(codec_name);
2431 }
2432 
endpoint_set_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2433 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2434     pa_bluetooth_discovery *y = userdata;
2435     pa_bluetooth_device *d;
2436     pa_bluetooth_transport *t;
2437     const pa_a2dp_endpoint_conf *endpoint_conf = NULL;
2438     const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
2439     const uint8_t *config = NULL;
2440     int size = 0;
2441     pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
2442     DBusMessageIter args, props;
2443     DBusMessage *r;
2444 
2445     if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
2446         pa_log_error("Invalid signature for method SetConfiguration()");
2447         goto fail2;
2448     }
2449 
2450     dbus_message_iter_get_basic(&args, &path);
2451 
2452     if (pa_hashmap_get(y->transports, path)) {
2453         pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
2454         goto fail2;
2455     }
2456 
2457     pa_assert_se(dbus_message_iter_next(&args));
2458 
2459     dbus_message_iter_recurse(&args, &props);
2460     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
2461         goto fail;
2462 
2463     endpoint_path = dbus_message_get_path(m);
2464 
2465     /* Read transport properties */
2466     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
2467         const char *key;
2468         DBusMessageIter value, entry;
2469         int var;
2470 
2471         dbus_message_iter_recurse(&props, &entry);
2472         dbus_message_iter_get_basic(&entry, &key);
2473 
2474         dbus_message_iter_next(&entry);
2475         dbus_message_iter_recurse(&entry, &value);
2476 
2477         var = dbus_message_iter_get_arg_type(&value);
2478 
2479         if (pa_streq(key, "UUID")) {
2480             if (var != DBUS_TYPE_STRING) {
2481                 pa_log_error("Property %s of wrong type %c", key, (char)var);
2482                 goto fail;
2483             }
2484 
2485             dbus_message_iter_get_basic(&value, &uuid);
2486 
2487             if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
2488                 p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2489             else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
2490                 p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
2491 
2492             if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
2493                 (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
2494                 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
2495                 goto fail;
2496             }
2497         } else if (pa_streq(key, "Device")) {
2498             if (var != DBUS_TYPE_OBJECT_PATH) {
2499                 pa_log_error("Property %s of wrong type %c", key, (char)var);
2500                 goto fail;
2501             }
2502 
2503             dbus_message_iter_get_basic(&value, &dev_path);
2504         } else if (pa_streq(key, "Configuration")) {
2505             DBusMessageIter array;
2506 
2507             if (var != DBUS_TYPE_ARRAY) {
2508                 pa_log_error("Property %s of wrong type %c", key, (char)var);
2509                 goto fail;
2510             }
2511 
2512             dbus_message_iter_recurse(&value, &array);
2513             var = dbus_message_iter_get_arg_type(&array);
2514             if (var != DBUS_TYPE_BYTE) {
2515                 pa_log_error("%s is an array of wrong type %c", key, (char)var);
2516                 goto fail;
2517             }
2518 
2519             dbus_message_iter_get_fixed_array(&array, &config, &size);
2520 
2521             endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2522             pa_assert(endpoint_conf);
2523 
2524             if (!endpoint_conf->is_configuration_valid(config, size))
2525                 goto fail;
2526         }
2527 
2528         dbus_message_iter_next(&props);
2529     }
2530 
2531     if (!endpoint_conf)
2532         goto fail2;
2533 
2534     if ((d = pa_hashmap_get(y->devices, dev_path))) {
2535         if (!d->valid) {
2536             pa_log_error("Information about device %s is invalid", dev_path);
2537             goto fail2;
2538         }
2539     } else {
2540         /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
2541         pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
2542         d = device_create(y, dev_path);
2543     }
2544 
2545     if (d->transports[p] != NULL) {
2546         pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
2547         goto fail2;
2548     }
2549 
2550     sender = dbus_message_get_sender(m);
2551 
2552     pa_assert_se(r = dbus_message_new_method_return(m));
2553     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2554     dbus_message_unref(r);
2555 
2556     t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
2557     t->acquire = bluez5_transport_acquire_cb;
2558     t->release = bluez5_transport_release_cb;
2559     /* A2DP Absolute Volume is optional but BlueZ unconditionally reports
2560      * feature category 2, meaning supporting it is mandatory.
2561      * PulseAudio can and should perform the attenuation anyway in
2562      * the source role as it is the audio rendering device.
2563      */
2564     t->set_source_volume = pa_bluetooth_transport_set_source_volume;
2565 
2566     pa_bluetooth_transport_reconfigure(t, &endpoint_conf->bt_codec, a2dp_transport_write, NULL);
2567     pa_bluetooth_transport_put(t);
2568 
2569     pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2570     pa_log_info("Selected codec: %s", endpoint_conf->bt_codec.name);
2571 
2572     return NULL;
2573 
2574 fail:
2575     pa_log_error("Endpoint SetConfiguration(): invalid arguments");
2576 
2577 fail2:
2578     pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to set configuration"));
2579     return r;
2580 }
2581 
endpoint_select_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2582 static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2583     pa_bluetooth_discovery *y = userdata;
2584     const char *endpoint_path;
2585     uint8_t *cap;
2586     int size;
2587     const pa_a2dp_endpoint_conf *endpoint_conf;
2588     uint8_t config[MAX_A2DP_CAPS_SIZE];
2589     uint8_t *config_ptr = config;
2590     size_t config_size;
2591     DBusMessage *r;
2592     DBusError err;
2593 
2594     endpoint_path = dbus_message_get_path(m);
2595 
2596     dbus_error_init(&err);
2597 
2598     if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
2599         pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
2600         dbus_error_free(&err);
2601         goto fail;
2602     }
2603 
2604     endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2605     pa_assert(endpoint_conf);
2606 
2607     config_size = endpoint_conf->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
2608     if (config_size == 0)
2609         goto fail;
2610 
2611     pa_assert_se(r = dbus_message_new_method_return(m));
2612     pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
2613 
2614     return r;
2615 
2616 fail:
2617     pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to select configuration"));
2618     return r;
2619 }
2620 
endpoint_clear_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2621 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2622     pa_bluetooth_discovery *y = userdata;
2623     pa_bluetooth_transport *t;
2624     DBusMessage *r = NULL;
2625     DBusError err;
2626     const char *path;
2627 
2628     dbus_error_init(&err);
2629 
2630     if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
2631         pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
2632         dbus_error_free(&err);
2633         goto fail;
2634     }
2635 
2636     if ((t = pa_hashmap_get(y->transports, path))) {
2637         pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2638         pa_bluetooth_transport_free(t);
2639     }
2640 
2641     if (!dbus_message_get_no_reply(m))
2642         pa_assert_se(r = dbus_message_new_method_return(m));
2643 
2644     return r;
2645 
2646 fail:
2647     if (!dbus_message_get_no_reply(m))
2648         pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to clear configuration"));
2649     return r;
2650 }
2651 
endpoint_release(DBusConnection * conn,DBusMessage * m,void * userdata)2652 static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
2653     DBusMessage *r = NULL;
2654 
2655     /* From doc/media-api.txt in bluez:
2656      *
2657      *    This method gets called when the service daemon
2658      *    unregisters the endpoint. An endpoint can use it to do
2659      *    cleanup tasks. There is no need to unregister the
2660      *    endpoint, because when this method gets called it has
2661      *    already been unregistered.
2662      *
2663      * We don't have any cleanup to do. */
2664 
2665     /* Reply only if requested. Generally bluetoothd doesn't request a reply
2666      * to the Release() call. Sending replies when not requested on the system
2667      * bus tends to cause errors in syslog from dbus-daemon, because it
2668      * doesn't let unexpected replies through, so it's important to have this
2669      * check here. */
2670     if (!dbus_message_get_no_reply(m))
2671         pa_assert_se(r = dbus_message_new_method_return(m));
2672 
2673     return r;
2674 }
2675 
endpoint_handler(DBusConnection * c,DBusMessage * m,void * userdata)2676 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2677     struct pa_bluetooth_discovery *y = userdata;
2678     DBusMessage *r = NULL;
2679     const char *path, *interface, *member;
2680 
2681     pa_assert(y);
2682 
2683     path = dbus_message_get_path(m);
2684     interface = dbus_message_get_interface(m);
2685     member = dbus_message_get_member(m);
2686 
2687     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2688 
2689     if (!a2dp_sep_to_a2dp_endpoint_conf(path))
2690         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2691 
2692     if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2693         const char *xml = ENDPOINT_INTROSPECT_XML;
2694 
2695         pa_assert_se(r = dbus_message_new_method_return(m));
2696         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2697 
2698     } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
2699         r = endpoint_set_configuration(c, m, userdata);
2700     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
2701         r = endpoint_select_configuration(c, m, userdata);
2702     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
2703         r = endpoint_clear_configuration(c, m, userdata);
2704     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
2705         r = endpoint_release(c, m, userdata);
2706     else
2707         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2708 
2709     if (r) {
2710         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2711         dbus_message_unref(r);
2712     }
2713 
2714     return DBUS_HANDLER_RESULT_HANDLED;
2715 }
2716 
endpoint_init(pa_bluetooth_discovery * y,const char * endpoint)2717 static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
2718     static const DBusObjectPathVTable vtable_endpoint = {
2719         .message_function = endpoint_handler,
2720     };
2721 
2722     pa_assert(y);
2723     pa_assert(endpoint);
2724 
2725     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
2726                                                       &vtable_endpoint, y));
2727 }
2728 
endpoint_done(pa_bluetooth_discovery * y,const char * endpoint)2729 static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
2730     pa_assert(y);
2731     pa_assert(endpoint);
2732 
2733     dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
2734 }
2735 
append_a2dp_object(DBusMessageIter * iter,const char * endpoint,const char * uuid,uint8_t codec_id,uint8_t * capabilities,uint8_t capabilities_size)2736 static void append_a2dp_object(DBusMessageIter *iter, const char *endpoint, const char *uuid, uint8_t codec_id, uint8_t *capabilities, uint8_t capabilities_size) {
2737     const char *interface_name = BLUEZ_MEDIA_ENDPOINT_INTERFACE;
2738     DBusMessageIter object, array, entry, dict;
2739 
2740     dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &object);
2741     pa_assert_se(dbus_message_iter_append_basic(&object, DBUS_TYPE_OBJECT_PATH, &endpoint));
2742 
2743     dbus_message_iter_open_container(&object, DBUS_TYPE_ARRAY,
2744                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2745                                      DBUS_TYPE_STRING_AS_STRING
2746                                      DBUS_TYPE_ARRAY_AS_STRING
2747                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2748                                      DBUS_TYPE_STRING_AS_STRING
2749                                      DBUS_TYPE_VARIANT_AS_STRING
2750                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2751                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2752                                      &array);
2753 
2754     dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
2755     pa_assert_se(dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &interface_name));
2756 
2757     dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
2758                                      DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2759                                      DBUS_TYPE_STRING_AS_STRING
2760                                      DBUS_TYPE_VARIANT_AS_STRING
2761                                      DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2762                                      &dict);
2763 
2764     pa_dbus_append_basic_variant_dict_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
2765     pa_dbus_append_basic_variant_dict_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec_id);
2766     pa_dbus_append_basic_array_variant_dict_entry(&dict, "Capabilities", DBUS_TYPE_BYTE,
2767             capabilities, capabilities_size);
2768 
2769     dbus_message_iter_close_container(&entry, &dict);
2770     dbus_message_iter_close_container(&array, &entry);
2771     dbus_message_iter_close_container(&object, &array);
2772     dbus_message_iter_close_container(iter, &object);
2773 }
2774 
object_manager_handler(DBusConnection * c,DBusMessage * m,void * userdata)2775 static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2776     struct pa_bluetooth_discovery *y = userdata;
2777     DBusMessage *r;
2778     const char *path, *interface, *member;
2779 
2780     pa_assert(y);
2781 
2782     path = dbus_message_get_path(m);
2783     interface = dbus_message_get_interface(m);
2784     member = dbus_message_get_member(m);
2785 
2786     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2787 
2788     if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2789         const char *xml = OBJECT_MANAGER_INTROSPECT_XML;
2790 
2791         pa_assert_se(r = dbus_message_new_method_return(m));
2792         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2793     } else if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
2794         DBusMessageIter iter, array;
2795         int i;
2796 
2797         pa_assert_se(r = dbus_message_new_method_return(m));
2798 
2799         dbus_message_iter_init_append(r, &iter);
2800         dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
2801                                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2802                                          DBUS_TYPE_OBJECT_PATH_AS_STRING
2803                                          DBUS_TYPE_ARRAY_AS_STRING
2804                                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2805                                          DBUS_TYPE_STRING_AS_STRING
2806                                          DBUS_TYPE_ARRAY_AS_STRING
2807                                          DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2808                                          DBUS_TYPE_STRING_AS_STRING
2809                                          DBUS_TYPE_VARIANT_AS_STRING
2810                                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2811                                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2812                                          DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2813                                          &array);
2814 
2815         for (i = 0; i < pa_bluetooth_a2dp_endpoint_conf_count(); i++) {
2816             const pa_a2dp_endpoint_conf *endpoint_conf;
2817             uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
2818             uint8_t capabilities_size;
2819             uint8_t codec_id;
2820             char *endpoint;
2821 
2822             endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2823 
2824             codec_id = endpoint_conf->id.codec_id;
2825 
2826             if (endpoint_conf->can_be_supported(false)) {
2827                 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2828                 pa_assert(capabilities_size != 0);
2829                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2830                 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK, codec_id,
2831                         capabilities, capabilities_size);
2832                 pa_xfree(endpoint);
2833             }
2834 
2835             if (endpoint_conf->can_be_supported(true)) {
2836                 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2837                 pa_assert(capabilities_size != 0);
2838                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2839                 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE, codec_id,
2840                         capabilities, capabilities_size);
2841                 pa_xfree(endpoint);
2842             }
2843         }
2844 
2845         dbus_message_iter_close_container(&iter, &array);
2846     } else
2847         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2848 
2849     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2850     dbus_message_unref(r);
2851 
2852     return DBUS_HANDLER_RESULT_HANDLED;
2853 }
2854 
object_manager_init(pa_bluetooth_discovery * y)2855 static void object_manager_init(pa_bluetooth_discovery *y) {
2856     static const DBusObjectPathVTable vtable = {
2857         .message_function = object_manager_handler,
2858     };
2859 
2860     pa_assert(y);
2861     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection),
2862                 A2DP_OBJECT_MANAGER_PATH, &vtable, y));
2863 }
2864 
object_manager_done(pa_bluetooth_discovery * y)2865 static void object_manager_done(pa_bluetooth_discovery *y) {
2866     pa_assert(y);
2867     dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection),
2868             A2DP_OBJECT_MANAGER_PATH);
2869 }
2870 
pa_bluetooth_discovery_get(pa_core * c,int headset_backend,bool enable_native_hsp_hs,bool enable_native_hfp_hf,bool enable_msbc)2871 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) {
2872     pa_bluetooth_discovery *y;
2873     DBusError err;
2874     DBusConnection *conn;
2875     unsigned i, count;
2876     const pa_a2dp_endpoint_conf *endpoint_conf;
2877     char *endpoint;
2878 
2879     pa_bluetooth_a2dp_codec_gst_init();
2880     y = pa_xnew0(pa_bluetooth_discovery, 1);
2881     PA_REFCNT_INIT(y);
2882     y->core = c;
2883     y->headset_backend = headset_backend;
2884     y->enable_native_hsp_hs = enable_native_hsp_hs;
2885     y->enable_native_hfp_hf = enable_native_hfp_hf;
2886     y->enable_msbc = enable_msbc;
2887     y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2888                                       (pa_free_cb_t) adapter_free);
2889     y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2890                                      (pa_free_cb_t) device_free);
2891     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2892     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2893 
2894     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2895         pa_hook_init(&y->hooks[i], y);
2896 
2897     pa_shared_set(c, "bluetooth-discovery", y);
2898 
2899     dbus_error_init(&err);
2900 
2901     if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
2902         pa_log_error("Failed to get D-Bus connection: %s", err.message);
2903         goto fail;
2904     }
2905 
2906     conn = pa_dbus_connection_get(y->connection);
2907 
2908     /* dynamic detection of bluetooth audio devices */
2909     if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2910         pa_log_error("Failed to add filter function");
2911         goto fail;
2912     }
2913     y->filter_added = true;
2914 
2915     if (pa_dbus_add_matches(conn, &err,
2916             "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'"
2917             ",arg0='" BLUEZ_SERVICE "'",
2918             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',member='InterfacesAdded'",
2919             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
2920             "member='InterfacesRemoved'",
2921             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2922             ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
2923             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2924             ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
2925             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2926             ",arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
2927             "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2928             ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
2929             NULL) < 0) {
2930         pa_log_error("Failed to add D-Bus matches: %s", err.message);
2931         goto fail;
2932     }
2933     y->matches_added = true;
2934 
2935     object_manager_init(y);
2936 
2937     count = pa_bluetooth_a2dp_endpoint_conf_count();
2938     for (i = 0; i < count; i++) {
2939         endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2940         if (endpoint_conf->can_be_supported(false)) {
2941             endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2942             endpoint_init(y, endpoint);
2943             pa_xfree(endpoint);
2944         }
2945 
2946         if (endpoint_conf->can_be_supported(true)) {
2947             endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2948             endpoint_init(y, endpoint);
2949             pa_xfree(endpoint);
2950         }
2951     }
2952 
2953     get_managed_objects(y);
2954 
2955     return y;
2956 
2957 fail:
2958     pa_bluetooth_discovery_unref(y);
2959     dbus_error_free(&err);
2960 
2961     return NULL;
2962 }
2963 
pa_bluetooth_discovery_ref(pa_bluetooth_discovery * y)2964 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2965     pa_assert(y);
2966     pa_assert(PA_REFCNT_VALUE(y) > 0);
2967 
2968     PA_REFCNT_INC(y);
2969 
2970     return y;
2971 }
2972 
pa_bluetooth_discovery_unref(pa_bluetooth_discovery * y)2973 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2974     unsigned i, count;
2975     const pa_a2dp_endpoint_conf *endpoint_conf;
2976     char *endpoint;
2977 
2978     pa_assert(y);
2979     pa_assert(PA_REFCNT_VALUE(y) > 0);
2980 
2981     if (PA_REFCNT_DEC(y) > 0)
2982         return;
2983 
2984     pa_dbus_free_pending_list(&y->pending);
2985 
2986     if (y->ofono_backend)
2987         pa_bluetooth_ofono_backend_free(y->ofono_backend);
2988     if (y->native_backend)
2989         pa_bluetooth_native_backend_free(y->native_backend);
2990 
2991     if (y->adapters)
2992         pa_hashmap_free(y->adapters);
2993 
2994     if (y->devices)
2995         pa_hashmap_free(y->devices);
2996 
2997     if (y->transports) {
2998         pa_assert(pa_hashmap_isempty(y->transports));
2999         pa_hashmap_free(y->transports);
3000     }
3001 
3002     if (y->connection) {
3003 
3004         if (y->matches_added)
3005             pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
3006                 "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',"
3007                 "arg0='" BLUEZ_SERVICE "'",
3008                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3009                 "member='InterfacesAdded'",
3010                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3011                 "member='InterfacesRemoved'",
3012                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3013                 "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
3014                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3015                 "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
3016                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3017                 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
3018                 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3019                 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
3020                 NULL);
3021 
3022         if (y->filter_added)
3023             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
3024 
3025         object_manager_done(y);
3026 
3027         count = pa_bluetooth_a2dp_endpoint_conf_count();
3028         for (i = 0; i < count; i++) {
3029             endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
3030 
3031             if (endpoint_conf->can_be_supported(false)) {
3032                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
3033                 endpoint_done(y, endpoint);
3034                 pa_xfree(endpoint);
3035             }
3036 
3037             if (endpoint_conf->can_be_supported(true)) {
3038                 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
3039                 endpoint_done(y, endpoint);
3040                 pa_xfree(endpoint);
3041             }
3042         }
3043 
3044         pa_dbus_connection_unref(y->connection);
3045     }
3046 
3047     pa_shared_remove(y->core, "bluetooth-discovery");
3048     pa_xfree(y);
3049 }
3050