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