• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
42 struct 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 
54 struct 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 
62 struct 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  */
74 enum 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 
86 enum 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 */
101 static 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 
hsp_gain_to_volume(uint16_t gain)138 static 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 
volume_to_hsp_gain(pa_volume_t volume)151 static 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 
is_peer_audio_gateway(pa_bluetooth_profile_t peer_profile)160 static 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 
is_pulseaudio_audio_gateway(pa_bluetooth_profile_t peer_profile)173 static bool is_pulseaudio_audio_gateway(pa_bluetooth_profile_t peer_profile) {
174     return !is_peer_audio_gateway(peer_profile);
175 }
176 
send_and_add_to_pending(pa_bluetooth_backend * backend,DBusMessage * m,DBusPendingCallNotifyFunction func,void * call_data)177 static 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 
rfcomm_fmt_write(int fd,const char * fmt_line,const char * fmt_command,va_list ap)195 static 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> */
rfcomm_write_command(int fd,const char * fmt,...)215 static 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> */
rfcomm_write_response(int fd,const char * fmt,...)225 static 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 
sco_setsockopt_enable_bt_voice(pa_bluetooth_transport * t,int fd)234 static 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 
sco_do_connect(pa_bluetooth_transport * t)248 static 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 
298 fail_close:
299     close(sock);
300     return -1;
301 }
302 
sco_do_accept(pa_bluetooth_transport * t)303 static 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 
321 fail:
322     return -1;
323 }
324 
sco_acquire_cb(pa_bluetooth_transport * t,bool optional,size_t * imtu,size_t * omtu)325 static 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 
378 fail:
379     return -1;
380 }
381 
sco_release_cb(pa_bluetooth_transport * t)382 static 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 
sco_transport_write(pa_bluetooth_transport * t,int fd,const void * buffer,size_t size,size_t write_mtu)387 static 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 
sco_io_callback(pa_mainloop_api * io,pa_io_event * e,int fd,pa_io_event_flags_t events,void * userdata)442 static 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 
458 fail:
459     return;
460 }
461 
sco_listen(pa_bluetooth_transport * t)462 static 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 
503 fail_close:
504     close(sock);
505     return -1;
506 }
507 
register_profile_reply(DBusPendingCall * pending,void * userdata)508 static 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 
535 finish:
536     dbus_message_unref(r);
537 
538     PA_LLIST_REMOVE(pa_dbus_pending, b->pending, p);
539     pa_dbus_pending_free(p);
540 }
541 
register_profile(pa_bluetooth_backend * b,const char * object,const char * uuid,pa_bluetooth_profile_t profile)542 static 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 
transport_put(pa_bluetooth_transport * t)579 static 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 
586 static pa_volume_t set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume);
587 static pa_volume_t set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume);
588 
hfp_rfcomm_handle(int fd,pa_bluetooth_transport * t,const char * buf)589 static 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 
rfcomm_io_callback(pa_mainloop_api * io,pa_io_event * e,int fd,pa_io_event_flags_t events,void * userdata)741 static 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 
862 fail:
863     pa_bluetooth_transport_unlink(t);
864     pa_bluetooth_transport_free(t);
865 }
866 
transport_destroy(pa_bluetooth_transport * t)867 static 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 
set_sink_volume(pa_bluetooth_transport * t,pa_volume_t volume)883 static 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 
set_source_volume(pa_bluetooth_transport * t,pa_volume_t volume)908 static 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 
profile_new_connection(DBusConnection * conn,DBusMessage * m,void * userdata)933 static 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 
1038 fail:
1039     pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to handle new connection"));
1040     return r;
1041 }
1042 
profile_request_disconnection(DBusConnection * conn,DBusMessage * m,void * userdata)1043 static 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 
profile_handler(DBusConnection * c,DBusMessage * m,void * userdata)1051 static 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 
adapter_uuids_changed_cb(pa_bluetooth_discovery * y,const pa_bluetooth_adapter * a,pa_bluetooth_backend * b)1092 static 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 
profile_init(pa_bluetooth_backend * b,pa_bluetooth_profile_t profile)1112 static 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 
profile_done(pa_bluetooth_backend * b,pa_bluetooth_profile_t profile)1145 static 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 
native_backend_apply_profile_registration_change(pa_bluetooth_backend * native_backend,bool enable_shared_profiles)1166 static 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 
pa_bluetooth_native_backend_enable_shared_profiles(pa_bluetooth_backend * native_backend,bool enable)1178 void 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 
pa_bluetooth_native_backend_new(pa_core * c,pa_bluetooth_discovery * y,bool enable_shared_profiles)1188 pa_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 
pa_bluetooth_native_backend_free(pa_bluetooth_backend * backend)1226 void 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