• 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 <pulse/rtclock.h>
26 #include <pulse/timeval.h>
27 #include <pulse/xmalloc.h>
28 
29 #include <pulsecore/core.h>
30 #include <pulsecore/core-util.h>
31 #include <pulsecore/dbus-shared.h>
32 #include <pulsecore/log.h>
33 #include <pulsecore/macro.h>
34 #include <pulsecore/refcnt.h>
35 #include <pulsecore/shared.h>
36 
37 #include "a2dp-codec-util.h"
38 #include "a2dp-codecs.h"
39 
40 #include "bluez5-util.h"
41 
42 #define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
43 
44 #define BLUEZ_SERVICE "org.bluez"
45 #define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
46 #define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
47 #define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
48 #define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
49 #define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
50 
51 #define BLUEZ_ERROR_NOT_SUPPORTED "org.bluez.Error.NotSupported"
52 
53 #define A2DP_SOURCE_ENDPOINT "/MediaEndpoint/A2DPSource"
54 #define A2DP_SINK_ENDPOINT "/MediaEndpoint/A2DPSink"
55 
56 #define ENDPOINT_INTROSPECT_XML                                         \
57     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
58     "<node>"                                                            \
59     " <interface name=\"" BLUEZ_MEDIA_ENDPOINT_INTERFACE "\">"          \
60     "  <method name=\"SetConfiguration\">"                              \
61     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
62     "   <arg name=\"properties\" direction=\"in\" type=\"ay\"/>"        \
63     "  </method>"                                                       \
64     "  <method name=\"SelectConfiguration\">"                           \
65     "   <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>"      \
66     "   <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>"    \
67     "  </method>"                                                       \
68     "  <method name=\"ClearConfiguration\">"                            \
69     "   <arg name=\"transport\" direction=\"in\" type=\"o\"/>"          \
70     "  </method>"                                                       \
71     "  <method name=\"Release\">"                                       \
72     "  </method>"                                                       \
73     " </interface>"                                                     \
74     " <interface name=\"org.freedesktop.DBus.Introspectable\">"         \
75     "  <method name=\"Introspect\">"                                    \
76     "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
77     "  </method>"                                                       \
78     " </interface>"                                                     \
79     "</node>"
80 
81 struct pa_bluetooth_discovery {
82     PA_REFCNT_DECLARE;
83 
84     pa_core *core;
85     pa_dbus_connection *connection;
86     bool filter_added;
87     bool matches_added;
88     bool objects_listed;
89     pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
90     pa_hashmap *adapters;
91     pa_hashmap *devices;
92     pa_hashmap *transports;
93 
94     int headset_backend;
95     pa_bluetooth_backend *ofono_backend, *native_backend;
96     PA_LLIST_HEAD(pa_dbus_pending, pending);
97 };
98 
send_and_add_to_pending(pa_bluetooth_discovery * y,DBusMessage * m,DBusPendingCallNotifyFunction func,void * call_data)99 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
100                                                                   DBusPendingCallNotifyFunction func, void *call_data) {
101     pa_dbus_pending *p;
102     DBusPendingCall *call;
103 
104     pa_assert(y);
105     pa_assert(m);
106 
107     pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
108 
109     p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
110     PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
111     dbus_pending_call_set_notify(call, func, p, NULL);
112 
113     return p;
114 }
115 
check_variant_property(DBusMessageIter * i)116 static const char *check_variant_property(DBusMessageIter *i) {
117     const char *key;
118 
119     pa_assert(i);
120 
121     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
122         pa_log_error("Property name not a string.");
123         return NULL;
124     }
125 
126     dbus_message_iter_get_basic(i, &key);
127 
128     if (!dbus_message_iter_next(i)) {
129         pa_log_error("Property value missing");
130         return NULL;
131     }
132 
133     if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
134         pa_log_error("Property value not a variant.");
135         return NULL;
136     }
137 
138     return key;
139 }
140 
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)141 pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
142                                                    pa_bluetooth_profile_t p, const uint8_t *config, size_t size) {
143     pa_bluetooth_transport *t;
144 
145     t = pa_xnew0(pa_bluetooth_transport, 1);
146     t->device = d;
147     t->owner = pa_xstrdup(owner);
148     t->path = pa_xstrdup(path);
149     t->profile = p;
150     t->config_size = size;
151 
152     if (size > 0) {
153         t->config = pa_xnew(uint8_t, size);
154         memcpy(t->config, config, size);
155     }
156 
157     return t;
158 }
159 
transport_state_to_string(pa_bluetooth_transport_state_t state)160 static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
161     switch(state) {
162         case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
163             return "disconnected";
164         case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
165             return "idle";
166         case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
167             return "playing";
168     }
169 
170     return "invalid";
171 }
172 
device_supports_profile(pa_bluetooth_device * device,pa_bluetooth_profile_t profile)173 static bool device_supports_profile(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
174     switch (profile) {
175         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
176             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK);
177         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
178             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE);
179         case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
180             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS)
181                 || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT)
182                 || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
183         case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
184             return !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG)
185                 || !!pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG);
186         case PA_BLUETOOTH_PROFILE_OFF:
187             pa_assert_not_reached();
188     }
189 
190     pa_assert_not_reached();
191 }
192 
device_is_profile_connected(pa_bluetooth_device * device,pa_bluetooth_profile_t profile)193 static bool device_is_profile_connected(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
194     if (device->transports[profile] && device->transports[profile]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
195         return true;
196     else
197         return false;
198 }
199 
device_count_disconnected_profiles(pa_bluetooth_device * device)200 static unsigned device_count_disconnected_profiles(pa_bluetooth_device *device) {
201     pa_bluetooth_profile_t profile;
202     unsigned count = 0;
203 
204     for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
205         if (!device_supports_profile(device, profile))
206             continue;
207 
208         if (!device_is_profile_connected(device, profile))
209             count++;
210     }
211 
212     return count;
213 }
214 
device_stop_waiting_for_profiles(pa_bluetooth_device * device)215 static void device_stop_waiting_for_profiles(pa_bluetooth_device *device) {
216     if (!device->wait_for_profiles_timer)
217         return;
218 
219     device->discovery->core->mainloop->time_free(device->wait_for_profiles_timer);
220     device->wait_for_profiles_timer = NULL;
221 }
222 
wait_for_profiles_cb(pa_mainloop_api * api,pa_time_event * event,const struct timeval * tv,void * userdata)223 static void wait_for_profiles_cb(pa_mainloop_api *api, pa_time_event* event, const struct timeval *tv, void *userdata) {
224     pa_bluetooth_device *device = userdata;
225     pa_strbuf *buf;
226     pa_bluetooth_profile_t profile;
227     bool first = true;
228     char *profiles_str;
229 
230     device_stop_waiting_for_profiles(device);
231 
232     buf = pa_strbuf_new();
233 
234     for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
235         if (device_is_profile_connected(device, profile))
236             continue;
237 
238         if (!device_supports_profile(device, profile))
239             continue;
240 
241         if (first)
242             first = false;
243         else
244             pa_strbuf_puts(buf, ", ");
245 
246         pa_strbuf_puts(buf, pa_bluetooth_profile_to_string(profile));
247     }
248 
249     profiles_str = pa_strbuf_to_string_free(buf);
250     pa_log_debug("Timeout expired, and device %s still has disconnected profiles: %s",
251                  device->path, profiles_str);
252     pa_xfree(profiles_str);
253     pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
254 }
255 
device_start_waiting_for_profiles(pa_bluetooth_device * device)256 static void device_start_waiting_for_profiles(pa_bluetooth_device *device) {
257     pa_assert(!device->wait_for_profiles_timer);
258     device->wait_for_profiles_timer = pa_core_rttime_new(device->discovery->core,
259                                                          pa_rtclock_now() + WAIT_FOR_PROFILES_TIMEOUT_USEC,
260                                                          wait_for_profiles_cb, device);
261 }
262 
pa_bluetooth_transport_set_state(pa_bluetooth_transport * t,pa_bluetooth_transport_state_t state)263 void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) {
264     bool old_any_connected;
265     unsigned n_disconnected_profiles;
266     bool new_device_appeared;
267     bool device_disconnected;
268 
269     pa_assert(t);
270 
271     if (t->state == state)
272         return;
273 
274     old_any_connected = pa_bluetooth_device_any_transport_connected(t->device);
275 
276     pa_log_debug("Transport %s state: %s -> %s",
277                  t->path, transport_state_to_string(t->state), transport_state_to_string(state));
278 
279     t->state = state;
280 
281     pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
282 
283     /* If there are profiles that are expected to get connected soon (based
284      * on the UUID list), we wait for a bit before announcing the new
285      * device, so that all profiles have time to get connected before the
286      * card object is created. If we didn't wait, the card would always
287      * have only one profile marked as available in the initial state,
288      * which would prevent module-card-restore from restoring the initial
289      * profile properly. */
290 
291     n_disconnected_profiles = device_count_disconnected_profiles(t->device);
292 
293     new_device_appeared = !old_any_connected && pa_bluetooth_device_any_transport_connected(t->device);
294     device_disconnected = old_any_connected && !pa_bluetooth_device_any_transport_connected(t->device);
295 
296     if (new_device_appeared) {
297         if (n_disconnected_profiles > 0)
298             device_start_waiting_for_profiles(t->device);
299         else
300             pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
301         return;
302     }
303 
304     if (device_disconnected) {
305         if (t->device->wait_for_profiles_timer) {
306             /* If the timer is still running when the device disconnects, we
307              * never sent the notification of the device getting connected, so
308              * we don't need to send a notification about the disconnection
309              * either. Let's just stop the timer. */
310             device_stop_waiting_for_profiles(t->device);
311         } else
312             pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
313         return;
314     }
315 
316     if (n_disconnected_profiles == 0 && t->device->wait_for_profiles_timer) {
317         /* All profiles are now connected, so we can stop the wait timer and
318          * send a notification of the new device. */
319         device_stop_waiting_for_profiles(t->device);
320         pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
321     }
322 }
323 
pa_bluetooth_transport_put(pa_bluetooth_transport * t)324 void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
325     pa_assert(t);
326 
327     t->device->transports[t->profile] = t;
328     pa_assert_se(pa_hashmap_put(t->device->discovery->transports, t->path, t) >= 0);
329     pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
330 }
331 
pa_bluetooth_transport_unlink(pa_bluetooth_transport * t)332 void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t) {
333     pa_assert(t);
334 
335     pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED);
336     pa_hashmap_remove(t->device->discovery->transports, t->path);
337     t->device->transports[t->profile] = NULL;
338 }
339 
pa_bluetooth_transport_free(pa_bluetooth_transport * t)340 void pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
341     pa_assert(t);
342 
343     if (t->destroy)
344         t->destroy(t);
345     pa_bluetooth_transport_unlink(t);
346 
347     pa_xfree(t->owner);
348     pa_xfree(t->path);
349     pa_xfree(t->config);
350     pa_xfree(t);
351 }
352 
bluez5_transport_acquire_cb(pa_bluetooth_transport * t,bool optional,size_t * imtu,size_t * omtu)353 static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
354     DBusMessage *m, *r;
355     DBusError err;
356     int ret;
357     uint16_t i, o;
358     const char *method = optional ? "TryAcquire" : "Acquire";
359 
360     pa_assert(t);
361     pa_assert(t->device);
362     pa_assert(t->device->discovery);
363 
364     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, method));
365 
366     dbus_error_init(&err);
367 
368     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
369     dbus_message_unref(m);
370     m = NULL;
371     if (!r) {
372         if (optional && pa_streq(err.name, "org.bluez.Error.NotAvailable"))
373             pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
374         else
375             pa_log_error("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
376 
377         dbus_error_free(&err);
378         return -1;
379     }
380 
381     if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
382                                DBUS_TYPE_INVALID)) {
383         pa_log_error("Failed to parse %s() reply: %s", method, err.message);
384         dbus_error_free(&err);
385         ret = -1;
386         goto finish;
387     }
388 
389     if (imtu)
390         *imtu = i;
391 
392     if (omtu)
393         *omtu = o;
394 
395 finish:
396     dbus_message_unref(r);
397     return ret;
398 }
399 
bluez5_transport_release_cb(pa_bluetooth_transport * t)400 static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
401     DBusMessage *m, *r;
402     DBusError err;
403 
404     pa_assert(t);
405     pa_assert(t->device);
406     pa_assert(t->device->discovery);
407 
408     dbus_error_init(&err);
409 
410     if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
411         pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
412         return;
413     }
414 
415     pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, "Release"));
416     r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
417     dbus_message_unref(m);
418     m = NULL;
419     if (r) {
420         dbus_message_unref(r);
421         r = NULL;
422     }
423 
424     if (dbus_error_is_set(&err)) {
425         pa_log_error("Failed to release transport %s: %s", t->path, err.message);
426         dbus_error_free(&err);
427     } else
428         pa_log_info("Transport %s released", t->path);
429 }
430 
pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device * d)431 bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
432     unsigned i;
433 
434     pa_assert(d);
435 
436     if (!d->valid)
437         return false;
438 
439     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
440         if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
441             return true;
442 
443     return false;
444 }
445 
transport_state_from_string(const char * value,pa_bluetooth_transport_state_t * state)446 static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
447     pa_assert(value);
448     pa_assert(state);
449 
450     if (pa_streq(value, "idle"))
451         *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
452     else if (pa_streq(value, "pending") || pa_streq(value, "active"))
453         *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
454     else
455         return -1;
456 
457     return 0;
458 }
459 
parse_transport_property(pa_bluetooth_transport * t,DBusMessageIter * i)460 static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
461     const char *key;
462     DBusMessageIter variant_i;
463 
464     key = check_variant_property(i);
465     if (key == NULL)
466         return;
467 
468     dbus_message_iter_recurse(i, &variant_i);
469 
470     switch (dbus_message_iter_get_arg_type(&variant_i)) {
471 
472         case DBUS_TYPE_STRING: {
473 
474             const char *value;
475             dbus_message_iter_get_basic(&variant_i, &value);
476 
477             if (pa_streq(key, "State")) {
478                 pa_bluetooth_transport_state_t state;
479 
480                 if (transport_state_from_string(value, &state) < 0) {
481                     pa_log_error("Invalid state received: %s", value);
482                     return;
483                 }
484 
485                 pa_bluetooth_transport_set_state(t, state);
486             }
487 
488             break;
489         }
490     }
491 
492     return;
493 }
494 
parse_transport_properties(pa_bluetooth_transport * t,DBusMessageIter * i)495 static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
496     DBusMessageIter element_i;
497 
498     dbus_message_iter_recurse(i, &element_i);
499 
500     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
501         DBusMessageIter dict_i;
502 
503         dbus_message_iter_recurse(&element_i, &dict_i);
504 
505         parse_transport_property(t, &dict_i);
506 
507         dbus_message_iter_next(&element_i);
508     }
509 
510     return 0;
511 }
512 
device_create(pa_bluetooth_discovery * y,const char * path)513 static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char *path) {
514     pa_bluetooth_device *d;
515 
516     pa_assert(y);
517     pa_assert(path);
518 
519     d = pa_xnew0(pa_bluetooth_device, 1);
520     d->discovery = y;
521     d->path = pa_xstrdup(path);
522     d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
523 
524     pa_hashmap_put(y->devices, d->path, d);
525 
526     return d;
527 }
528 
pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery * y,const char * path)529 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path) {
530     pa_bluetooth_device *d;
531 
532     pa_assert(y);
533     pa_assert(PA_REFCNT_VALUE(y) > 0);
534     pa_assert(path);
535 
536     if ((d = pa_hashmap_get(y->devices, path)) && d->valid)
537         return d;
538 
539     return NULL;
540 }
541 
pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery * y,const char * remote,const char * local)542 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
543     pa_bluetooth_device *d;
544     void *state = NULL;
545 
546     pa_assert(y);
547     pa_assert(PA_REFCNT_VALUE(y) > 0);
548     pa_assert(remote);
549     pa_assert(local);
550 
551     while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
552         if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
553             return d;
554 
555     return NULL;
556 }
557 
device_free(pa_bluetooth_device * d)558 static void device_free(pa_bluetooth_device *d) {
559     unsigned i;
560 
561     pa_assert(d);
562 
563     device_stop_waiting_for_profiles(d);
564 
565     pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UNLINK], d);
566 
567     for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
568         pa_bluetooth_transport *t;
569 
570         if (!(t = d->transports[i]))
571             continue;
572 
573         pa_bluetooth_transport_free(t);
574     }
575 
576     if (d->uuids)
577         pa_hashmap_free(d->uuids);
578 
579     pa_xfree(d->path);
580     pa_xfree(d->alias);
581     pa_xfree(d->address);
582     pa_xfree(d->adapter_path);
583     pa_xfree(d);
584 }
585 
device_remove(pa_bluetooth_discovery * y,const char * path)586 static void device_remove(pa_bluetooth_discovery *y, const char *path) {
587     pa_bluetooth_device *d;
588 
589     if (!(d = pa_hashmap_remove(y->devices, path)))
590         pa_log_warn("Unknown device removed %s", path);
591     else {
592         pa_log_debug("Device %s removed", path);
593         device_free(d);
594     }
595 }
596 
device_set_valid(pa_bluetooth_device * device,bool valid)597 static void device_set_valid(pa_bluetooth_device *device, bool valid) {
598     bool old_any_connected;
599 
600     pa_assert(device);
601 
602     if (valid == device->valid)
603         return;
604 
605     old_any_connected = pa_bluetooth_device_any_transport_connected(device);
606     device->valid = valid;
607 
608     if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
609         pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
610 }
611 
device_update_valid(pa_bluetooth_device * d)612 static void device_update_valid(pa_bluetooth_device *d) {
613     pa_assert(d);
614 
615     if (!d->properties_received) {
616         pa_assert(!d->valid);
617         return;
618     }
619 
620     /* Check if mandatory properties are set. */
621     if (!d->address || !d->adapter_path || !d->alias) {
622         device_set_valid(d, false);
623         return;
624     }
625 
626     if (!d->adapter || !d->adapter->valid) {
627         device_set_valid(d, false);
628         return;
629     }
630 
631     device_set_valid(d, true);
632 }
633 
device_set_adapter(pa_bluetooth_device * device,pa_bluetooth_adapter * adapter)634 static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
635     pa_assert(device);
636 
637     if (adapter == device->adapter)
638         return;
639 
640     device->adapter = adapter;
641 
642     device_update_valid(device);
643 }
644 
adapter_create(pa_bluetooth_discovery * y,const char * path)645 static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
646     pa_bluetooth_adapter *a;
647 
648     pa_assert(y);
649     pa_assert(path);
650 
651     a = pa_xnew0(pa_bluetooth_adapter, 1);
652     a->discovery = y;
653     a->path = pa_xstrdup(path);
654 
655     pa_hashmap_put(y->adapters, a->path, a);
656 
657     return a;
658 }
659 
adapter_free(pa_bluetooth_adapter * a)660 static void adapter_free(pa_bluetooth_adapter *a) {
661     pa_bluetooth_device *d;
662     void *state;
663 
664     pa_assert(a);
665     pa_assert(a->discovery);
666 
667     PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
668         if (d->adapter == a)
669             device_set_adapter(d, NULL);
670 
671     pa_xfree(a->path);
672     pa_xfree(a->address);
673     pa_xfree(a);
674 }
675 
adapter_remove(pa_bluetooth_discovery * y,const char * path)676 static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
677     pa_bluetooth_adapter *a;
678 
679     if (!(a = pa_hashmap_remove(y->adapters, path)))
680         pa_log_warn("Unknown adapter removed %s", path);
681     else {
682         pa_log_debug("Adapter %s removed", path);
683         adapter_free(a);
684     }
685 }
686 
parse_device_property(pa_bluetooth_device * d,DBusMessageIter * i)687 static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
688     const char *key;
689     DBusMessageIter variant_i;
690 
691     pa_assert(d);
692 
693     key = check_variant_property(i);
694     if (key == NULL) {
695         pa_log_error("Received invalid property for device %s", d->path);
696         return;
697     }
698 
699     dbus_message_iter_recurse(i, &variant_i);
700 
701     switch (dbus_message_iter_get_arg_type(&variant_i)) {
702 
703         case DBUS_TYPE_STRING: {
704             const char *value;
705             dbus_message_iter_get_basic(&variant_i, &value);
706 
707             if (pa_streq(key, "Alias")) {
708                 pa_xfree(d->alias);
709                 d->alias = pa_xstrdup(value);
710                 pa_log_debug("%s: %s", key, value);
711             } else if (pa_streq(key, "Address")) {
712                 if (d->properties_received) {
713                     pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
714                     return;
715                 }
716 
717                 if (d->address) {
718                     pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
719                     return;
720                 }
721 
722                 d->address = pa_xstrdup(value);
723                 pa_log_debug("%s: %s", key, value);
724             }
725 
726             break;
727         }
728 
729         case DBUS_TYPE_OBJECT_PATH: {
730             const char *value;
731             dbus_message_iter_get_basic(&variant_i, &value);
732 
733             if (pa_streq(key, "Adapter")) {
734 
735                 if (d->properties_received) {
736                     pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
737                     return;
738                 }
739 
740                 if (d->adapter_path) {
741                     pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
742                     return;
743                 }
744 
745                 d->adapter_path = pa_xstrdup(value);
746                 pa_log_debug("%s: %s", key, value);
747             }
748 
749             break;
750         }
751 
752         case DBUS_TYPE_UINT32: {
753             uint32_t value;
754             dbus_message_iter_get_basic(&variant_i, &value);
755 
756             if (pa_streq(key, "Class")) {
757                 d->class_of_device = value;
758                 pa_log_debug("%s: %d", key, value);
759             }
760 
761             break;
762         }
763 
764         case DBUS_TYPE_ARRAY: {
765             DBusMessageIter ai;
766             dbus_message_iter_recurse(&variant_i, &ai);
767 
768             if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
769                 /* bluetoothd never removes UUIDs from a device object so we
770                  * don't need to check for disappeared UUIDs here. */
771                 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
772                     const char *value;
773                     char *uuid;
774 
775                     dbus_message_iter_get_basic(&ai, &value);
776 
777                     if (pa_hashmap_get(d->uuids, value)) {
778                         dbus_message_iter_next(&ai);
779                         continue;
780                     }
781 
782                     uuid = pa_xstrdup(value);
783                     pa_hashmap_put(d->uuids, uuid, uuid);
784 
785                     pa_log_debug("%s: %s", key, value);
786                     dbus_message_iter_next(&ai);
787                 }
788             }
789 
790             break;
791         }
792     }
793 }
794 
parse_device_properties(pa_bluetooth_device * d,DBusMessageIter * i)795 static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
796     DBusMessageIter element_i;
797 
798     dbus_message_iter_recurse(i, &element_i);
799 
800     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
801         DBusMessageIter dict_i;
802 
803         dbus_message_iter_recurse(&element_i, &dict_i);
804         parse_device_property(d, &dict_i);
805         dbus_message_iter_next(&element_i);
806     }
807 
808     if (!d->properties_received) {
809         d->properties_received = true;
810         device_update_valid(d);
811 
812         if (!d->address || !d->adapter_path || !d->alias)
813             pa_log_error("Non-optional information missing for device %s", d->path);
814     }
815 }
816 
parse_adapter_properties(pa_bluetooth_adapter * a,DBusMessageIter * i,bool is_property_change)817 static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
818     DBusMessageIter element_i;
819 
820     pa_assert(a);
821 
822     dbus_message_iter_recurse(i, &element_i);
823 
824     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
825         DBusMessageIter dict_i, variant_i;
826         const char *key;
827 
828         dbus_message_iter_recurse(&element_i, &dict_i);
829 
830         key = check_variant_property(&dict_i);
831         if (key == NULL) {
832             pa_log_error("Received invalid property for adapter %s", a->path);
833             return;
834         }
835 
836         dbus_message_iter_recurse(&dict_i, &variant_i);
837 
838         if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
839             const char *value;
840 
841             if (is_property_change) {
842                 pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
843                 return;
844             }
845 
846             if (a->address) {
847                 pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
848                 return;
849             }
850 
851             dbus_message_iter_get_basic(&variant_i, &value);
852             a->address = pa_xstrdup(value);
853             a->valid = true;
854         }
855 
856         dbus_message_iter_next(&element_i);
857     }
858 }
859 
register_endpoint_reply(DBusPendingCall * pending,void * userdata)860 static void register_endpoint_reply(DBusPendingCall *pending, void *userdata) {
861     DBusMessage *r;
862     pa_dbus_pending *p;
863     pa_bluetooth_discovery *y;
864     char *endpoint;
865 
866     pa_assert(pending);
867     pa_assert_se(p = userdata);
868     pa_assert_se(y = p->context_data);
869     pa_assert_se(endpoint = p->call_data);
870     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
871 
872     if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
873         pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
874         goto finish;
875     }
876 
877     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
878         pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
879                      pa_dbus_get_error_message(r));
880         goto finish;
881     }
882 
883 finish:
884     dbus_message_unref(r);
885 
886     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
887     pa_dbus_pending_free(p);
888 
889     pa_xfree(endpoint);
890 }
891 
register_endpoint(pa_bluetooth_discovery * y,const pa_a2dp_codec * a2dp_codec,const char * path,const char * endpoint,const char * uuid)892 static void register_endpoint(pa_bluetooth_discovery *y, const pa_a2dp_codec *a2dp_codec, const char *path, const char *endpoint, const char *uuid) {
893     DBusMessage *m;
894     DBusMessageIter i, d;
895     uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
896     size_t capabilities_size;
897     uint8_t codec_id;
898 
899     pa_log_debug("Registering %s on adapter %s", endpoint, path);
900 
901     codec_id = a2dp_codec->id.codec_id;
902     capabilities_size = a2dp_codec->fill_capabilities(capabilities);
903     pa_assert(capabilities_size != 0);
904 
905     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
906 
907     dbus_message_iter_init_append(m, &i);
908     pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
909     dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
910                                          DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &d);
911     pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
912     pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
913     pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
914 
915     dbus_message_iter_close_container(&i, &d);
916 
917     send_and_add_to_pending(y, m, register_endpoint_reply, pa_xstrdup(endpoint));
918 }
919 
parse_interfaces_and_properties(pa_bluetooth_discovery * y,DBusMessageIter * dict_i)920 static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
921     DBusMessageIter element_i;
922     const char *path;
923     void *state;
924     pa_bluetooth_device *d;
925 
926     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
927     dbus_message_iter_get_basic(dict_i, &path);
928 
929     pa_assert_se(dbus_message_iter_next(dict_i));
930     pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
931 
932     dbus_message_iter_recurse(dict_i, &element_i);
933 
934     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
935         DBusMessageIter iface_i;
936         const char *interface;
937 
938         dbus_message_iter_recurse(&element_i, &iface_i);
939 
940         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
941         dbus_message_iter_get_basic(&iface_i, &interface);
942 
943         pa_assert_se(dbus_message_iter_next(&iface_i));
944         pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
945 
946         if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
947 
948             const pa_a2dp_codec *a2dp_codec_sbc;
949             pa_bluetooth_adapter *a;
950 
951             if ((a = pa_hashmap_get(y->adapters, path))) {
952                 pa_log_error("Found duplicated D-Bus path for adapter %s", path);
953                 return;
954             } else
955                 a = adapter_create(y, path);
956 
957             pa_log_debug("Adapter %s found", path);
958 
959             parse_adapter_properties(a, &iface_i, false);
960 
961             if (!a->valid)
962                 return;
963 
964             /* Currently only one A2DP codec is supported, so register only SBC
965              * Support for multiple codecs needs to use a new Bluez API which
966              * pulseaudio does not implement yet, patches are waiting in queue */
967             a2dp_codec_sbc = pa_bluetooth_get_a2dp_codec("sbc");
968             pa_assert(a2dp_codec_sbc);
969             register_endpoint(y, a2dp_codec_sbc, path, A2DP_SINK_ENDPOINT "/sbc", PA_BLUETOOTH_UUID_A2DP_SINK);
970             register_endpoint(y, a2dp_codec_sbc, path, A2DP_SOURCE_ENDPOINT "/sbc", PA_BLUETOOTH_UUID_A2DP_SOURCE);
971 
972         } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
973 
974             if ((d = pa_hashmap_get(y->devices, path))) {
975                 if (d->properties_received) {
976                     pa_log_error("Found duplicated D-Bus path for device %s", path);
977                     return;
978                 }
979             } else
980                 d = device_create(y, path);
981 
982             pa_log_debug("Device %s found", d->path);
983 
984             parse_device_properties(d, &iface_i);
985 
986         } else
987             pa_log_debug("Unknown interface %s found, skipping", interface);
988 
989         dbus_message_iter_next(&element_i);
990     }
991 
992     PA_HASHMAP_FOREACH(d, y->devices, state) {
993         if (d->properties_received && !d->tried_to_link_with_adapter) {
994             if (d->adapter_path) {
995                 device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
996 
997                 if (!d->adapter)
998                     pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
999                 else if (!d->adapter->valid)
1000                     pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
1001             }
1002 
1003             d->tried_to_link_with_adapter = true;
1004         }
1005     }
1006 
1007     return;
1008 }
1009 
pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery * y,bool is_running)1010 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
1011     pa_assert(y);
1012 
1013     pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
1014     if (y->headset_backend != HEADSET_BACKEND_AUTO)
1015         return;
1016 
1017     /* If ofono starts running, all devices that might be connected to the HS role
1018      * need to be disconnected, so that the devices can be handled by ofono */
1019     if (is_running) {
1020         void *state;
1021         pa_bluetooth_device *d;
1022 
1023         PA_HASHMAP_FOREACH(d, y->devices, state) {
1024             if (device_supports_profile(d, PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY)) {
1025                 DBusMessage *m;
1026 
1027                 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, "org.bluez.Device1", "Disconnect"));
1028                 dbus_message_set_no_reply(m, true);
1029                 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
1030                 dbus_message_unref(m);
1031             }
1032         }
1033     }
1034 
1035     pa_bluetooth_native_backend_enable_hs_role(y->native_backend, !is_running);
1036 }
1037 
get_managed_objects_reply(DBusPendingCall * pending,void * userdata)1038 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
1039     pa_dbus_pending *p;
1040     pa_bluetooth_discovery *y;
1041     DBusMessage *r;
1042     DBusMessageIter arg_i, element_i;
1043 
1044     pa_assert_se(p = userdata);
1045     pa_assert_se(y = p->context_data);
1046     pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1047 
1048     if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
1049         pa_log_warn("BlueZ D-Bus ObjectManager not available");
1050         goto finish;
1051     }
1052 
1053     if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1054         pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1055         goto finish;
1056     }
1057 
1058     if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
1059         pa_log_error("Invalid reply signature for GetManagedObjects()");
1060         goto finish;
1061     }
1062 
1063     dbus_message_iter_recurse(&arg_i, &element_i);
1064     while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1065         DBusMessageIter dict_i;
1066 
1067         dbus_message_iter_recurse(&element_i, &dict_i);
1068 
1069         parse_interfaces_and_properties(y, &dict_i);
1070 
1071         dbus_message_iter_next(&element_i);
1072     }
1073 
1074     y->objects_listed = true;
1075 
1076     if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
1077         y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
1078     if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
1079         y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
1080 
1081 finish:
1082     dbus_message_unref(r);
1083 
1084     PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1085     pa_dbus_pending_free(p);
1086 }
1087 
get_managed_objects(pa_bluetooth_discovery * y)1088 static void get_managed_objects(pa_bluetooth_discovery *y) {
1089     DBusMessage *m;
1090 
1091     pa_assert(y);
1092 
1093     pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", "org.freedesktop.DBus.ObjectManager",
1094                                                   "GetManagedObjects"));
1095     send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
1096 }
1097 
pa_bluetooth_discovery_hook(pa_bluetooth_discovery * y,pa_bluetooth_hook_t hook)1098 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
1099     pa_assert(y);
1100     pa_assert(PA_REFCNT_VALUE(y) > 0);
1101 
1102     return &y->hooks[hook];
1103 }
1104 
filter_cb(DBusConnection * bus,DBusMessage * m,void * userdata)1105 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
1106     pa_bluetooth_discovery *y;
1107     DBusError err;
1108 
1109     pa_assert(bus);
1110     pa_assert(m);
1111     pa_assert_se(y = userdata);
1112 
1113     dbus_error_init(&err);
1114 
1115     if (dbus_message_is_signal(m, "org.freedesktop.DBus", "NameOwnerChanged")) {
1116         const char *name, *old_owner, *new_owner;
1117 
1118         if (!dbus_message_get_args(m, &err,
1119                                    DBUS_TYPE_STRING, &name,
1120                                    DBUS_TYPE_STRING, &old_owner,
1121                                    DBUS_TYPE_STRING, &new_owner,
1122                                    DBUS_TYPE_INVALID)) {
1123             pa_log_error("Failed to parse org.freedesktop.DBus.NameOwnerChanged: %s", err.message);
1124             goto fail;
1125         }
1126 
1127         if (pa_streq(name, BLUEZ_SERVICE)) {
1128             if (old_owner && *old_owner) {
1129                 pa_log_debug("Bluetooth daemon disappeared");
1130                 pa_hashmap_remove_all(y->devices);
1131                 pa_hashmap_remove_all(y->adapters);
1132                 y->objects_listed = false;
1133                 if (y->ofono_backend) {
1134                     pa_bluetooth_ofono_backend_free(y->ofono_backend);
1135                     y->ofono_backend = NULL;
1136                 }
1137                 if (y->native_backend) {
1138                     pa_bluetooth_native_backend_free(y->native_backend);
1139                     y->native_backend = NULL;
1140                 }
1141             }
1142 
1143             if (new_owner && *new_owner) {
1144                 pa_log_debug("Bluetooth daemon appeared");
1145                 get_managed_objects(y);
1146             }
1147         }
1148 
1149         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1150     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesAdded")) {
1151         DBusMessageIter arg_i;
1152 
1153         if (!y->objects_listed)
1154             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1155 
1156         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
1157             pa_log_error("Invalid signature found in InterfacesAdded");
1158             goto fail;
1159         }
1160 
1161         parse_interfaces_and_properties(y, &arg_i);
1162 
1163         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1164     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.ObjectManager", "InterfacesRemoved")) {
1165         const char *p;
1166         DBusMessageIter arg_i;
1167         DBusMessageIter element_i;
1168 
1169         if (!y->objects_listed)
1170             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1171 
1172         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
1173             pa_log_error("Invalid signature found in InterfacesRemoved");
1174             goto fail;
1175         }
1176 
1177         dbus_message_iter_get_basic(&arg_i, &p);
1178 
1179         pa_assert_se(dbus_message_iter_next(&arg_i));
1180         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
1181 
1182         dbus_message_iter_recurse(&arg_i, &element_i);
1183 
1184         while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
1185             const char *iface;
1186 
1187             dbus_message_iter_get_basic(&element_i, &iface);
1188 
1189             if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
1190                 device_remove(y, p);
1191             else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
1192                 adapter_remove(y, p);
1193 
1194             dbus_message_iter_next(&element_i);
1195         }
1196 
1197         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1198 
1199     } else if (dbus_message_is_signal(m, "org.freedesktop.DBus.Properties", "PropertiesChanged")) {
1200         DBusMessageIter arg_i;
1201         const char *iface;
1202 
1203         if (!y->objects_listed)
1204             return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
1205 
1206         if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
1207             pa_log_error("Invalid signature found in PropertiesChanged");
1208             goto fail;
1209         }
1210 
1211         dbus_message_iter_get_basic(&arg_i, &iface);
1212 
1213         pa_assert_se(dbus_message_iter_next(&arg_i));
1214         pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
1215 
1216         if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
1217             pa_bluetooth_adapter *a;
1218 
1219             pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
1220 
1221             if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
1222                 pa_log_warn("Properties changed in unknown adapter");
1223                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1224             }
1225 
1226             parse_adapter_properties(a, &arg_i, true);
1227 
1228         } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
1229             pa_bluetooth_device *d;
1230 
1231             pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
1232 
1233             if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
1234                 pa_log_warn("Properties changed in unknown device");
1235                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1236             }
1237 
1238             if (!d->properties_received)
1239                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1240 
1241             parse_device_properties(d, &arg_i);
1242         } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
1243             pa_bluetooth_transport *t;
1244 
1245             pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
1246 
1247             if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
1248                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1249 
1250             parse_transport_properties(t, &arg_i);
1251         }
1252 
1253         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1254     }
1255 
1256 fail:
1257     dbus_error_free(&err);
1258 
1259     return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1260 }
1261 
pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile)1262 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
1263     switch(profile) {
1264         case PA_BLUETOOTH_PROFILE_A2DP_SINK:
1265             return "a2dp_sink";
1266         case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
1267             return "a2dp_source";
1268         case PA_BLUETOOTH_PROFILE_HEADSET_HEAD_UNIT:
1269             return "headset_head_unit";
1270         case PA_BLUETOOTH_PROFILE_HEADSET_AUDIO_GATEWAY:
1271             return "headset_audio_gateway";
1272         case PA_BLUETOOTH_PROFILE_OFF:
1273             return "off";
1274     }
1275 
1276     return NULL;
1277 }
1278 
a2dp_endpoint_to_a2dp_codec(const char * endpoint)1279 static const pa_a2dp_codec *a2dp_endpoint_to_a2dp_codec(const char *endpoint) {
1280     const char *codec_name;
1281 
1282     if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
1283         codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
1284     else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
1285         codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
1286     else
1287         return NULL;
1288 
1289     return pa_bluetooth_get_a2dp_codec(codec_name);
1290 }
1291 
endpoint_set_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)1292 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1293     pa_bluetooth_discovery *y = userdata;
1294     pa_bluetooth_device *d;
1295     pa_bluetooth_transport *t;
1296     const pa_a2dp_codec *a2dp_codec = NULL;
1297     const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
1298     const uint8_t *config = NULL;
1299     int size = 0;
1300     pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
1301     DBusMessageIter args, props;
1302     DBusMessage *r;
1303 
1304     if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
1305         pa_log_error("Invalid signature for method SetConfiguration()");
1306         goto fail2;
1307     }
1308 
1309     dbus_message_iter_get_basic(&args, &path);
1310 
1311     if (pa_hashmap_get(y->transports, path)) {
1312         pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
1313         goto fail2;
1314     }
1315 
1316     pa_assert_se(dbus_message_iter_next(&args));
1317 
1318     dbus_message_iter_recurse(&args, &props);
1319     if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
1320         goto fail;
1321 
1322     endpoint_path = dbus_message_get_path(m);
1323 
1324     /* Read transport properties */
1325     while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
1326         const char *key;
1327         DBusMessageIter value, entry;
1328         int var;
1329 
1330         dbus_message_iter_recurse(&props, &entry);
1331         dbus_message_iter_get_basic(&entry, &key);
1332 
1333         dbus_message_iter_next(&entry);
1334         dbus_message_iter_recurse(&entry, &value);
1335 
1336         var = dbus_message_iter_get_arg_type(&value);
1337 
1338         if (pa_streq(key, "UUID")) {
1339             if (var != DBUS_TYPE_STRING) {
1340                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1341                 goto fail;
1342             }
1343 
1344             dbus_message_iter_get_basic(&value, &uuid);
1345 
1346             if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
1347                 p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
1348             else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
1349                 p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
1350 
1351             if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
1352                 (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
1353                 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
1354                 goto fail;
1355             }
1356         } else if (pa_streq(key, "Device")) {
1357             if (var != DBUS_TYPE_OBJECT_PATH) {
1358                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1359                 goto fail;
1360             }
1361 
1362             dbus_message_iter_get_basic(&value, &dev_path);
1363         } else if (pa_streq(key, "Configuration")) {
1364             DBusMessageIter array;
1365 
1366             if (var != DBUS_TYPE_ARRAY) {
1367                 pa_log_error("Property %s of wrong type %c", key, (char)var);
1368                 goto fail;
1369             }
1370 
1371             dbus_message_iter_recurse(&value, &array);
1372             var = dbus_message_iter_get_arg_type(&array);
1373             if (var != DBUS_TYPE_BYTE) {
1374                 pa_log_error("%s is an array of wrong type %c", key, (char)var);
1375                 goto fail;
1376             }
1377 
1378             dbus_message_iter_get_fixed_array(&array, &config, &size);
1379 
1380             a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
1381             pa_assert(a2dp_codec);
1382 
1383             if (!a2dp_codec->is_configuration_valid(config, size))
1384                 goto fail;
1385         }
1386 
1387         dbus_message_iter_next(&props);
1388     }
1389 
1390     if (!a2dp_codec)
1391         goto fail2;
1392 
1393     if ((d = pa_hashmap_get(y->devices, dev_path))) {
1394         if (!d->valid) {
1395             pa_log_error("Information about device %s is invalid", dev_path);
1396             goto fail2;
1397         }
1398     } else {
1399         /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
1400         pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
1401         d = device_create(y, dev_path);
1402     }
1403 
1404     if (d->transports[p] != NULL) {
1405         pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
1406         goto fail2;
1407     }
1408 
1409     sender = dbus_message_get_sender(m);
1410 
1411     pa_assert_se(r = dbus_message_new_method_return(m));
1412     pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1413     dbus_message_unref(r);
1414 
1415     t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
1416     t->a2dp_codec = a2dp_codec;
1417     t->acquire = bluez5_transport_acquire_cb;
1418     t->release = bluez5_transport_release_cb;
1419     pa_bluetooth_transport_put(t);
1420 
1421     pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
1422 
1423     return NULL;
1424 
1425 fail:
1426     pa_log_error("Endpoint SetConfiguration(): invalid arguments");
1427 
1428 fail2:
1429     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to set configuration"));
1430     return r;
1431 }
1432 
endpoint_select_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)1433 static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1434     pa_bluetooth_discovery *y = userdata;
1435     const char *endpoint_path;
1436     uint8_t *cap;
1437     int size;
1438     const pa_a2dp_codec *a2dp_codec;
1439     uint8_t config[MAX_A2DP_CAPS_SIZE];
1440     uint8_t *config_ptr = config;
1441     size_t config_size;
1442     DBusMessage *r;
1443     DBusError err;
1444 
1445     endpoint_path = dbus_message_get_path(m);
1446 
1447     dbus_error_init(&err);
1448 
1449     if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
1450         pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
1451         dbus_error_free(&err);
1452         goto fail;
1453     }
1454 
1455     a2dp_codec = a2dp_endpoint_to_a2dp_codec(endpoint_path);
1456     pa_assert(a2dp_codec);
1457 
1458     config_size = a2dp_codec->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
1459     if (config_size == 0)
1460         goto fail;
1461 
1462     pa_assert_se(r = dbus_message_new_method_return(m));
1463     pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
1464 
1465     return r;
1466 
1467 fail:
1468     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to select configuration"));
1469     return r;
1470 }
1471 
endpoint_clear_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)1472 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
1473     pa_bluetooth_discovery *y = userdata;
1474     pa_bluetooth_transport *t;
1475     DBusMessage *r;
1476     DBusError err;
1477     const char *path;
1478 
1479     dbus_error_init(&err);
1480 
1481     if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
1482         pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
1483         dbus_error_free(&err);
1484         goto fail;
1485     }
1486 
1487     if ((t = pa_hashmap_get(y->transports, path))) {
1488         pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
1489         pa_bluetooth_transport_free(t);
1490     }
1491 
1492     pa_assert_se(r = dbus_message_new_method_return(m));
1493 
1494     return r;
1495 
1496 fail:
1497     pa_assert_se(r = dbus_message_new_error(m, "org.bluez.Error.InvalidArguments", "Unable to clear configuration"));
1498     return r;
1499 }
1500 
endpoint_release(DBusConnection * conn,DBusMessage * m,void * userdata)1501 static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
1502     DBusMessage *r = NULL;
1503 
1504     /* From doc/media-api.txt in bluez:
1505      *
1506      *    This method gets called when the service daemon
1507      *    unregisters the endpoint. An endpoint can use it to do
1508      *    cleanup tasks. There is no need to unregister the
1509      *    endpoint, because when this method gets called it has
1510      *    already been unregistered.
1511      *
1512      * We don't have any cleanup to do. */
1513 
1514     /* Reply only if requested. Generally bluetoothd doesn't request a reply
1515      * to the Release() call. Sending replies when not requested on the system
1516      * bus tends to cause errors in syslog from dbus-daemon, because it
1517      * doesn't let unexpected replies through, so it's important to have this
1518      * check here. */
1519     if (!dbus_message_get_no_reply(m))
1520         pa_assert_se(r = dbus_message_new_method_return(m));
1521 
1522     return r;
1523 }
1524 
endpoint_handler(DBusConnection * c,DBusMessage * m,void * userdata)1525 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1526     struct pa_bluetooth_discovery *y = userdata;
1527     DBusMessage *r = NULL;
1528     const char *path, *interface, *member;
1529 
1530     pa_assert(y);
1531 
1532     path = dbus_message_get_path(m);
1533     interface = dbus_message_get_interface(m);
1534     member = dbus_message_get_member(m);
1535 
1536     pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
1537 
1538     if (!a2dp_endpoint_to_a2dp_codec(path))
1539         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1540 
1541     if (dbus_message_is_method_call(m, "org.freedesktop.DBus.Introspectable", "Introspect")) {
1542         const char *xml = ENDPOINT_INTROSPECT_XML;
1543 
1544         pa_assert_se(r = dbus_message_new_method_return(m));
1545         pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
1546 
1547     } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
1548         r = endpoint_set_configuration(c, m, userdata);
1549     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
1550         r = endpoint_select_configuration(c, m, userdata);
1551     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
1552         r = endpoint_clear_configuration(c, m, userdata);
1553     else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
1554         r = endpoint_release(c, m, userdata);
1555     else
1556         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1557 
1558     if (r) {
1559         pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
1560         dbus_message_unref(r);
1561     }
1562 
1563     return DBUS_HANDLER_RESULT_HANDLED;
1564 }
1565 
endpoint_init(pa_bluetooth_discovery * y,const char * endpoint)1566 static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
1567     static const DBusObjectPathVTable vtable_endpoint = {
1568         .message_function = endpoint_handler,
1569     };
1570 
1571     pa_assert(y);
1572     pa_assert(endpoint);
1573 
1574     pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
1575                                                       &vtable_endpoint, y));
1576 }
1577 
endpoint_done(pa_bluetooth_discovery * y,const char * endpoint)1578 static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
1579     pa_assert(y);
1580     pa_assert(endpoint);
1581 
1582     dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
1583 }
1584 
pa_bluetooth_discovery_get(pa_core * c,int headset_backend)1585 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend) {
1586     pa_bluetooth_discovery *y;
1587     DBusError err;
1588     DBusConnection *conn;
1589     unsigned i, count;
1590     const pa_a2dp_codec *a2dp_codec;
1591     char *endpoint;
1592 
1593     y = pa_xnew0(pa_bluetooth_discovery, 1);
1594     PA_REFCNT_INIT(y);
1595     y->core = c;
1596     y->headset_backend = headset_backend;
1597     y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1598                                       (pa_free_cb_t) adapter_free);
1599     y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
1600                                      (pa_free_cb_t) device_free);
1601     y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
1602     PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
1603 
1604     for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
1605         pa_hook_init(&y->hooks[i], y);
1606 
1607     pa_shared_set(c, "bluetooth-discovery", y);
1608 
1609     dbus_error_init(&err);
1610 
1611     if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
1612         pa_log_error("Failed to get D-Bus connection: %s", err.message);
1613         goto fail;
1614     }
1615 
1616     conn = pa_dbus_connection_get(y->connection);
1617 
1618     /* dynamic detection of bluetooth audio devices */
1619     if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
1620         pa_log_error("Failed to add filter function");
1621         goto fail;
1622     }
1623     y->filter_added = true;
1624 
1625     if (pa_dbus_add_matches(conn, &err,
1626             "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
1627             ",arg0='" BLUEZ_SERVICE "'",
1628             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',member='InterfacesAdded'",
1629             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
1630             "member='InterfacesRemoved'",
1631             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
1632             ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
1633             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
1634             ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
1635             "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',member='PropertiesChanged'"
1636             ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
1637             NULL) < 0) {
1638         pa_log_error("Failed to add D-Bus matches: %s", err.message);
1639         goto fail;
1640     }
1641     y->matches_added = true;
1642 
1643     count = pa_bluetooth_a2dp_codec_count();
1644     for (i = 0; i < count; i++) {
1645         a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
1646 
1647         endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
1648         endpoint_init(y, endpoint);
1649         pa_xfree(endpoint);
1650 
1651         endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
1652         endpoint_init(y, endpoint);
1653         pa_xfree(endpoint);
1654     }
1655 
1656     get_managed_objects(y);
1657 
1658     return y;
1659 
1660 fail:
1661     pa_bluetooth_discovery_unref(y);
1662     dbus_error_free(&err);
1663 
1664     return NULL;
1665 }
1666 
pa_bluetooth_discovery_ref(pa_bluetooth_discovery * y)1667 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
1668     pa_assert(y);
1669     pa_assert(PA_REFCNT_VALUE(y) > 0);
1670 
1671     PA_REFCNT_INC(y);
1672 
1673     return y;
1674 }
1675 
pa_bluetooth_discovery_unref(pa_bluetooth_discovery * y)1676 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
1677     unsigned i, count;
1678     const pa_a2dp_codec *a2dp_codec;
1679     char *endpoint;
1680 
1681     pa_assert(y);
1682     pa_assert(PA_REFCNT_VALUE(y) > 0);
1683 
1684     if (PA_REFCNT_DEC(y) > 0)
1685         return;
1686 
1687     pa_dbus_free_pending_list(&y->pending);
1688 
1689     if (y->ofono_backend)
1690         pa_bluetooth_ofono_backend_free(y->ofono_backend);
1691     if (y->native_backend)
1692         pa_bluetooth_native_backend_free(y->native_backend);
1693 
1694     if (y->adapters)
1695         pa_hashmap_free(y->adapters);
1696 
1697     if (y->devices)
1698         pa_hashmap_free(y->devices);
1699 
1700     if (y->transports) {
1701         pa_assert(pa_hashmap_isempty(y->transports));
1702         pa_hashmap_free(y->transports);
1703     }
1704 
1705     if (y->connection) {
1706 
1707         if (y->matches_added)
1708             pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
1709                 "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='NameOwnerChanged',"
1710                 "arg0='" BLUEZ_SERVICE "'",
1711                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
1712                 "member='InterfacesAdded'",
1713                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.ObjectManager',"
1714                 "member='InterfacesRemoved'",
1715                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
1716                 "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
1717                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
1718                 "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
1719                 "type='signal',sender='" BLUEZ_SERVICE "',interface='org.freedesktop.DBus.Properties',"
1720                 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
1721                 NULL);
1722 
1723         if (y->filter_added)
1724             dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
1725 
1726         count = pa_bluetooth_a2dp_codec_count();
1727         for (i = 0; i < count; i++) {
1728             a2dp_codec = pa_bluetooth_a2dp_codec_iter(i);
1729 
1730             endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, a2dp_codec->name);
1731             endpoint_done(y, endpoint);
1732             pa_xfree(endpoint);
1733 
1734             endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, a2dp_codec->name);
1735             endpoint_done(y, endpoint);
1736             pa_xfree(endpoint);
1737         }
1738 
1739         pa_dbus_connection_unref(y->connection);
1740     }
1741 
1742     pa_shared_remove(y->core, "bluetooth-discovery");
1743     pa_xfree(y);
1744 }
1745