1/***
2  This file is part of PulseAudio.
3
4  Copyright 2014 Wim Taymans <wim.taymans at gmail.com>
5
6  PulseAudio is free software; you can redistribute it and/or modify
7  it under the terms of the GNU Lesser General Public License as
8  published by the Free Software Foundation; either version 2.1 of the
9  License, or (at your option) any later version.
10
11  PulseAudio is distributed in the hope that it will be useful, but
12  WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  General Public License for more details.
15
16  You should have received a copy of the GNU Lesser General Public
17  License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
18***/
19
20#ifdef HAVE_CONFIG_H
21#include <config.h>
22#endif
23
24#include <pulse/util.h>
25
26#include <pulsecore/shared.h>
27#include <pulsecore/core-error.h>
28#include <pulsecore/core-util.h>
29#include <pulsecore/dbus-shared.h>
30#include <pulsecore/log.h>
31
32#include <errno.h>
33#include <sys/types.h>
34#include <sys/socket.h>
35
36#include <bluetooth/bluetooth.h>
37#include <bluetooth/sco.h>
38
39#include "bluez5-util.h"
40#include "bt-codec-msbc.h"
41
42struct pa_bluetooth_backend {
43  pa_core *core;
44  pa_dbus_connection *connection;
45  pa_bluetooth_discovery *discovery;
46  pa_hook_slot *adapter_uuids_changed_slot;
47  bool enable_shared_profiles;
48  bool enable_hsp_hs;
49  bool enable_hfp_hf;
50
51  PA_LLIST_HEAD(pa_dbus_pending, pending);
52};
53
54struct transport_data {
55    int rfcomm_fd;
56    pa_io_event *rfcomm_io;
57    int sco_fd;
58    pa_io_event *sco_io;
59    pa_mainloop_api *mainloop;
60};
61
62struct hfp_config {
63    uint32_t capabilities;
64    int state;
65    bool support_codec_negotiation;
66    bool support_msbc;
67    bool supports_indicators;
68    int selected_codec;
69};
70
71/*
72 * the separate hansfree headset (HF) and Audio Gateway (AG) features
73 */
74enum hfp_hf_features {
75    HFP_HF_EC_NR = 0,
76    HFP_HF_CALL_WAITING = 1,
77    HFP_HF_CLI = 2,
78    HFP_HF_VR = 3,
79    HFP_HF_RVOL = 4,
80    HFP_HF_ESTATUS = 5,
81    HFP_HF_ECALL = 6,
82    HFP_HF_CODECS = 7,
83    HFP_HF_INDICATORS = 8,
84};
85
86enum hfp_ag_features {
87    HFP_AG_THREE_WAY = 0,
88    HFP_AG_EC_NR = 1,
89    HFP_AG_VR = 2,
90    HFP_AG_RING = 3,
91    HFP_AG_NUM_TAG = 4,
92    HFP_AG_REJECT = 5,
93    HFP_AG_ESTATUS = 6,
94    HFP_AG_ECALL = 7,
95    HFP_AG_EERR = 8,
96    HFP_AG_CODECS = 9,
97    HFP_AG_INDICATORS = 10,
98};
99
100/* gateway features we support, which is as little as we can get away with */
101static uint32_t hfp_features =
102    /* HFP 1.6 requires this */
103    (1 << HFP_AG_ESTATUS ) | (1 << HFP_AG_CODECS) | (1 << HFP_AG_INDICATORS);
104
105#define HSP_AG_PROFILE "/Profile/HSPAGProfile"
106#define HFP_AG_PROFILE "/Profile/HFPAGProfile"
107#define HSP_HS_PROFILE "/Profile/HSPHSProfile"
108
109/* RFCOMM channel for HSP headset role
110 * The choice seems to be a bit arbitrary -- it looks like at least channels 2, 4 and 5 also work*/
111#define HSP_HS_DEFAULT_CHANNEL  3
112
113/* Total number of trying to reconnect */
114#define SCO_RECONNECTION_COUNT 3
115
116#define PROFILE_INTROSPECT_XML                                          \
117    DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
118    "<node>"                                                            \
119    " <interface name=\"" BLUEZ_PROFILE_INTERFACE "\">"                 \
120    "  <method name=\"Release\">"                                       \
121    "  </method>"                                                       \
122    "  <method name=\"RequestDisconnection\">"                          \
123    "   <arg name=\"device\" direction=\"in\" type=\"o\"/>"             \
124    "  </method>"                                                       \
125    "  <method name=\"NewConnection\">"                                 \
126    "   <arg name=\"device\" direction=\"in\" type=\"o\"/>"             \
127    "   <arg name=\"fd\" direction=\"in\" type=\"h\"/>"                 \
128    "   <arg name=\"opts\" direction=\"in\" type=\"a{sv}\"/>"           \
129    "  </method>"                                                       \
130    " </interface>"                                                     \
131    " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">"           \
132    "  <method name=\"Introspect\">"                                    \
133    "   <arg name=\"data\" type=\"s\" direction=\"out\"/>"              \
134    "  </method>"                                                       \
135    " </interface>"                                                     \
136    "</node>"
137
138static pa_volume_t hsp_gain_to_volume(uint16_t gain) {
139    pa_volume_t volume = (pa_volume_t) ((
140        gain * PA_VOLUME_NORM
141        /* Round to closest by adding half the denominator */
142        + HSP_MAX_GAIN / 2
143    ) / HSP_MAX_GAIN);
144
145    if (volume > PA_VOLUME_NORM)
146        volume = PA_VOLUME_NORM;
147
148    return volume;
149}
150
151static uint16_t volume_to_hsp_gain(pa_volume_t volume) {
152    uint16_t gain = volume * HSP_MAX_GAIN / PA_VOLUME_NORM;
153
154    if (gain > HSP_MAX_GAIN)
155        gain = HSP_MAX_GAIN;
156
157    return gain;
158}
159
160static bool is_peer_audio_gateway(pa_bluetooth_profile_t peer_profile) {
161    switch(peer_profile) {
162        case PA_BLUETOOTH_PROFILE_HFP_HF:
163        case PA_BLUETOOTH_PROFILE_HSP_HS:
164            return false;
165        case PA_BLUETOOTH_PROFILE_HFP_AG:
166        case PA_BLUETOOTH_PROFILE_HSP_AG:
167            return true;
168        default:
169            pa_assert_not_reached();
170    }
171}
172
173static bool is_pulseaudio_audio_gateway(pa_bluetooth_profile_t peer_profile) {
174    return !is_peer_audio_gateway(peer_profile);
175}
176
177static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_backend *backend, DBusMessage *m,
178        DBusPendingCallNotifyFunction func, void *call_data) {
179
180    pa_dbus_pending *p;
181    DBusPendingCall *call;
182
183    pa_assert(backend);
184    pa_assert(m);
185
186    pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(backend->connection), m, &call, -1));
187
188    p = pa_dbus_pending_new(pa_dbus_connection_get(backend->connection), m, call, backend, call_data);
189    PA_LLIST_PREPEND(pa_dbus_pending, backend->pending, p);
190    dbus_pending_call_set_notify(call, func, p, NULL);
191
192    return p;
193}
194
195static void rfcomm_fmt_write(int fd, const char* fmt_line, const char *fmt_command, va_list ap)
196{
197    size_t len;
198    char buf[512];
199    char command[512];
200
201    pa_vsnprintf(command, sizeof(command), fmt_command, ap);
202
203    pa_log_debug("RFCOMM >> %s", command);
204
205    len = pa_snprintf(buf, sizeof(buf), fmt_line, command);
206
207    /* we ignore any errors, it's not critical and real errors should
208     * be caught with the HANGUP and ERROR events handled above */
209
210    if ((size_t)write(fd, buf, len) != len)
211        pa_log_error("RFCOMM write error: %s", pa_cstrerror(errno));
212}
213
214/* The format of COMMAND line sent from HS to AG is COMMAND<cr> */
215static void rfcomm_write_command(int fd, const char *fmt, ...)
216{
217    va_list ap;
218
219    va_start(ap, fmt);
220    rfcomm_fmt_write(fd, "%s\r", fmt, ap);
221    va_end(ap);
222}
223
224/* The format of RESPONSE line sent from AG to HS is <cr><lf>RESPONSE<cr><lf> */
225static void rfcomm_write_response(int fd, const char *fmt, ...)
226{
227    va_list ap;
228
229    va_start(ap, fmt);
230    rfcomm_fmt_write(fd, "\r\n%s\r\n", fmt, ap);
231    va_end(ap);
232}
233
234static int sco_setsockopt_enable_bt_voice(pa_bluetooth_transport *t, int fd) {
235    /* the mSBC codec requires a special transparent eSCO connection */
236    struct bt_voice voice;
237
238    memset(&voice, 0, sizeof(voice));
239    voice.setting = BT_VOICE_TRANSPARENT;
240    if (setsockopt(fd, SOL_BLUETOOTH, BT_VOICE, &voice, sizeof(voice)) < 0) {
241        pa_log_error("sockopt(): %s", pa_cstrerror(errno));
242        return -1;
243    }
244    pa_log_info("Enabled BT_VOICE_TRANSPARENT connection for mSBC");
245    return 0;
246}
247
248static int sco_do_connect(pa_bluetooth_transport *t) {
249    pa_bluetooth_device *d = t->device;
250    struct sockaddr_sco addr;
251    socklen_t len;
252    int err, i;
253    int sock;
254    bdaddr_t src;
255    bdaddr_t dst;
256    const char *src_addr, *dst_addr;
257
258    src_addr = d->adapter->address;
259    dst_addr = d->address;
260
261    /* don't use ba2str to avoid -lbluetooth */
262    for (i = 5; i >= 0; i--, src_addr += 3)
263        src.b[i] = strtol(src_addr, NULL, 16);
264    for (i = 5; i >= 0; i--, dst_addr += 3)
265        dst.b[i] = strtol(dst_addr, NULL, 16);
266
267    sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_SCO);
268    if (sock < 0) {
269        pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno));
270        return -1;
271    }
272
273    len = sizeof(addr);
274    memset(&addr, 0, len);
275    addr.sco_family = AF_BLUETOOTH;
276    bacpy(&addr.sco_bdaddr, &src);
277
278    if (bind(sock, (struct sockaddr *) &addr, len) < 0) {
279        pa_log_error("bind(): %s", pa_cstrerror(errno));
280        goto fail_close;
281    }
282
283    if (t->setsockopt && t->setsockopt(t, sock) < 0)
284        goto fail_close;
285
286    memset(&addr, 0, len);
287    addr.sco_family = AF_BLUETOOTH;
288    bacpy(&addr.sco_bdaddr, &dst);
289
290    pa_log_info("doing connect");
291    err = connect(sock, (struct sockaddr *) &addr, len);
292    if (err < 0 && !(errno == EAGAIN || errno == EINPROGRESS)) {
293        pa_log_error("connect(): %s", pa_cstrerror(errno));
294        goto fail_close;
295    }
296    return sock;
297
298fail_close:
299    close(sock);
300    return -1;
301}
302
303static int sco_do_accept(pa_bluetooth_transport *t) {
304    struct transport_data *trd = t->userdata;
305    struct sockaddr_sco addr;
306    socklen_t optlen;
307    int sock;
308
309    memset(&addr, 0, sizeof(addr));
310    optlen = sizeof(addr);
311
312    pa_log_info ("doing accept");
313    sock = accept(trd->sco_fd, (struct sockaddr *) &addr, &optlen);
314    if (sock < 0) {
315        if (errno != EAGAIN)
316            pa_log_error("accept(): %s", pa_cstrerror(errno));
317        goto fail;
318    }
319    return sock;
320
321fail:
322    return -1;
323}
324
325static int sco_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
326    int sock;
327    socklen_t len;
328    int i;
329
330    if (optional)
331        sock = sco_do_accept(t);
332    else {
333        for (i = 0; i < SCO_RECONNECTION_COUNT; i++) {
334            sock = sco_do_connect(t);
335
336            if (sock < 0) {
337                pa_log_debug("err is %s and reconnection count is %d", pa_cstrerror(errno), i);
338                pa_msleep(300);
339                continue;
340            } else
341                break;
342        }
343    }
344
345    if (sock < 0)
346        goto fail;
347
348    /* The correct block size should take into account the SCO MTU from
349     * the Bluetooth adapter and (for adapters in the USB bus) the MxPS
350     * value from the Isoc USB endpoint in use by btusb and should be
351     * made available to userspace by the Bluetooth kernel subsystem.
352     *
353     * Set initial MTU to max known payload length of HCI packet
354     * in USB Alternate Setting 5 (144 bytes)
355     * See also pa_bluetooth_transport::last_read_size handling
356     * and comment about MTU size in bt_prepare_encoder_buffer()
357     */
358    if (imtu) *imtu = 144;
359    if (omtu) *omtu = 144;
360
361    if (t->device->autodetect_mtu) {
362        struct sco_options sco_opt;
363
364        len = sizeof(sco_opt);
365        memset(&sco_opt, 0, len);
366
367        if (getsockopt(sock, SOL_SCO, SCO_OPTIONS, &sco_opt, &len) < 0)
368            pa_log_warn("getsockopt(SCO_OPTIONS) failed, loading defaults");
369        else {
370            pa_log_debug("autodetected imtu = omtu = %u", sco_opt.mtu);
371            if (imtu) *imtu = sco_opt.mtu;
372            if (omtu) *omtu = sco_opt.mtu;
373        }
374    }
375
376    return sock;
377
378fail:
379    return -1;
380}
381
382static void sco_release_cb(pa_bluetooth_transport *t) {
383    pa_log_info("Transport %s released", t->path);
384    /* device will close the SCO socket for us */
385}
386
387static ssize_t sco_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) {
388    ssize_t l = 0;
389    size_t written = 0;
390    size_t write_size;
391
392    pa_assert(t);
393
394    /* since SCO setup is symmetric, fix write MTU to be size of last read packet */
395    if (t->last_read_size)
396        write_mtu = PA_MIN(t->last_read_size, write_mtu);
397
398    /* if encoder buffer has less data than required to make complete packet */
399    if (size < write_mtu)
400        return 0;
401
402    /* write out MTU sized chunks only */
403    while (written < size) {
404        write_size = PA_MIN(size - written, write_mtu);
405        if (write_size < write_mtu)
406            break;
407        l = pa_write(fd, buffer + written, write_size, &t->stream_write_type);
408        if (l < 0)
409            break;
410        written += l;
411    }
412
413    if (l < 0) {
414        if (errno == EAGAIN) {
415            /* Hmm, apparently the socket was not writable, give up for now */
416            pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss.");
417            /* Drain write buffer */
418            written = size;
419        } else if (errno == EINVAL && t->last_read_size == 0) {
420            /* Likely write_link_mtu is still wrong, retry after next successful read */
421            pa_log_debug("got write EINVAL, next successful read should fix MTU");
422            /* Drain write buffer */
423            written = size;
424        } else {
425            pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
426            /* Report error from write call */
427            return -1;
428        }
429    }
430
431    /* if too much data left discard it all */
432    if (size - written >= write_mtu) {
433        pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu",
434                    written, size, write_mtu);
435        /* Drain write buffer */
436        written = size;
437    }
438
439    return written;
440}
441
442static void sco_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
443    pa_bluetooth_transport *t = userdata;
444
445    pa_assert(io);
446    pa_assert(t);
447
448    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
449        pa_log_error("error listening SCO connection: %s", pa_cstrerror(errno));
450        goto fail;
451    }
452
453    if (t->state != PA_BLUETOOTH_TRANSPORT_STATE_PLAYING) {
454        pa_log_info("SCO incoming connection: changing state to PLAYING");
455        pa_bluetooth_transport_set_state (t, PA_BLUETOOTH_TRANSPORT_STATE_PLAYING);
456    }
457
458fail:
459    return;
460}
461
462static int sco_listen(pa_bluetooth_transport *t) {
463    struct transport_data *trd = t->userdata;
464    struct sockaddr_sco addr;
465    int sock, i;
466    bdaddr_t src;
467    const char *src_addr;
468
469    sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET | SOCK_NONBLOCK | SOCK_CLOEXEC, BTPROTO_SCO);
470    if (sock < 0) {
471        pa_log_error("socket(SEQPACKET, SCO) %s", pa_cstrerror(errno));
472        return -1;
473    }
474
475    src_addr = t->device->adapter->address;
476
477    /* don't use ba2str to avoid -lbluetooth */
478    for (i = 5; i >= 0; i--, src_addr += 3)
479        src.b[i] = strtol(src_addr, NULL, 16);
480
481    /* Bind to local address */
482    memset(&addr, 0, sizeof(addr));
483    addr.sco_family = AF_BLUETOOTH;
484    bacpy(&addr.sco_bdaddr, &src);
485
486    if (bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
487        pa_log_error("bind(): %s", pa_cstrerror(errno));
488        goto fail_close;
489    }
490
491    pa_log_info ("doing listen");
492    if (listen(sock, 1) < 0) {
493        pa_log_error("listen(): %s", pa_cstrerror(errno));
494        goto fail_close;
495    }
496
497    trd->sco_fd = sock;
498    trd->sco_io = trd->mainloop->io_new(trd->mainloop, sock, PA_IO_EVENT_INPUT,
499        sco_io_callback, t);
500
501    return sock;
502
503fail_close:
504    close(sock);
505    return -1;
506}
507
508static void register_profile_reply(DBusPendingCall *pending, void *userdata) {
509    DBusMessage *r;
510    pa_dbus_pending *p;
511    pa_bluetooth_backend *b;
512    pa_bluetooth_profile_t profile;
513
514    pa_assert(pending);
515    pa_assert_se(p = userdata);
516    pa_assert_se(b = p->context_data);
517    pa_assert_se(profile = (pa_bluetooth_profile_t)p->call_data);
518    pa_assert_se(r = dbus_pending_call_steal_reply(pending));
519
520    if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
521        pa_log_info("Couldn't register profile %s because it is disabled in BlueZ", pa_bluetooth_profile_to_string(profile));
522        profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);
523        goto finish;
524    }
525
526    if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
527        pa_log_error(BLUEZ_PROFILE_MANAGER_INTERFACE ".RegisterProfile() failed: %s: %s", dbus_message_get_error_name(r),
528                     pa_dbus_get_error_message(r));
529        profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);
530        goto finish;
531    }
532
533    profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_REGISTERED);
534
535finish:
536    dbus_message_unref(r);
537
538    PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p);
539    pa_dbus_pending_free(p);
540}
541
542static void register_profile(pa_bluetooth_backend *b, const char *object, const char *uuid, pa_bluetooth_profile_t profile) {
543    DBusMessage *m;
544    DBusMessageIter i, d;
545    dbus_bool_t autoconnect;
546    dbus_uint16_t version, chan;
547
548    pa_assert(profile_status_get(b->discovery, profile) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);
549
550    pa_log_debug("Registering Profile %s %s", pa_bluetooth_profile_to_string(profile), uuid);
551
552    pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/org/bluez", BLUEZ_PROFILE_MANAGER_INTERFACE, "RegisterProfile"));
553
554    dbus_message_iter_init_append(m, &i);
555    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object));
556    pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &uuid));
557    dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
558                                     DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
559                                     DBUS_TYPE_STRING_AS_STRING
560                                     DBUS_TYPE_VARIANT_AS_STRING
561                                     DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
562                                     &d);
563    if (pa_bluetooth_uuid_is_hsp_hs(uuid)) {
564        /* In the headset role, the connection will only be initiated from the remote side */
565        autoconnect = 0;
566        pa_dbus_append_basic_variant_dict_entry(&d, "AutoConnect", DBUS_TYPE_BOOLEAN, &autoconnect);
567        chan = HSP_HS_DEFAULT_CHANNEL;
568        pa_dbus_append_basic_variant_dict_entry(&d, "Channel", DBUS_TYPE_UINT16, &chan);
569        /* HSP version 1.2 */
570        version = 0x0102;
571        pa_dbus_append_basic_variant_dict_entry(&d, "Version", DBUS_TYPE_UINT16, &version);
572    }
573    dbus_message_iter_close_container(&i, &d);
574
575    profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_REGISTERING);
576    send_and_add_to_pending(b, m, register_profile_reply, (void *)profile);
577}
578
579static void transport_put(pa_bluetooth_transport *t)
580{
581    pa_bluetooth_transport_put(t);
582
583    pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
584}
585
586static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume);
587static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume);
588
589static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf)
590{
591    struct hfp_config *c = t->config;
592    int indicator, val;
593    char str[5];
594    const char *r;
595    size_t len;
596    const char *state;
597
598    /* first-time initialize selected codec to CVSD */
599    if (c->selected_codec == 0)
600        c->selected_codec = 1;
601
602    /* stateful negotiation */
603    if (c->state == 0 && sscanf(buf, "AT+BRSF=%d", &val) == 1) {
604        c->capabilities = val;
605        pa_log_info("HFP capabilities returns 0x%x", val);
606        rfcomm_write_response(fd, "+BRSF: %d", hfp_features);
607        c->supports_indicators = !!(1 << HFP_HF_INDICATORS);
608        c->state = 1;
609
610        return true;
611    } else if (sscanf(buf, "AT+BAC=%3s", str) == 1) {
612        c->support_msbc = false;
613
614        state = NULL;
615
616        /* check if codec id 2 (mSBC) is in the list of supported codecs */
617        while ((r = pa_split_in_place(str, ",", &len, &state))) {
618            if (len == 1 && r[0] == '2') {
619                c->support_msbc = true;
620                break;
621            }
622        }
623
624        c->support_codec_negotiation = true;
625
626        if (c->state == 1) {
627            /* initial list of codecs supported by HF */
628        } else {
629            /* HF sent updated list of codecs */
630        }
631
632        /* no state change */
633
634        return true;
635    } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) {
636        /* we declare minimal no indicators */
637        rfcomm_write_response(fd, "+CIND: "
638                     /* many indicators can be supported, only call and
639                      * callheld are mandatory, so that's all we reply */
640                     "(\"service\",(0-1)),"
641                     "(\"call\",(0-1)),"
642                     "(\"callsetup\",(0-3)),"
643                     "(\"callheld\",(0-2))");
644        c->state = 2;
645
646        return true;
647    } else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) {
648        rfcomm_write_response(fd, "+CIND: 0,0,0,0");
649        c->state = 3;
650
651        return true;
652    } else if ((c->state == 2 || c->state == 3) && pa_startswith(buf, "AT+CMER=")) {
653        rfcomm_write_response(fd, "OK");
654
655        if (c->support_codec_negotiation) {
656            if (c->support_msbc && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) {
657                rfcomm_write_response(fd, "+BCS:2");
658                c->state = 4;
659            } else {
660                rfcomm_write_response(fd, "+BCS:1");
661                c->state = 4;
662            }
663        } else {
664            c->state = 5;
665            pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL);
666            transport_put(t);
667        }
668
669        return false;
670    } else if (sscanf(buf, "AT+BCS=%d", &val)) {
671        if (val == 1) {
672            pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL);
673        } else if (val == 2 && pa_bluetooth_discovery_get_enable_msbc(t->device->discovery)) {
674            pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("mSBC"), sco_transport_write, sco_setsockopt_enable_bt_voice);
675        } else {
676            pa_assert_fp(val != 1 && val != 2);
677            rfcomm_write_response(fd, "ERROR");
678            return false;
679        }
680
681        c->selected_codec = val;
682
683        if (c->state == 4) {
684            c->state = 5;
685            pa_log_info("HFP negotiated codec %s", t->bt_codec->name);
686            transport_put(t);
687        }
688
689        return true;
690    } else if (c->supports_indicators && pa_startswith(buf, "AT+BIND=?")) {
691        // Support battery indication
692        rfcomm_write_response(fd, "+BIND: (2)");
693        return true;
694    } else if (c->supports_indicators && pa_startswith(buf, "AT+BIND?")) {
695        // Battery indication is enabled
696        rfcomm_write_response(fd, "+BIND: 2,1");
697        return true;
698    } else if (c->supports_indicators && pa_startswith(buf, "AT+BIND=")) {
699        // If this comma-separated list contains `2`, the HF is
700        // able to report values for the battery indicator.
701        return true;
702    } else if (c->supports_indicators && sscanf(buf, "AT+BIEV=%u,%u", &indicator, &val)) {
703        switch (indicator) {
704            case 2:
705                pa_log_notice("Battery Level: %d%%", val);
706                if (val < 0 || val > 100) {
707                    pa_log_error("Battery HF indicator %d out of [0, 100] range", val);
708                    rfcomm_write_response(fd, "ERROR");
709                    return false;
710                }
711                pa_bluetooth_device_report_battery_level(t->device, val, "HFP 1.7 HF indicator");
712                break;
713            default:
714                pa_log_error("Unknown HF indicator %u", indicator);
715                rfcomm_write_response(fd, "ERROR");
716                return false;
717        }
718        return true;
719    } if (c->state == 4) {
720        /* the ack for the codec setting may take a while. we need
721         * to reply OK to everything else until then */
722        return true;
723    }
724
725    /* if we get here, negotiation should be complete */
726    if (c->state != 5) {
727        pa_log_error("HFP negotiation failed in state %d with inbound %s\n",
728                     c->state, buf);
729        rfcomm_write_response(fd, "ERROR");
730        return false;
731    }
732
733    /*
734     * once we're fully connected, just reply OK to everything
735     * it will just be the headset sending the occasional status
736     * update, but we process only the ones we care about
737     */
738    return true;
739}
740
741static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) {
742    pa_bluetooth_transport *t = userdata;
743
744    pa_assert(io);
745    pa_assert(t);
746
747    if (events & (PA_IO_EVENT_HANGUP|PA_IO_EVENT_ERROR)) {
748        pa_log_info("Lost RFCOMM connection.");
749        // TODO: Keep track of which profile is the current battery provider,
750        // only deregister if it is us currently providing these levels.
751        // (Also helpful to fill the 'Source' property)
752        // We might also move this to Profile1::RequestDisconnection
753        pa_bluetooth_device_deregister_battery(t->device);
754        goto fail;
755    }
756
757    if (events & PA_IO_EVENT_INPUT) {
758        char buf[512];
759        ssize_t len;
760        int gain, dummy;
761        bool do_reply = false;
762        int vendor, product, version, features;
763        int num;
764
765        len = pa_read(fd, buf, 511, NULL);
766        if (len < 0) {
767            pa_log_error("RFCOMM read error: %s", pa_cstrerror(errno));
768            goto fail;
769        }
770        buf[len] = 0;
771        pa_log_debug("RFCOMM << %s", buf);
772
773        /* There are only four HSP AT commands:
774         * AT+VGS=value: value between 0 and 15, sent by the HS to AG to set the speaker gain.
775         * +VGS=value is sent by AG to HS as a response to an AT+VGS command or when the gain
776         * is changed on the AG side.
777         * AT+VGM=value: value between 0 and 15, sent by the HS to AG to set the microphone gain.
778         * +VGM=value is sent by AG to HS as a response to an AT+VGM command or when the gain
779         * is changed on the AG side.
780         * AT+CKPD=200: Sent by HS when headset button is pressed.
781         * RING: Sent by AG to HS to notify of an incoming call. It can safely be ignored because
782         * it does not expect a reply. */
783        if (sscanf(buf, "AT+VGS=%d", &gain) == 1 || sscanf(buf, "\r\n+VGM%*[=:]%d\r\n", &gain) == 1) {
784            if (!t->set_sink_volume) {
785                pa_log_debug("HS/HF peer supports speaker gain control");
786                t->set_sink_volume = set_sink_volume;
787            }
788
789            t->sink_volume = hsp_gain_to_volume(gain);
790            pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED), t);
791            do_reply = true;
792
793        } else if (sscanf(buf, "AT+VGM=%d", &gain) == 1 || sscanf(buf, "\r\n+VGS%*[=:]%d\r\n", &gain) == 1) {
794            if (!t->set_source_volume) {
795                pa_log_debug("HS/HF peer supports microphone gain control");
796                t->set_source_volume = set_source_volume;
797            }
798
799            t->source_volume = hsp_gain_to_volume(gain);
800            pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED), t);
801            do_reply = true;
802        } else if (sscanf(buf, "AT+CKPD=%d", &dummy) == 1) {
803            do_reply = true;
804        } else if (sscanf(buf, "AT+XAPL=%04x-%04x-%04x,%d", &vendor, &product, &version, &features) == 4) {
805            if (features & 0x2)
806                /* claim, that we support battery status reports */
807                rfcomm_write_response(fd, "+XAPL=iPhone,6");
808            do_reply = true;
809        } else if (sscanf(buf, "AT+IPHONEACCEV=%d", &num) == 1) {
810            char *substr = buf, *keystr;
811            int key, val, i;
812
813            do_reply = true;
814
815            for (i = 0; i < num; ++i) {
816                keystr = strchr(substr, ',');
817                if (!keystr) {
818                    pa_log_warn("%s misses key for argument #%d", buf, i);
819                    do_reply = false;
820                    break;
821                }
822                keystr++;
823                substr = strchr(keystr, ',');
824                if (!substr) {
825                    pa_log_warn("%s misses value for argument #%d", buf, i);
826                    do_reply = false;
827                    break;
828                }
829                substr++;
830
831                key = atoi(keystr);
832                val = atoi(substr);
833
834                switch (key) {
835                    case 1:
836                        pa_log_notice("Battery Level: %d0%%", val + 1);
837                        pa_bluetooth_device_report_battery_level(t->device, (val + 1) * 10, "Apple accessory indication");
838                        break;
839                    case 2:
840                        pa_log_notice("Dock Status: %s", val ? "docked" : "undocked");
841                        break;
842                    default:
843                        pa_log_debug("Unexpected IPHONEACCEV key %#x", key);
844                        break;
845                }
846            }
847            if (!do_reply)
848                rfcomm_write_response(fd, "ERROR");
849        } else if (t->config) { /* t->config is only non-null for hfp profile */
850            do_reply = hfp_rfcomm_handle(fd, t, buf);
851        } else {
852            rfcomm_write_response(fd, "ERROR");
853            do_reply = false;
854        }
855
856        if (do_reply)
857            rfcomm_write_response(fd, "OK");
858    }
859
860    return;
861
862fail:
863    pa_bluetooth_transport_unlink(t);
864    pa_bluetooth_transport_free(t);
865}
866
867static void transport_destroy(pa_bluetooth_transport *t) {
868    struct transport_data *trd = t->userdata;
869
870    if (trd->sco_io) {
871        trd->mainloop->io_free(trd->sco_io);
872        shutdown(trd->sco_fd, SHUT_RDWR);
873        close (trd->sco_fd);
874    }
875
876    trd->mainloop->io_free(trd->rfcomm_io);
877    shutdown(trd->rfcomm_fd, SHUT_RDWR);
878    close (trd->rfcomm_fd);
879
880    pa_xfree(trd);
881}
882
883static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
884    struct transport_data *trd = t->userdata;
885    uint16_t gain = volume_to_hsp_gain(volume);
886
887    /* Propagate rounding and bound checks */
888    volume = hsp_gain_to_volume(gain);
889
890    if (t->sink_volume == volume)
891        return volume;
892
893    t->sink_volume = volume;
894
895    /* If we are in the AG role, we send an unsolicited result-code to the headset
896     * to change the speaker gain. In the HS role, source and sink are swapped,
897     * so in this case we notify the AG that the microphone gain has changed
898     * by sending a command. */
899    if (is_pulseaudio_audio_gateway(t->profile)) {
900        rfcomm_write_response(trd->rfcomm_fd, "+VGS=%d", gain);
901    } else {
902        rfcomm_write_command(trd->rfcomm_fd, "AT+VGM=%d", gain);
903    }
904
905    return volume;
906}
907
908static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
909    struct transport_data *trd = t->userdata;
910    uint16_t gain = volume_to_hsp_gain(volume);
911
912    /* Propagate rounding and bound checks */
913    volume = hsp_gain_to_volume(gain);
914
915    if (t->source_volume == volume)
916        return volume;
917
918    t->source_volume = volume;
919
920    /* If we are in the AG role, we send an unsolicited result-code to the headset
921     * to change the microphone gain. In the HS role, source and sink are swapped,
922     * so in this case we notify the AG that the speaker gain has changed
923     * by sending a command. */
924    if (is_pulseaudio_audio_gateway(t->profile)) {
925        rfcomm_write_response(trd->rfcomm_fd, "+VGM=%d", gain);
926    } else {
927        rfcomm_write_command(trd->rfcomm_fd, "AT+VGS=%d", gain);
928    }
929
930    return volume;
931}
932
933static DBusMessage *profile_new_connection(DBusConnection *conn, DBusMessage *m, void *userdata) {
934    pa_bluetooth_backend *b = userdata;
935    pa_bluetooth_device *d;
936    pa_bluetooth_transport *t;
937    pa_bluetooth_profile_t p;
938    DBusMessage *r;
939    int fd;
940    const char *sender, *path, PA_UNUSED *handler;
941    DBusMessageIter arg_i;
942    char *pathfd;
943    struct transport_data *trd;
944
945    if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oha{sv}")) {
946        pa_log_error("Invalid signature found in NewConnection");
947        goto fail;
948    }
949
950    handler = dbus_message_get_path(m);
951    if (pa_streq(handler, HSP_AG_PROFILE)) {
952        p = PA_BLUETOOTH_PROFILE_HSP_HS;
953    } else if (pa_streq(handler, HSP_HS_PROFILE)) {
954        p = PA_BLUETOOTH_PROFILE_HSP_AG;
955    } else if (pa_streq(handler, HFP_AG_PROFILE)) {
956        p = PA_BLUETOOTH_PROFILE_HFP_HF;
957    } else {
958        pa_log_error("Invalid handler");
959        goto fail;
960    }
961
962    pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_OBJECT_PATH);
963    dbus_message_iter_get_basic(&arg_i, &path);
964
965    d = pa_bluetooth_discovery_get_device_by_path(b->discovery, path);
966    if (d == NULL) {
967        pa_log_error("Device doesn't exist for %s", path);
968        goto fail;
969    }
970
971    if (d->enable_hfp_hf) {
972        if (p == PA_BLUETOOTH_PROFILE_HSP_HS && pa_hashmap_get(d->uuids, PA_BLUETOOTH_UUID_HFP_HF)) {
973            /* If peer connecting to HSP Audio Gateway supports HFP HF profile
974             * reject this connection to force it to connect to HSP Audio Gateway instead.
975             */
976            pa_log_info("HFP HF enabled in native backend and is supported by peer, rejecting HSP HS peer connection");
977            goto fail;
978        }
979    }
980
981    pa_assert_se(dbus_message_iter_next(&arg_i));
982
983    pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_UNIX_FD);
984    dbus_message_iter_get_basic(&arg_i, &fd);
985
986    pa_log_debug("dbus: NewConnection path=%s, fd=%d, profile %s", path, fd,
987        pa_bluetooth_profile_to_string(p));
988
989    sender = dbus_message_get_sender(m);
990
991    pathfd = pa_sprintf_malloc ("%s/fd%d", path, fd);
992    t = pa_bluetooth_transport_new(d, sender, pathfd, p, NULL,
993                                   p == PA_BLUETOOTH_PROFILE_HFP_HF ?
994                                   sizeof(struct hfp_config) : 0);
995    pa_xfree(pathfd);
996
997    t->acquire = sco_acquire_cb;
998    t->release = sco_release_cb;
999    t->destroy = transport_destroy;
1000
1001    /* If PA is the HF/HS we are in control of volume attenuation and
1002     * can always send volume commands (notifications) to keep the peer
1003     * updated on actual volume value.
1004     *
1005     * If the peer is the HF/HS it is responsible for attenuation of both
1006     * speaker and microphone gain.
1007     * On HFP speaker/microphone gain support is reported by bit 4 in the
1008     * `AT+BRSF=` command. Since it isn't explicitly documented whether this
1009     * applies to speaker or microphone gain but the peer is required to send
1010     * an initial value with `AT+VG[MS]=` either callback is hooked
1011     * independently as soon as this command is received.
1012     * On HSP this is not specified and is assumed to be dynamic for both
1013     * speaker and microphone.
1014     */
1015    if (is_peer_audio_gateway(p)) {
1016        t->set_sink_volume = set_sink_volume;
1017        t->set_source_volume = set_source_volume;
1018    }
1019
1020    pa_bluetooth_transport_reconfigure(t, pa_bluetooth_get_hf_codec("CVSD"), sco_transport_write, NULL);
1021
1022    trd = pa_xnew0(struct transport_data, 1);
1023    trd->rfcomm_fd = fd;
1024    trd->mainloop = b->core->mainloop;
1025    trd->rfcomm_io = trd->mainloop->io_new(b->core->mainloop, fd, PA_IO_EVENT_INPUT,
1026        rfcomm_io_callback, t);
1027    t->userdata =  trd;
1028
1029    sco_listen(t);
1030
1031    if (p != PA_BLUETOOTH_PROFILE_HFP_HF)
1032        transport_put(t);
1033
1034    pa_assert_se(r = dbus_message_new_method_return(m));
1035
1036    return r;
1037
1038fail:
1039    pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to handle new connection"));
1040    return r;
1041}
1042
1043static DBusMessage *profile_request_disconnection(DBusConnection *conn, DBusMessage *m, void *userdata) {
1044    DBusMessage *r;
1045
1046    pa_assert_se(r = dbus_message_new_method_return(m));
1047
1048    return r;
1049}
1050
1051static DBusHandlerResult profile_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1052    pa_bluetooth_backend *b = userdata;
1053    DBusMessage *r = NULL;
1054    const char *path, *interface, *member;
1055
1056    pa_assert(b);
1057
1058    path = dbus_message_get_path(m);
1059    interface = dbus_message_get_interface(m);
1060    member = dbus_message_get_member(m);
1061
1062    pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
1063
1064    if (!pa_streq(path, HSP_AG_PROFILE) && !pa_streq(path, HSP_HS_PROFILE)
1065        && !pa_streq(path, HFP_AG_PROFILE))
1066        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1067
1068    if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
1069        const char *xml = PROFILE_INTROSPECT_XML;
1070
1071        pa_assert_se(r = dbus_message_new_method_return(m));
1072        pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
1073
1074    } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "Release")) {
1075        pa_log_debug("Release not handled");
1076        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1077    } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "RequestDisconnection")) {
1078        r = profile_request_disconnection(c, m, userdata);
1079    } else if (dbus_message_is_method_call(m, BLUEZ_PROFILE_INTERFACE, "NewConnection"))
1080        r = profile_new_connection(c, m, userdata);
1081    else
1082        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1083
1084    if (r) {
1085        pa_assert_se(dbus_connection_send(pa_dbus_connection_get(b->connection), r, NULL));
1086        dbus_message_unref(r);
1087    }
1088
1089    return DBUS_HANDLER_RESULT_HANDLED;
1090}
1091
1092static pa_hook_result_t adapter_uuids_changed_cb(pa_bluetooth_discovery *y, const pa_bluetooth_adapter *a, pa_bluetooth_backend *b) {
1093    pa_assert(y);
1094    pa_assert(a);
1095    pa_assert(b);
1096
1097    if (profile_status_get(y, PA_BLUETOOTH_PROFILE_HSP_HS) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE &&
1098        !pa_hashmap_get(a->uuids, PA_BLUETOOTH_UUID_HSP_AG))
1099        register_profile(b, HSP_AG_PROFILE, PA_BLUETOOTH_UUID_HSP_AG, PA_BLUETOOTH_PROFILE_HSP_HS);
1100
1101    if (profile_status_get(y, PA_BLUETOOTH_PROFILE_HSP_AG) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE &&
1102        !pa_hashmap_get(a->uuids, PA_BLUETOOTH_UUID_HSP_HS))
1103        register_profile(b, HSP_HS_PROFILE, PA_BLUETOOTH_UUID_HSP_HS, PA_BLUETOOTH_PROFILE_HSP_AG);
1104
1105    if (profile_status_get(y, PA_BLUETOOTH_PROFILE_HFP_HF) == PA_BLUETOOTH_PROFILE_STATUS_ACTIVE &&
1106        !pa_hashmap_get(a->uuids, PA_BLUETOOTH_UUID_HFP_AG))
1107        register_profile(b, HFP_AG_PROFILE, PA_BLUETOOTH_UUID_HFP_AG, PA_BLUETOOTH_PROFILE_HFP_HF);
1108
1109    return PA_HOOK_OK;
1110}
1111
1112static void profile_init(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) {
1113    static const DBusObjectPathVTable vtable_profile = {
1114        .message_function = profile_handler,
1115    };
1116    const char *object_name;
1117    const char *uuid;
1118
1119    pa_assert(b);
1120
1121    switch (profile) {
1122        case PA_BLUETOOTH_PROFILE_HSP_HS:
1123            object_name = HSP_AG_PROFILE;
1124            uuid = PA_BLUETOOTH_UUID_HSP_AG;
1125            break;
1126        case PA_BLUETOOTH_PROFILE_HSP_AG:
1127            object_name = HSP_HS_PROFILE;
1128            uuid = PA_BLUETOOTH_UUID_HSP_HS;
1129            break;
1130        case PA_BLUETOOTH_PROFILE_HFP_HF:
1131            object_name = HFP_AG_PROFILE;
1132            uuid = PA_BLUETOOTH_UUID_HFP_AG;
1133            break;
1134        default:
1135            pa_assert_not_reached();
1136            break;
1137    }
1138
1139    pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(b->connection), object_name, &vtable_profile, b));
1140
1141    profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_ACTIVE);
1142    register_profile(b, object_name, uuid, profile);
1143}
1144
1145static void profile_done(pa_bluetooth_backend *b, pa_bluetooth_profile_t profile) {
1146    pa_assert(b);
1147
1148    profile_status_set(b->discovery, profile, PA_BLUETOOTH_PROFILE_STATUS_INACTIVE);
1149
1150    switch (profile) {
1151        case PA_BLUETOOTH_PROFILE_HSP_HS:
1152            dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_AG_PROFILE);
1153            break;
1154        case PA_BLUETOOTH_PROFILE_HSP_AG:
1155            dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HSP_HS_PROFILE);
1156            break;
1157        case PA_BLUETOOTH_PROFILE_HFP_HF:
1158            dbus_connection_unregister_object_path(pa_dbus_connection_get(b->connection), HFP_AG_PROFILE);
1159            break;
1160        default:
1161            pa_assert_not_reached();
1162            break;
1163    }
1164}
1165
1166static void native_backend_apply_profile_registration_change(pa_bluetooth_backend *native_backend, bool enable_shared_profiles) {
1167    if (enable_shared_profiles) {
1168        profile_init(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG);
1169        if (native_backend->enable_hfp_hf)
1170            profile_init(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF);
1171    } else {
1172        profile_done(native_backend, PA_BLUETOOTH_PROFILE_HSP_AG);
1173        if (native_backend->enable_hfp_hf)
1174            profile_done(native_backend, PA_BLUETOOTH_PROFILE_HFP_HF);
1175    }
1176}
1177
1178void pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend *native_backend, bool enable) {
1179
1180   if (enable == native_backend->enable_shared_profiles)
1181       return;
1182
1183   native_backend_apply_profile_registration_change(native_backend, enable);
1184
1185   native_backend->enable_shared_profiles = enable;
1186}
1187
1188pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_discovery *y, bool enable_shared_profiles) {
1189    pa_bluetooth_backend *backend;
1190    DBusError err;
1191
1192    pa_log_debug("Bluetooth Headset Backend API support using the native backend");
1193
1194    backend = pa_xnew0(pa_bluetooth_backend, 1);
1195    backend->core = c;
1196
1197    dbus_error_init(&err);
1198    if (!(backend->connection = pa_dbus_bus_get(c, DBUS_BUS_SYSTEM, &err))) {
1199        pa_log("Failed to get D-Bus connection: %s", err.message);
1200        dbus_error_free(&err);
1201        pa_xfree(backend);
1202        return NULL;
1203    }
1204
1205    backend->discovery = y;
1206    backend->enable_shared_profiles = enable_shared_profiles;
1207    backend->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y);
1208    backend->enable_hsp_hs = pa_bluetooth_discovery_get_enable_native_hsp_hs(y);
1209
1210    backend->adapter_uuids_changed_slot =
1211        pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), PA_HOOK_NORMAL,
1212                        (pa_hook_cb_t) adapter_uuids_changed_cb, backend);
1213
1214    if (!backend->enable_hsp_hs && !backend->enable_hfp_hf)
1215        pa_log_warn("Both HSP HS and HFP HF bluetooth profiles disabled in native backend. Native backend will not register for headset connections.");
1216
1217    if (backend->enable_hsp_hs)
1218        profile_init(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
1219
1220    if (backend->enable_shared_profiles)
1221        native_backend_apply_profile_registration_change(backend, true);
1222
1223    return backend;
1224}
1225
1226void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) {
1227    pa_assert(backend);
1228
1229    pa_dbus_free_pending_list(&backend->pending);
1230
1231    if (backend->adapter_uuids_changed_slot)
1232        pa_hook_slot_free(backend->adapter_uuids_changed_slot);
1233
1234    if (backend->enable_shared_profiles)
1235        native_backend_apply_profile_registration_change(backend, false);
1236
1237    if (backend->enable_hsp_hs)
1238        profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS);
1239
1240    pa_dbus_connection_unref(backend->connection);
1241
1242    pa_xfree(backend);
1243}
1244