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