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
105static 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
118static 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
131struct 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
153static 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
170static 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
195pa_bluetooth_profile_status_t profile_status_get(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
196    return y->profiles_status[profile];
197}
198
199void 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
203pa_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
228void 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
244static 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
257bool 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
322static 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
329static 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
344static 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
352static 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
385static 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
392struct 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
400static 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
440bool 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
509void 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
570static 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
620static 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
626static 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
632static 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
676void 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
684void 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
692void 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
705static 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
747finish:
748    dbus_message_unref(r);
749    return ret;
750}
751
752static 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
783static 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
829finish:
830    dbus_message_unref(r);
831
832    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
833    pa_dbus_pending_free(p);
834}
835
836static 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
856void 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
868static 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
907bool 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 */
923static 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 */
929static 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
934static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object);
935static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *object, bool only_percentage);
936
937void 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 */
981void 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
1016static 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
1030static 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
1078static 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
1096static 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
1110static 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
1132static 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
1149static 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
1168pa_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
1181bool 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
1189bool 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
1197bool 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
1205pa_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
1221static 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
1253static 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
1264static 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
1279static 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
1301static 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
1312static 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
1336static 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
1361static 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
1417static 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
1452static 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
1485static 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
1501static 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
1520static 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
1531static 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
1639static 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
1661static 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
1729static 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
1752finish:
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
1761static 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
1793static 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
1829finish:
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
1850static 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
1878static 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
2027static 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
2110void 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
2138static 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
2181finish:
2182    dbus_message_unref(r);
2183
2184    PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
2185    pa_dbus_pending_free(p);
2186}
2187
2188static 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
2198pa_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
2205static 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
2362fail:
2363    dbus_error_free(&err);
2364
2365    return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2366}
2367
2368const 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 */
2398bool 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
2416bool 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
2420static 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
2433static 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
2574fail:
2575    pa_log_error("Endpoint SetConfiguration(): invalid arguments");
2576
2577fail2:
2578    pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to set configuration"));
2579    return r;
2580}
2581
2582static 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
2616fail:
2617    pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to select configuration"));
2618    return r;
2619}
2620
2621static 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
2646fail:
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
2652static 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
2676static 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
2717static 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
2729static 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
2736static 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
2775static 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
2855static 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
2865static 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
2871pa_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
2957fail:
2958    pa_bluetooth_discovery_unref(y);
2959    dbus_error_free(&err);
2960
2961    return NULL;
2962}
2963
2964pa_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
2973void 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