1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2008-2013 João Paulo Rechi Vita
5 Copyrigth 2018-2019 Pali Rohár <pali.rohar@gmail.com>
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <errno.h>
26
27 #include <pulse/rtclock.h>
28 #include <pulse/timeval.h>
29 #include <pulse/xmalloc.h>
30
31 #include <pulsecore/core.h>
32 #include <pulsecore/core-error.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/dbus-shared.h>
35 #include <pulsecore/log.h>
36 #include <pulsecore/macro.h>
37 #include <pulsecore/refcnt.h>
38 #include <pulsecore/shared.h>
39
40 #include "a2dp-codec-api.h"
41 #include "a2dp-codec-util.h"
42 #include "a2dp-codecs.h"
43
44 #include "bluez5-util.h"
45
46 #define WAIT_FOR_PROFILES_TIMEOUT_USEC (3 * PA_USEC_PER_SEC)
47
48 #define DBUS_INTERFACE_OBJECT_MANAGER DBUS_INTERFACE_DBUS ".ObjectManager"
49
50 #define A2DP_OBJECT_MANAGER_PATH "/MediaEndpoint"
51 #define A2DP_SOURCE_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSource"
52 #define A2DP_SINK_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSink"
53 #define PULSEAUDIO_BASE_PATH "/org/pulseaudio"
54
55 #define OBJECT_MANAGER_INTROSPECT_XML \
56 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
57 "<node>\n" \
58 " <interface name=\"" DBUS_INTERFACE_OBJECT_MANAGER "\">\n" \
59 " <method name=\"GetManagedObjects\">\n" \
60 " <arg name=\"objects\" direction=\"out\" type=\"a{oa{sa{sv}}}\"/>\n" \
61 " </method>\n" \
62 " <signal name=\"InterfacesAdded\">\n" \
63 " <arg name=\"object\" type=\"o\"/>\n" \
64 " <arg name=\"interfaces\" type=\"a{sa{sv}}\"/>\n" \
65 " </signal>\n" \
66 " <signal name=\"InterfacesRemoved\">\n" \
67 " <arg name=\"object\" type=\"o\"/>\n" \
68 " <arg name=\"interfaces\" type=\"as\"/>\n" \
69 " </signal>\n" \
70 " </interface>\n" \
71 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \
72 " <method name=\"Introspect\">\n" \
73 " <arg name=\"data\" direction=\"out\" type=\"s\"/>\n" \
74 " </method>\n" \
75 " </interface>\n" \
76 " <node name=\"A2DPSink\"/>\n" \
77 " <node name=\"A2DPSource\"/>\n" \
78 "</node>\n"
79
80 #define ENDPOINT_INTROSPECT_XML \
81 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
82 "<node>" \
83 " <interface name=\"" BLUEZ_MEDIA_ENDPOINT_INTERFACE "\">" \
84 " <method name=\"SetConfiguration\">" \
85 " <arg name=\"transport\" direction=\"in\" type=\"o\"/>" \
86 " <arg name=\"properties\" direction=\"in\" type=\"ay\"/>" \
87 " </method>" \
88 " <method name=\"SelectConfiguration\">" \
89 " <arg name=\"capabilities\" direction=\"in\" type=\"ay\"/>" \
90 " <arg name=\"configuration\" direction=\"out\" type=\"ay\"/>" \
91 " </method>" \
92 " <method name=\"ClearConfiguration\">" \
93 " <arg name=\"transport\" direction=\"in\" type=\"o\"/>" \
94 " </method>" \
95 " <method name=\"Release\">" \
96 " </method>" \
97 " </interface>" \
98 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">" \
99 " <method name=\"Introspect\">" \
100 " <arg name=\"data\" type=\"s\" direction=\"out\"/>" \
101 " </method>" \
102 " </interface>" \
103 "</node>"
104
a2dp_gain_to_volume(uint16_t gain)105 static pa_volume_t a2dp_gain_to_volume(uint16_t gain) {
106 pa_volume_t volume = (pa_volume_t) ((
107 gain * PA_VOLUME_NORM
108 /* Round to closest by adding half the denominator */
109 + A2DP_MAX_GAIN / 2
110 ) / A2DP_MAX_GAIN);
111
112 if (volume > PA_VOLUME_NORM)
113 volume = PA_VOLUME_NORM;
114
115 return volume;
116 }
117
volume_to_a2dp_gain(pa_volume_t volume)118 static uint16_t volume_to_a2dp_gain(pa_volume_t volume) {
119 uint16_t gain = (uint16_t) ((
120 volume * A2DP_MAX_GAIN
121 /* Round to closest by adding half the denominator */
122 + PA_VOLUME_NORM / 2
123 ) / PA_VOLUME_NORM);
124
125 if (gain > A2DP_MAX_GAIN)
126 gain = A2DP_MAX_GAIN;
127
128 return gain;
129 }
130
131 struct pa_bluetooth_discovery {
132 PA_REFCNT_DECLARE;
133
134 pa_core *core;
135 pa_dbus_connection *connection;
136 bool filter_added;
137 bool matches_added;
138 bool objects_listed;
139 pa_hook hooks[PA_BLUETOOTH_HOOK_MAX];
140 pa_hashmap *adapters;
141 pa_hashmap *devices;
142 pa_hashmap *transports;
143 pa_bluetooth_profile_status_t profiles_status[PA_BLUETOOTH_PROFILE_COUNT];
144
145 int headset_backend;
146 pa_bluetooth_backend *ofono_backend, *native_backend;
147 PA_LLIST_HEAD(pa_dbus_pending, pending);
148 bool enable_native_hsp_hs;
149 bool enable_native_hfp_hf;
150 bool enable_msbc;
151 };
152
send_and_add_to_pending(pa_bluetooth_discovery * y,DBusMessage * m,DBusPendingCallNotifyFunction func,void * call_data)153 static pa_dbus_pending* send_and_add_to_pending(pa_bluetooth_discovery *y, DBusMessage *m,
154 DBusPendingCallNotifyFunction func, void *call_data) {
155 pa_dbus_pending *p;
156 DBusPendingCall *call;
157
158 pa_assert(y);
159 pa_assert(m);
160
161 pa_assert_se(dbus_connection_send_with_reply(pa_dbus_connection_get(y->connection), m, &call, -1));
162
163 p = pa_dbus_pending_new(pa_dbus_connection_get(y->connection), m, call, y, call_data);
164 PA_LLIST_PREPEND(pa_dbus_pending, y->pending, p);
165 dbus_pending_call_set_notify(call, func, p, NULL);
166
167 return p;
168 }
169
check_variant_property(DBusMessageIter * i)170 static const char *check_variant_property(DBusMessageIter *i) {
171 const char *key;
172
173 pa_assert(i);
174
175 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_STRING) {
176 pa_log_error("Property name not a string.");
177 return NULL;
178 }
179
180 dbus_message_iter_get_basic(i, &key);
181
182 if (!dbus_message_iter_next(i)) {
183 pa_log_error("Property value missing");
184 return NULL;
185 }
186
187 if (dbus_message_iter_get_arg_type(i) != DBUS_TYPE_VARIANT) {
188 pa_log_error("Property value not a variant.");
189 return NULL;
190 }
191
192 return key;
193 }
194
profile_status_get(pa_bluetooth_discovery * y,pa_bluetooth_profile_t profile)195 pa_bluetooth_profile_status_t profile_status_get(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) {
196 return y->profiles_status[profile];
197 }
198
profile_status_set(pa_bluetooth_discovery * y,pa_bluetooth_profile_t profile,pa_bluetooth_profile_status_t status)199 void profile_status_set(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile, pa_bluetooth_profile_status_t status) {
200 y->profiles_status[profile] = status;
201 }
202
pa_bluetooth_transport_new(pa_bluetooth_device * d,const char * owner,const char * path,pa_bluetooth_profile_t p,const uint8_t * config,size_t size)203 pa_bluetooth_transport *pa_bluetooth_transport_new(pa_bluetooth_device *d, const char *owner, const char *path,
204 pa_bluetooth_profile_t p, const uint8_t *config, size_t size) {
205 pa_bluetooth_transport *t;
206
207 t = pa_xnew0(pa_bluetooth_transport, 1);
208 t->device = d;
209 t->owner = pa_xstrdup(owner);
210 t->path = pa_xstrdup(path);
211 t->profile = p;
212 t->config_size = size;
213 /* Always force initial volume to be set/propagated correctly */
214 t->sink_volume = PA_VOLUME_INVALID;
215 t->source_volume = PA_VOLUME_INVALID;
216
217 if (size > 0) {
218 t->config = pa_xnew(uint8_t, size);
219 if (config)
220 memcpy(t->config, config, size);
221 else
222 memset(t->config, 0, size);
223 }
224
225 return t;
226 }
227
pa_bluetooth_transport_reconfigure(pa_bluetooth_transport * t,const pa_bt_codec * bt_codec,pa_bluetooth_transport_write_cb write_cb,pa_bluetooth_transport_setsockopt_cb setsockopt_cb)228 void pa_bluetooth_transport_reconfigure(pa_bluetooth_transport *t, const pa_bt_codec *bt_codec,
229 pa_bluetooth_transport_write_cb write_cb, pa_bluetooth_transport_setsockopt_cb setsockopt_cb) {
230 pa_assert(t);
231
232 t->bt_codec = bt_codec;
233
234 t->write = write_cb;
235 t->setsockopt = setsockopt_cb;
236
237 /* reset stream write type hint */
238 t->stream_write_type = 0;
239
240 /* reset SCO MTU adjustment hint */
241 t->last_read_size = 0;
242 }
243
transport_state_to_string(pa_bluetooth_transport_state_t state)244 static const char *transport_state_to_string(pa_bluetooth_transport_state_t state) {
245 switch(state) {
246 case PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED:
247 return "disconnected";
248 case PA_BLUETOOTH_TRANSPORT_STATE_IDLE:
249 return "idle";
250 case PA_BLUETOOTH_TRANSPORT_STATE_PLAYING:
251 return "playing";
252 }
253
254 return "invalid";
255 }
256
pa_bluetooth_device_supports_profile(const pa_bluetooth_device * device,pa_bluetooth_profile_t profile)257 bool pa_bluetooth_device_supports_profile(const pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
258 bool show_hfp, show_hsp, r;
259
260 pa_assert(device);
261
262 /* While discovery is being released adapters will be removed from devices,
263 * and there are no profiles to support without adapter.
264 */
265 if (!device->adapter) {
266 pa_log_debug("Device %s (%s) has no adapter to support profile %s",
267 device->alias, device->address, pa_bluetooth_profile_to_string(profile));
268 return false;
269 }
270
271 if (device->enable_hfp_hf) {
272 show_hfp = pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF);
273 show_hsp = !show_hfp;
274 } else {
275 show_hfp = false;
276 show_hsp = true;
277 }
278
279 switch (profile) {
280 case PA_BLUETOOTH_PROFILE_A2DP_SINK:
281 r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SINK) &&
282 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE));
283 break;
284 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
285 r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_A2DP_SOURCE) &&
286 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_A2DP_SINK));
287 break;
288 case PA_BLUETOOTH_PROFILE_HSP_HS:
289 r = show_hsp
290 && ( !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS) &&
291 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_AG)) ||
292 !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT) &&
293 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_AG)) );
294 break;
295 case PA_BLUETOOTH_PROFILE_HSP_AG:
296 r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG) &&
297 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_HS)) ||
298 !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HSP_AG) &&
299 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HSP_HS_ALT));
300 break;
301 case PA_BLUETOOTH_PROFILE_HFP_HF:
302 r = show_hfp
303 && !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_HF) &&
304 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HFP_AG));
305 break;
306 case PA_BLUETOOTH_PROFILE_HFP_AG:
307 r = !!(pa_hashmap_get(device->uuids, PA_BLUETOOTH_UUID_HFP_AG) &&
308 pa_hashmap_get(device->adapter->uuids, PA_BLUETOOTH_UUID_HFP_HF));
309 break;
310 case PA_BLUETOOTH_PROFILE_OFF:
311 default:
312 pa_assert_not_reached();
313 break;
314 }
315
316 pa_log_debug("Checking if device %s (%s) supports profile %s: %s",
317 device->alias, device->address, pa_bluetooth_profile_to_string(profile), r ? "true" : "false");
318
319 return r;
320 }
321
device_is_profile_connected(pa_bluetooth_device * device,pa_bluetooth_profile_t profile)322 static bool device_is_profile_connected(pa_bluetooth_device *device, pa_bluetooth_profile_t profile) {
323 if (device->transports[profile] && device->transports[profile]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
324 return true;
325 else
326 return false;
327 }
328
device_count_disconnected_profiles(pa_bluetooth_device * device)329 static unsigned device_count_disconnected_profiles(pa_bluetooth_device *device) {
330 pa_bluetooth_profile_t profile;
331 unsigned count = 0;
332
333 for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
334 if (!pa_bluetooth_device_supports_profile(device, profile))
335 continue;
336
337 if (!device_is_profile_connected(device, profile))
338 count++;
339 }
340
341 return count;
342 }
343
device_stop_waiting_for_profiles(pa_bluetooth_device * device)344 static void device_stop_waiting_for_profiles(pa_bluetooth_device *device) {
345 if (!device->wait_for_profiles_timer)
346 return;
347
348 device->discovery->core->mainloop->time_free(device->wait_for_profiles_timer);
349 device->wait_for_profiles_timer = NULL;
350 }
351
wait_for_profiles_cb(pa_mainloop_api * api,pa_time_event * event,const struct timeval * tv,void * userdata)352 static void wait_for_profiles_cb(pa_mainloop_api *api, pa_time_event* event, const struct timeval *tv, void *userdata) {
353 pa_bluetooth_device *device = userdata;
354 pa_strbuf *buf;
355 pa_bluetooth_profile_t profile;
356 bool first = true;
357 char *profiles_str;
358
359 device_stop_waiting_for_profiles(device);
360
361 buf = pa_strbuf_new();
362
363 for (profile = 0; profile < PA_BLUETOOTH_PROFILE_COUNT; profile++) {
364 if (device_is_profile_connected(device, profile))
365 continue;
366
367 if (!pa_bluetooth_device_supports_profile(device, profile))
368 continue;
369
370 if (first)
371 first = false;
372 else
373 pa_strbuf_puts(buf, ", ");
374
375 pa_strbuf_puts(buf, pa_bluetooth_profile_to_string(profile));
376 }
377
378 profiles_str = pa_strbuf_to_string_free(buf);
379 pa_log_debug("Timeout expired, and device %s still has disconnected profiles: %s",
380 device->path, profiles_str);
381 pa_xfree(profiles_str);
382 pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
383 }
384
device_start_waiting_for_profiles(pa_bluetooth_device * device)385 static void device_start_waiting_for_profiles(pa_bluetooth_device *device) {
386 pa_assert(!device->wait_for_profiles_timer);
387 device->wait_for_profiles_timer = pa_core_rttime_new(device->discovery->core,
388 pa_rtclock_now() + WAIT_FOR_PROFILES_TIMEOUT_USEC,
389 wait_for_profiles_cb, device);
390 }
391
392 struct switch_codec_data {
393 char *pa_endpoint;
394 char *device_path;
395 pa_bluetooth_profile_t profile;
396 void (*cb)(bool, pa_bluetooth_profile_t profile, void *);
397 void *userdata;
398 };
399
pa_bluetooth_device_switch_codec_reply(DBusPendingCall * pending,void * userdata)400 static void pa_bluetooth_device_switch_codec_reply(DBusPendingCall *pending, void *userdata) {
401 DBusMessage *r;
402 pa_dbus_pending *p;
403 pa_bluetooth_discovery *y;
404 pa_bluetooth_device *device;
405 struct switch_codec_data *data;
406
407 pa_assert(pending);
408 pa_assert_se(p = userdata);
409 pa_assert_se(y = p->context_data);
410 pa_assert_se(data = p->call_data);
411 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
412
413 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
414 pa_dbus_pending_free(p);
415
416 device = pa_hashmap_get(y->devices, data->device_path);
417 if (!device) {
418 pa_log_error("Changing codec for device %s with profile %s failed. Device is not connected anymore",
419 data->device_path, pa_bluetooth_profile_to_string(data->profile));
420 data->cb(false, data->profile, data->userdata);
421 } else if (dbus_message_get_type(r) != DBUS_MESSAGE_TYPE_ERROR) {
422 pa_log_info("Changing codec for device %s with profile %s succeeded",
423 data->device_path, pa_bluetooth_profile_to_string(data->profile));
424 data->cb(true, data->profile, data->userdata);
425 } else if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
426 pa_log_error("Changing codec for device %s with profile %s failed. Error: %s",
427 data->device_path, pa_bluetooth_profile_to_string(data->profile),
428 dbus_message_get_error_name(r));
429 }
430
431 dbus_message_unref(r);
432
433 pa_xfree(data->pa_endpoint);
434 pa_xfree(data->device_path);
435 pa_xfree(data);
436
437 device->codec_switching_in_progress = false;
438 }
439
pa_bluetooth_device_switch_codec(pa_bluetooth_device * device,pa_bluetooth_profile_t profile,pa_hashmap * capabilities_hashmap,const pa_a2dp_endpoint_conf * endpoint_conf,void (* codec_switch_cb)(bool,pa_bluetooth_profile_t profile,void *),void * userdata)440 bool pa_bluetooth_device_switch_codec(pa_bluetooth_device *device, pa_bluetooth_profile_t profile,
441 pa_hashmap *capabilities_hashmap, const pa_a2dp_endpoint_conf *endpoint_conf,
442 void (*codec_switch_cb)(bool, pa_bluetooth_profile_t profile, void *), void *userdata) {
443 DBusMessageIter iter, dict;
444 DBusMessage *m;
445 struct switch_codec_data *data;
446 pa_a2dp_codec_capabilities *capabilities;
447 uint8_t config[MAX_A2DP_CAPS_SIZE];
448 uint8_t config_size;
449 bool is_a2dp_sink;
450 pa_hashmap *all_endpoints;
451 char *pa_endpoint;
452 const char *endpoint;
453
454 pa_assert(device);
455 pa_assert(capabilities_hashmap);
456 pa_assert(endpoint_conf);
457
458 if (device->codec_switching_in_progress) {
459 pa_log_error("Codec switching operation already in progress");
460 return false;
461 }
462
463 is_a2dp_sink = profile == PA_BLUETOOTH_PROFILE_A2DP_SINK;
464
465 all_endpoints = NULL;
466 all_endpoints = pa_hashmap_get(is_a2dp_sink ? device->a2dp_sink_endpoints : device->a2dp_source_endpoints,
467 &endpoint_conf->id);
468 pa_assert(all_endpoints);
469
470 pa_assert_se(endpoint = endpoint_conf->choose_remote_endpoint(capabilities_hashmap, &device->discovery->core->default_sample_spec, is_a2dp_sink));
471 pa_assert_se(capabilities = pa_hashmap_get(all_endpoints, endpoint));
472
473 config_size = endpoint_conf->fill_preferred_configuration(&device->discovery->core->default_sample_spec,
474 capabilities->buffer, capabilities->size, config);
475 if (config_size == 0)
476 return false;
477
478 pa_endpoint = pa_sprintf_malloc("%s/%s", is_a2dp_sink ? A2DP_SOURCE_ENDPOINT : A2DP_SINK_ENDPOINT,
479 endpoint_conf->bt_codec.name);
480
481 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, endpoint,
482 BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"));
483
484 dbus_message_iter_init_append(m, &iter);
485 pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &pa_endpoint));
486 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
487 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
488 DBUS_TYPE_STRING_AS_STRING
489 DBUS_TYPE_VARIANT_AS_STRING
490 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
491 &dict);
492 pa_dbus_append_basic_array_variant_dict_entry(&dict, "Capabilities", DBUS_TYPE_BYTE, &config, config_size);
493 dbus_message_iter_close_container(&iter, &dict);
494
495 device->codec_switching_in_progress = true;
496
497 data = pa_xnew0(struct switch_codec_data, 1);
498 data->pa_endpoint = pa_endpoint;
499 data->device_path = pa_xstrdup(device->path);
500 data->profile = profile;
501 data->cb = codec_switch_cb;
502 data->userdata = userdata;
503
504 send_and_add_to_pending(device->discovery, m, pa_bluetooth_device_switch_codec_reply, data);
505
506 return true;
507 }
508
pa_bluetooth_transport_set_state(pa_bluetooth_transport * t,pa_bluetooth_transport_state_t state)509 void pa_bluetooth_transport_set_state(pa_bluetooth_transport *t, pa_bluetooth_transport_state_t state) {
510 bool old_any_connected;
511 unsigned n_disconnected_profiles;
512 bool new_device_appeared;
513 bool device_disconnected;
514
515 pa_assert(t);
516
517 if (t->state == state)
518 return;
519
520 old_any_connected = pa_bluetooth_device_any_transport_connected(t->device);
521
522 pa_log_debug("Transport %s state: %s -> %s",
523 t->path, transport_state_to_string(t->state), transport_state_to_string(state));
524
525 t->state = state;
526
527 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED], t);
528
529 /* If there are profiles that are expected to get connected soon (based
530 * on the UUID list), we wait for a bit before announcing the new
531 * device, so that all profiles have time to get connected before the
532 * card object is created. If we didn't wait, the card would always
533 * have only one profile marked as available in the initial state,
534 * which would prevent module-card-restore from restoring the initial
535 * profile properly. */
536
537 n_disconnected_profiles = device_count_disconnected_profiles(t->device);
538
539 new_device_appeared = !old_any_connected && pa_bluetooth_device_any_transport_connected(t->device);
540 device_disconnected = old_any_connected && !pa_bluetooth_device_any_transport_connected(t->device);
541
542 if (new_device_appeared) {
543 if (n_disconnected_profiles > 0)
544 device_start_waiting_for_profiles(t->device);
545 else
546 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
547 return;
548 }
549
550 if (device_disconnected) {
551 if (t->device->wait_for_profiles_timer) {
552 /* If the timer is still running when the device disconnects, we
553 * never sent the notification of the device getting connected, so
554 * we don't need to send a notification about the disconnection
555 * either. Let's just stop the timer. */
556 device_stop_waiting_for_profiles(t->device);
557 } else
558 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
559 return;
560 }
561
562 if (n_disconnected_profiles == 0 && t->device->wait_for_profiles_timer) {
563 /* All profiles are now connected, so we can stop the wait timer and
564 * send a notification of the new device. */
565 device_stop_waiting_for_profiles(t->device);
566 pa_hook_fire(&t->device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], t->device);
567 }
568 }
569
pa_bluetooth_transport_set_volume(pa_bluetooth_transport * t,pa_volume_t volume)570 static pa_volume_t pa_bluetooth_transport_set_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
571 static const char *volume_str = "Volume";
572 static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
573 DBusMessage *m;
574 DBusMessageIter iter;
575 uint16_t gain;
576
577 pa_assert(t);
578 pa_assert(t->device);
579 pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
580 pa_assert(t->device->discovery);
581
582 gain = volume_to_a2dp_gain(volume);
583 /* Propagate rounding and bound checks */
584 volume = a2dp_gain_to_volume(gain);
585
586 if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE && t->source_volume == volume)
587 return volume;
588 else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK && t->sink_volume == volume)
589 return volume;
590
591 if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE)
592 t->source_volume = volume;
593 else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
594 t->sink_volume = volume;
595
596 pa_log_debug("Sending A2DP volume %d/127 to peer", gain);
597
598 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Set"));
599
600 dbus_message_iter_init_append(m, &iter);
601 pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &mediatransport_str));
602 pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &volume_str));
603 pa_dbus_append_basic_variant(&iter, DBUS_TYPE_UINT16, &gain);
604
605 /* Ignore replies, wait for the Volume property to change (generally arrives
606 * before this function replies).
607 *
608 * In an ideal world BlueZ exposes a function to change volume, that returns
609 * with the actual volume set by the peer as returned by the SetAbsoluteVolume
610 * AVRCP command. That is required later to perform software volume compensation
611 * based on actual playback volume.
612 */
613 dbus_message_set_no_reply(m, true);
614 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(t->device->discovery->connection), m, NULL));
615 dbus_message_unref(m);
616
617 return volume;
618 }
619
pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport * t,pa_volume_t volume)620 static pa_volume_t pa_bluetooth_transport_set_sink_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
621 pa_assert(t);
622 pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK);
623 return pa_bluetooth_transport_set_volume(t, volume);
624 }
625
pa_bluetooth_transport_set_source_volume(pa_bluetooth_transport * t,pa_volume_t volume)626 static pa_volume_t pa_bluetooth_transport_set_source_volume(pa_bluetooth_transport *t, pa_volume_t volume) {
627 pa_assert(t);
628 pa_assert(t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE);
629 return pa_bluetooth_transport_set_volume(t, volume);
630 }
631
pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport * t,pa_volume_t volume)632 static void pa_bluetooth_transport_remote_volume_changed(pa_bluetooth_transport *t, pa_volume_t volume) {
633 pa_bluetooth_hook_t hook;
634 bool is_source;
635 char volume_str[PA_VOLUME_SNPRINT_MAX];
636
637 pa_assert(t);
638 pa_assert(t->device);
639
640 if (!t->device->avrcp_absolute_volume)
641 return;
642
643 is_source = t->profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
644
645 if (is_source) {
646 if (t->source_volume == volume)
647 return;
648 t->source_volume = volume;
649 hook = PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED;
650 } else if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK) {
651 if (t->sink_volume == volume)
652 return;
653 t->sink_volume = volume;
654 hook = PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED;
655
656 /* A2DP Absolute Volume is optional. This callback is only
657 * attached when the peer supports it, and the hook handler
658 * further attaches the necessary hardware callback to the
659 * pa_sink and disables software attenuation.
660 */
661 if (!t->set_sink_volume) {
662 pa_log_debug("A2DP sink supports volume control");
663 t->set_sink_volume = pa_bluetooth_transport_set_sink_volume;
664 }
665 } else {
666 pa_assert_not_reached();
667 }
668
669 pa_log_debug("Reporting volume change %s for %s",
670 pa_volume_snprint(volume_str, sizeof(volume_str), volume),
671 is_source ? "source" : "sink");
672
673 pa_hook_fire(pa_bluetooth_discovery_hook(t->device->discovery, hook), t);
674 }
675
pa_bluetooth_transport_put(pa_bluetooth_transport * t)676 void pa_bluetooth_transport_put(pa_bluetooth_transport *t) {
677 pa_assert(t);
678
679 t->device->transports[t->profile] = t;
680 pa_assert_se(pa_hashmap_put(t->device->discovery->transports, t->path, t) >= 0);
681 pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_IDLE);
682 }
683
pa_bluetooth_transport_unlink(pa_bluetooth_transport * t)684 void pa_bluetooth_transport_unlink(pa_bluetooth_transport *t) {
685 pa_assert(t);
686
687 pa_bluetooth_transport_set_state(t, PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED);
688 pa_hashmap_remove(t->device->discovery->transports, t->path);
689 t->device->transports[t->profile] = NULL;
690 }
691
pa_bluetooth_transport_free(pa_bluetooth_transport * t)692 void pa_bluetooth_transport_free(pa_bluetooth_transport *t) {
693 pa_assert(t);
694
695 if (t->destroy)
696 t->destroy(t);
697 pa_bluetooth_transport_unlink(t);
698
699 pa_xfree(t->owner);
700 pa_xfree(t->path);
701 pa_xfree(t->config);
702 pa_xfree(t);
703 }
704
bluez5_transport_acquire_cb(pa_bluetooth_transport * t,bool optional,size_t * imtu,size_t * omtu)705 static int bluez5_transport_acquire_cb(pa_bluetooth_transport *t, bool optional, size_t *imtu, size_t *omtu) {
706 DBusMessage *m, *r;
707 DBusError err;
708 int ret;
709 uint16_t i, o;
710 const char *method = optional ? "TryAcquire" : "Acquire";
711
712 pa_assert(t);
713 pa_assert(t->device);
714 pa_assert(t->device->discovery);
715
716 pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, method));
717
718 dbus_error_init(&err);
719
720 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
721 dbus_message_unref(m);
722 m = NULL;
723 if (!r) {
724 if (optional && pa_streq(err.name, BLUEZ_ERROR_NOT_AVAILABLE))
725 pa_log_info("Failed optional acquire of unavailable transport %s", t->path);
726 else
727 pa_log_error("Transport %s() failed for transport %s (%s)", method, t->path, err.message);
728
729 dbus_error_free(&err);
730 return -1;
731 }
732
733 if (!dbus_message_get_args(r, &err, DBUS_TYPE_UNIX_FD, &ret, DBUS_TYPE_UINT16, &i, DBUS_TYPE_UINT16, &o,
734 DBUS_TYPE_INVALID)) {
735 pa_log_error("Failed to parse %s() reply: %s", method, err.message);
736 dbus_error_free(&err);
737 ret = -1;
738 goto finish;
739 }
740
741 if (imtu)
742 *imtu = i;
743
744 if (omtu)
745 *omtu = o;
746
747 finish:
748 dbus_message_unref(r);
749 return ret;
750 }
751
bluez5_transport_release_cb(pa_bluetooth_transport * t)752 static void bluez5_transport_release_cb(pa_bluetooth_transport *t) {
753 DBusMessage *m, *r;
754 DBusError err;
755
756 pa_assert(t);
757 pa_assert(t->device);
758 pa_assert(t->device->discovery);
759
760 dbus_error_init(&err);
761
762 if (t->state <= PA_BLUETOOTH_TRANSPORT_STATE_IDLE) {
763 pa_log_info("Transport %s auto-released by BlueZ or already released", t->path);
764 return;
765 }
766
767 pa_assert_se(m = dbus_message_new_method_call(t->owner, t->path, BLUEZ_MEDIA_TRANSPORT_INTERFACE, "Release"));
768 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(t->device->discovery->connection), m, -1, &err);
769 dbus_message_unref(m);
770 m = NULL;
771 if (r) {
772 dbus_message_unref(r);
773 r = NULL;
774 }
775
776 if (dbus_error_is_set(&err)) {
777 pa_log_error("Failed to release transport %s: %s", t->path, err.message);
778 dbus_error_free(&err);
779 } else
780 pa_log_info("Transport %s released", t->path);
781 }
782
get_volume_reply(DBusPendingCall * pending,void * userdata)783 static void get_volume_reply(DBusPendingCall *pending, void *userdata) {
784 DBusMessage *r;
785 DBusMessageIter iter, variant;
786 pa_dbus_pending *p;
787 pa_bluetooth_discovery *y;
788 pa_bluetooth_transport *t;
789 uint16_t gain;
790 pa_volume_t volume;
791 const char *error_name, *error_message;
792
793 pa_assert(pending);
794 pa_assert_se(p = userdata);
795 pa_assert_se(y = p->context_data);
796 pa_assert_se(t = p->call_data);
797 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
798
799 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
800 error_name = dbus_message_get_error_name(r);
801 error_message = pa_dbus_get_error_message(r);
802
803 if (pa_streq(error_name, DBUS_ERROR_INVALID_ARGS) && pa_streq(error_message, "No such property 'Volume'")) {
804 pa_log_warn(DBUS_INTERFACE_PROPERTIES ".Get %s Volume property not (yet) available",
805 dbus_message_get_path(p->message));
806 } else {
807 pa_log_error(DBUS_INTERFACE_PROPERTIES ".Get %s Volume failed: %s: %s",
808 dbus_message_get_path(p->message),
809 error_name,
810 error_message);
811 }
812 goto finish;
813 }
814 dbus_message_iter_init(r, &iter);
815 pa_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_VARIANT);
816 dbus_message_iter_recurse(&iter, &variant);
817 pa_assert(dbus_message_iter_get_arg_type(&variant) == DBUS_TYPE_UINT16);
818 dbus_message_iter_get_basic(&variant, &gain);
819
820 if (gain > A2DP_MAX_GAIN)
821 gain = A2DP_MAX_GAIN;
822
823 pa_log_debug("Received A2DP Absolute Volume %d", gain);
824
825 volume = a2dp_gain_to_volume(gain);
826
827 pa_bluetooth_transport_remote_volume_changed(t, volume);
828
829 finish:
830 dbus_message_unref(r);
831
832 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
833 pa_dbus_pending_free(p);
834 }
835
bluez5_transport_get_volume(pa_bluetooth_transport * t)836 static void bluez5_transport_get_volume(pa_bluetooth_transport *t) {
837 static const char *volume_str = "Volume";
838 static const char *mediatransport_str = BLUEZ_MEDIA_TRANSPORT_INTERFACE;
839 DBusMessage *m;
840
841 pa_assert(t);
842 pa_assert(t->device);
843 pa_assert(t->device->discovery);
844
845 pa_assert(pa_bluetooth_profile_is_a2dp(t->profile));
846
847 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, t->path, DBUS_INTERFACE_PROPERTIES, "Get"));
848 pa_assert_se(dbus_message_append_args(m,
849 DBUS_TYPE_STRING, &mediatransport_str,
850 DBUS_TYPE_STRING, &volume_str,
851 DBUS_TYPE_INVALID));
852
853 send_and_add_to_pending(t->device->discovery, m, get_volume_reply, t);
854 }
855
pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport * t)856 void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t) {
857 pa_assert(t);
858 pa_assert(t->device);
859
860 if (!t->device->avrcp_absolute_volume)
861 return;
862
863 if (t->profile == PA_BLUETOOTH_PROFILE_A2DP_SINK)
864 /* A2DP Absolute Volume control (AVRCP 1.4) is optional */
865 bluez5_transport_get_volume(t);
866 }
867
a2dp_transport_write(pa_bluetooth_transport * t,int fd,const void * buffer,size_t size,size_t write_mtu)868 static ssize_t a2dp_transport_write(pa_bluetooth_transport *t, int fd, const void* buffer, size_t size, size_t write_mtu) {
869 ssize_t l = 0;
870 size_t written = 0;
871 size_t write_size;
872
873 pa_assert(t);
874
875 while (written < size) {
876 write_size = PA_MIN(size - written, write_mtu);
877 l = pa_write(fd, buffer + written, write_size, &t->stream_write_type);
878 if (l < 0)
879 break;
880 written += l;
881 }
882
883 if (l < 0) {
884 if (errno == EAGAIN) {
885 /* Hmm, apparently the socket was not writable, give up for now */
886 pa_log_debug("Got EAGAIN on write() after POLLOUT, probably there is a temporary connection loss.");
887 /* Drain write buffer */
888 written = size;
889 } else {
890 pa_log_error("Failed to write data to socket: %s", pa_cstrerror(errno));
891 /* Report error from write call */
892 return -1;
893 }
894 }
895
896 /* if too much data left discard it all */
897 if (size - written >= write_mtu) {
898 pa_log_warn("Wrote memory block to socket only partially! %lu written, discarding pending write size %lu larger than write_mtu %lu",
899 written, size, write_mtu);
900 /* Drain write buffer */
901 written = size;
902 }
903
904 return written;
905 }
906
pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device * d)907 bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
908 unsigned i;
909
910 pa_assert(d);
911
912 if (!d->valid)
913 return false;
914
915 for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++)
916 if (d->transports[i] && d->transports[i]->state != PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED)
917 return true;
918
919 return false;
920 }
921
922 /* Returns a path containing /org/pulseaudio + /bluez/hciXX */
adapter_battery_provider_path(pa_bluetooth_adapter * d)923 static char *adapter_battery_provider_path(pa_bluetooth_adapter *d) {
924 const char *devname = d->path + sizeof("/org") - 1;
925 return pa_sprintf_malloc(PULSEAUDIO_BASE_PATH "%s", devname);
926 }
927
928 /* Returns a path containing /org/pulseaudio + /bluez/hciXX/dev_XX_XX_XX_XX_XX_XX */
device_battery_provider_path(pa_bluetooth_device * d)929 static char *device_battery_provider_path(pa_bluetooth_device *d) {
930 const char *devname = d->path + sizeof("/org") - 1;
931 return pa_sprintf_malloc(PULSEAUDIO_BASE_PATH "%s", devname);
932 }
933
934 static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object);
935 static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *object, bool only_percentage);
936
pa_bluetooth_device_report_battery_level(pa_bluetooth_device * d,uint8_t level,const char * reporting_source)937 void pa_bluetooth_device_report_battery_level(pa_bluetooth_device *d, uint8_t level, const char *reporting_source) {
938 bool had_battery_provider = d->has_battery_level;
939 d->has_battery_level = true;
940 d->battery_level = level;
941 pa_assert_se(d->battery_source = reporting_source);
942
943 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED], d);
944
945 if (!had_battery_provider) {
946 DBusMessage *m;
947 DBusMessageIter iter;
948 char *provider_path;
949
950 if (!d->adapter->battery_provider_registered) {
951 pa_log_debug("No battery provider registered on adapter of %s", d->path);
952 return;
953 }
954
955 provider_path = adapter_battery_provider_path(d->adapter);
956
957 pa_log_debug("Registering new battery for %s with level %d", d->path, level);
958
959 pa_assert_se(m = dbus_message_new_signal(provider_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"));
960 dbus_message_iter_init_append(m, &iter);
961 append_battery_provider(d, &iter);
962 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
963
964 pa_xfree(provider_path);
965 } else {
966 DBusMessage *m;
967 DBusMessageIter iter;
968 char *battery_path = device_battery_provider_path(d);
969
970 pa_log_debug("Notifying battery Percentage for %s changed %d", battery_path, level);
971
972 pa_assert_se(m = dbus_message_new_signal(battery_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"));
973 dbus_message_iter_init_append(m, &iter);
974 append_battery_provider_properties(d, &iter, true);
975 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
976 pa_xfree(battery_path);
977 }
978 }
979
980 /* Notify BlueZ that we're no longer providing battery info for this device */
pa_bluetooth_device_deregister_battery(pa_bluetooth_device * d)981 void pa_bluetooth_device_deregister_battery(pa_bluetooth_device *d) {
982 static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
983 DBusMessage *m;
984 DBusMessageIter iter, array;
985 char *battery_path, *provider_path;
986
987 if (!d->has_battery_level)
988 return;
989
990 d->has_battery_level = false;
991 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED], d);
992
993 if (!d->adapter->battery_provider_registered)
994 return;
995
996 battery_path = device_battery_provider_path(d);
997 provider_path = adapter_battery_provider_path(d->adapter);
998
999 pa_log_debug("Deregistering battery provider %s", battery_path);
1000
1001 pa_assert_se(m = dbus_message_new_signal(provider_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved"));
1002 dbus_message_iter_init_append(m, &iter);
1003 pa_assert_se(dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &battery_path));
1004 pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING_AS_STRING, &array));
1005 pa_assert_se(dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &interface_name));
1006 pa_assert_se(dbus_message_iter_close_container(&iter, &array));
1007
1008 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
1009 d->has_battery_level = false;
1010
1011 pa_xfree(battery_path);
1012 pa_xfree(provider_path);
1013 }
1014
1015
transport_state_from_string(const char * value,pa_bluetooth_transport_state_t * state)1016 static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
1017 pa_assert(value);
1018 pa_assert(state);
1019
1020 if (pa_streq(value, "idle"))
1021 *state = PA_BLUETOOTH_TRANSPORT_STATE_IDLE;
1022 else if (pa_streq(value, "pending") || pa_streq(value, "active"))
1023 *state = PA_BLUETOOTH_TRANSPORT_STATE_PLAYING;
1024 else
1025 return -1;
1026
1027 return 0;
1028 }
1029
parse_transport_property(pa_bluetooth_transport * t,DBusMessageIter * i)1030 static void parse_transport_property(pa_bluetooth_transport *t, DBusMessageIter *i) {
1031 const char *key;
1032 DBusMessageIter variant_i;
1033
1034 key = check_variant_property(i);
1035 if (key == NULL)
1036 return;
1037
1038 pa_log_debug("Transport property %s changed", key);
1039
1040 dbus_message_iter_recurse(i, &variant_i);
1041
1042 switch (dbus_message_iter_get_arg_type(&variant_i)) {
1043
1044 case DBUS_TYPE_STRING: {
1045
1046 const char *value;
1047 dbus_message_iter_get_basic(&variant_i, &value);
1048
1049 if (pa_streq(key, "State")) {
1050 pa_bluetooth_transport_state_t state;
1051
1052 if (transport_state_from_string(value, &state) < 0) {
1053 pa_log_error("Invalid state received: %s", value);
1054 return;
1055 }
1056
1057 pa_bluetooth_transport_set_state(t, state);
1058 }
1059
1060 break;
1061 }
1062
1063 case DBUS_TYPE_UINT16: {
1064 uint16_t value;
1065 dbus_message_iter_get_basic(&variant_i, &value);
1066
1067 if (pa_streq(key, "Volume")) {
1068 pa_volume_t volume = a2dp_gain_to_volume(value);
1069 pa_bluetooth_transport_remote_volume_changed(t, volume);
1070 }
1071 break;
1072 }
1073 }
1074
1075 return;
1076 }
1077
parse_transport_properties(pa_bluetooth_transport * t,DBusMessageIter * i)1078 static int parse_transport_properties(pa_bluetooth_transport *t, DBusMessageIter *i) {
1079 DBusMessageIter element_i;
1080
1081 dbus_message_iter_recurse(i, &element_i);
1082
1083 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1084 DBusMessageIter dict_i;
1085
1086 dbus_message_iter_recurse(&element_i, &dict_i);
1087
1088 parse_transport_property(t, &dict_i);
1089
1090 dbus_message_iter_next(&element_i);
1091 }
1092
1093 return 0;
1094 }
1095
pa_a2dp_codec_id_hash_func(const void * _p)1096 static unsigned pa_a2dp_codec_id_hash_func(const void *_p) {
1097 unsigned hash;
1098 const pa_a2dp_codec_id *p = _p;
1099
1100 hash = p->codec_id;
1101 hash = 31 * hash + ((p->vendor_id >> 0) & 0xFF);
1102 hash = 31 * hash + ((p->vendor_id >> 8) & 0xFF);
1103 hash = 31 * hash + ((p->vendor_id >> 16) & 0xFF);
1104 hash = 31 * hash + ((p->vendor_id >> 24) & 0xFF);
1105 hash = 31 * hash + ((p->vendor_codec_id >> 0) & 0xFF);
1106 hash = 31 * hash + ((p->vendor_codec_id >> 8) & 0xFF);
1107 return hash;
1108 }
1109
pa_a2dp_codec_id_compare_func(const void * _a,const void * _b)1110 static int pa_a2dp_codec_id_compare_func(const void *_a, const void *_b) {
1111 const pa_a2dp_codec_id *a = _a;
1112 const pa_a2dp_codec_id *b = _b;
1113
1114 if (a->codec_id < b->codec_id)
1115 return -1;
1116 if (a->codec_id > b->codec_id)
1117 return 1;
1118
1119 if (a->vendor_id < b->vendor_id)
1120 return -1;
1121 if (a->vendor_id > b->vendor_id)
1122 return 1;
1123
1124 if (a->vendor_codec_id < b->vendor_codec_id)
1125 return -1;
1126 if (a->vendor_codec_id > b->vendor_codec_id)
1127 return 1;
1128
1129 return 0;
1130 }
1131
remote_endpoint_remove(pa_bluetooth_discovery * y,const char * path)1132 static void remote_endpoint_remove(pa_bluetooth_discovery *y, const char *path) {
1133 pa_bluetooth_device *device;
1134 pa_hashmap *endpoints;
1135 void *devices_state;
1136 void *state;
1137
1138 PA_HASHMAP_FOREACH(device, y->devices, devices_state) {
1139 PA_HASHMAP_FOREACH(endpoints, device->a2dp_sink_endpoints, state)
1140 pa_hashmap_remove_and_free(endpoints, path);
1141
1142 PA_HASHMAP_FOREACH(endpoints, device->a2dp_source_endpoints, state)
1143 pa_hashmap_remove_and_free(endpoints, path);
1144 }
1145
1146 pa_log_debug("Remote endpoint %s was removed", path);
1147 }
1148
device_create(pa_bluetooth_discovery * y,const char * path)1149 static pa_bluetooth_device* device_create(pa_bluetooth_discovery *y, const char *path) {
1150 pa_bluetooth_device *d;
1151
1152 pa_assert(y);
1153 pa_assert(path);
1154
1155 d = pa_xnew0(pa_bluetooth_device, 1);
1156 d->discovery = y;
1157 d->enable_hfp_hf = pa_bluetooth_discovery_get_enable_native_hfp_hf(y);
1158 d->path = pa_xstrdup(path);
1159 d->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1160 d->a2dp_sink_endpoints = pa_hashmap_new_full(pa_a2dp_codec_id_hash_func, pa_a2dp_codec_id_compare_func, pa_xfree, (pa_free_cb_t)pa_hashmap_free);
1161 d->a2dp_source_endpoints = pa_hashmap_new_full(pa_a2dp_codec_id_hash_func, pa_a2dp_codec_id_compare_func, pa_xfree, (pa_free_cb_t)pa_hashmap_free);
1162
1163 pa_hashmap_put(y->devices, d->path, d);
1164
1165 return d;
1166 }
1167
pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery * y,const char * path)1168 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path) {
1169 pa_bluetooth_device *d;
1170
1171 pa_assert(y);
1172 pa_assert(PA_REFCNT_VALUE(y) > 0);
1173 pa_assert(path);
1174
1175 if ((d = pa_hashmap_get(y->devices, path)) && d->valid)
1176 return d;
1177
1178 return NULL;
1179 }
1180
pa_bluetooth_discovery_get_enable_native_hsp_hs(pa_bluetooth_discovery * y)1181 bool pa_bluetooth_discovery_get_enable_native_hsp_hs(pa_bluetooth_discovery *y)
1182 {
1183 pa_assert(y);
1184 pa_assert(PA_REFCNT_VALUE(y) > 0);
1185
1186 return y->enable_native_hsp_hs;
1187 }
1188
pa_bluetooth_discovery_get_enable_native_hfp_hf(pa_bluetooth_discovery * y)1189 bool pa_bluetooth_discovery_get_enable_native_hfp_hf(pa_bluetooth_discovery *y)
1190 {
1191 pa_assert(y);
1192 pa_assert(PA_REFCNT_VALUE(y) > 0);
1193
1194 return y->enable_native_hfp_hf;
1195 }
1196
pa_bluetooth_discovery_get_enable_msbc(pa_bluetooth_discovery * y)1197 bool pa_bluetooth_discovery_get_enable_msbc(pa_bluetooth_discovery *y)
1198 {
1199 pa_assert(y);
1200 pa_assert(PA_REFCNT_VALUE(y) > 0);
1201
1202 return y->enable_msbc;
1203 }
1204
pa_bluetooth_discovery_get_transports(pa_bluetooth_discovery * y)1205 pa_hashmap* pa_bluetooth_discovery_get_transports(pa_bluetooth_discovery *y) {
1206 pa_assert(y);
1207 pa_assert(PA_REFCNT_VALUE(y) > 0);
1208
1209 return y->transports;
1210 }
1211
pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery * y,const char * remote,const char * local)1212 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
1213 pa_bluetooth_device *d;
1214 void *state = NULL;
1215
1216 pa_assert(y);
1217 pa_assert(PA_REFCNT_VALUE(y) > 0);
1218 pa_assert(remote);
1219 pa_assert(local);
1220
1221 while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
1222 if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
1223 return d;
1224
1225 return NULL;
1226 }
1227
device_free(pa_bluetooth_device * d)1228 static void device_free(pa_bluetooth_device *d) {
1229 unsigned i;
1230
1231 pa_assert(d);
1232
1233 device_stop_waiting_for_profiles(d);
1234
1235 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UNLINK], d);
1236
1237 for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
1238 pa_bluetooth_transport *t;
1239
1240 if (!(t = d->transports[i]))
1241 continue;
1242
1243 pa_bluetooth_transport_free(t);
1244 }
1245
1246 if (d->uuids)
1247 pa_hashmap_free(d->uuids);
1248 if (d->a2dp_sink_endpoints)
1249 pa_hashmap_free(d->a2dp_sink_endpoints);
1250 if (d->a2dp_source_endpoints)
1251 pa_hashmap_free(d->a2dp_source_endpoints);
1252
1253 pa_xfree(d->path);
1254 pa_xfree(d->alias);
1255 pa_xfree(d->address);
1256 pa_xfree(d->adapter_path);
1257 pa_xfree(d);
1258 }
1259
device_remove(pa_bluetooth_discovery * y,const char * path)1260 static void device_remove(pa_bluetooth_discovery *y, const char *path) {
1261 pa_bluetooth_device *d;
1262
1263 if (!(d = pa_hashmap_remove(y->devices, path)))
1264 pa_log_warn("Unknown device removed %s", path);
1265 else {
1266 pa_log_debug("Device %s removed", path);
1267 device_free(d);
1268 }
1269 }
1270
device_set_valid(pa_bluetooth_device * device,bool valid)1271 static void device_set_valid(pa_bluetooth_device *device, bool valid) {
1272 bool old_any_connected;
1273
1274 pa_assert(device);
1275
1276 if (valid == device->valid)
1277 return;
1278
1279 old_any_connected = pa_bluetooth_device_any_transport_connected(device);
1280 device->valid = valid;
1281
1282 if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
1283 pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
1284 }
1285
device_update_valid(pa_bluetooth_device * d)1286 static void device_update_valid(pa_bluetooth_device *d) {
1287 pa_assert(d);
1288
1289 if (!d->properties_received) {
1290 pa_assert(!d->valid);
1291 return;
1292 }
1293
1294 /* Check if mandatory properties are set. */
1295 if (!d->address || !d->adapter_path || !d->alias) {
1296 device_set_valid(d, false);
1297 return;
1298 }
1299
1300 if (!d->adapter || !d->adapter->valid) {
1301 device_set_valid(d, false);
1302 return;
1303 }
1304
1305 device_set_valid(d, true);
1306 }
1307
device_set_adapter(pa_bluetooth_device * device,pa_bluetooth_adapter * adapter)1308 static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
1309 pa_assert(device);
1310
1311 if (adapter == device->adapter)
1312 return;
1313
1314 device->adapter = adapter;
1315
1316 device_update_valid(device);
1317 }
1318
append_battery_provider_properties(pa_bluetooth_device * d,DBusMessageIter * entry,bool only_percentage)1319 static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *entry, bool only_percentage) {
1320 static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
1321 DBusMessageIter dict;
1322
1323 pa_assert_se(dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &interface_name));
1324
1325 pa_assert_se(dbus_message_iter_open_container(entry, DBUS_TYPE_ARRAY,
1326 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1327 DBUS_TYPE_STRING_AS_STRING
1328 DBUS_TYPE_VARIANT_AS_STRING
1329 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1330 &dict));
1331
1332 pa_dbus_append_basic_variant_dict_entry(&dict, "Percentage", DBUS_TYPE_BYTE, &d->battery_level);
1333
1334 if (!only_percentage) {
1335 pa_assert(d->battery_source);
1336 pa_dbus_append_basic_variant_dict_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &d->path);
1337 pa_dbus_append_basic_variant_dict_entry(&dict, "Source", DBUS_TYPE_STRING, &d->battery_source);
1338 }
1339
1340 pa_assert_se(dbus_message_iter_close_container(entry, &dict));
1341 }
1342
append_battery_provider(pa_bluetooth_device * d,DBusMessageIter * object)1343 static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object) {
1344 char *battery_path = device_battery_provider_path(d);
1345 DBusMessageIter array, entry;
1346
1347 pa_assert_se(dbus_message_iter_append_basic(object, DBUS_TYPE_OBJECT_PATH, &battery_path));
1348
1349 pa_assert_se(dbus_message_iter_open_container(object, DBUS_TYPE_ARRAY,
1350 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1351 DBUS_TYPE_STRING_AS_STRING
1352 DBUS_TYPE_ARRAY_AS_STRING
1353 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1354 DBUS_TYPE_STRING_AS_STRING
1355 DBUS_TYPE_VARIANT_AS_STRING
1356 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1357 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1358 &array));
1359
1360 pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry));
1361 append_battery_provider_properties(d, &entry, false);
1362 pa_assert_se(dbus_message_iter_close_container(&array, &entry));
1363 pa_assert_se(dbus_message_iter_close_container(object, &array));
1364
1365 pa_xfree(battery_path);
1366 }
1367
battery_provider_handler(DBusConnection * c,DBusMessage * m,void * userdata)1368 static DBusHandlerResult battery_provider_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1369 pa_bluetooth_adapter *a = userdata;
1370 DBusMessage *r = NULL;
1371 const char *path, *interface, *member;
1372
1373 pa_assert(a);
1374
1375 path = dbus_message_get_path(m);
1376 interface = dbus_message_get_interface(m);
1377 member = dbus_message_get_member(m);
1378
1379 pa_log_debug("%s %s %s", path, interface, member);
1380
1381 if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
1382 DBusMessageIter iter, array, object;
1383 pa_bluetooth_device *d;
1384 void *state;
1385
1386 pa_assert_se(r = dbus_message_new_method_return(m));
1387
1388 dbus_message_iter_init_append(r, &iter);
1389 pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
1390 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1391 DBUS_TYPE_OBJECT_PATH_AS_STRING
1392 DBUS_TYPE_ARRAY_AS_STRING
1393 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1394 DBUS_TYPE_STRING_AS_STRING
1395 DBUS_TYPE_ARRAY_AS_STRING
1396 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1397 DBUS_TYPE_STRING_AS_STRING
1398 DBUS_TYPE_VARIANT_AS_STRING
1399 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1400 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1401 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1402 &array));
1403
1404 PA_HASHMAP_FOREACH(d, a->discovery->devices, state) {
1405
1406 if (d->has_battery_level) {
1407 pa_log_debug("%s: battery level = %d", d->path, d->battery_level);
1408 pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &object));
1409 append_battery_provider(d, &object);
1410 pa_assert_se(dbus_message_iter_close_container(&array, &object));
1411 }
1412 }
1413
1414 pa_assert_se(dbus_message_iter_close_container(&iter, &array));
1415 } else
1416 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1417
1418 pa_assert_se(dbus_connection_send(c, r, NULL));
1419 dbus_message_unref(r);
1420
1421 return DBUS_HANDLER_RESULT_HANDLED;
1422 }
1423
adapter_register_battery_provider(pa_bluetooth_adapter * a)1424 static void adapter_register_battery_provider(pa_bluetooth_adapter *a) {
1425 DBusMessage *m, *r;
1426 DBusError error;
1427
1428 static const DBusObjectPathVTable vtable_profile = {
1429 .message_function = battery_provider_handler,
1430 };
1431
1432 char *provider_path = adapter_battery_provider_path(a);
1433
1434 pa_log_debug("Registering battery provider for %s at %s", a->path, provider_path);
1435
1436 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path, &vtable_profile, a));
1437
1438 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "RegisterBatteryProvider"));
1439 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1440
1441 dbus_error_init(&error);
1442 if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1443 if (dbus_error_has_name(&error, DBUS_ERROR_UNKNOWN_METHOD))
1444 pa_log_notice("Could not find " BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE
1445 ".RegisterBatteryProvider(), is bluetoothd started with experimental features enabled (-E flag)?");
1446 else
1447 pa_log_warn(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".RegisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1448 dbus_error_free(&error);
1449 dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1450 } else {
1451 dbus_message_unref(r);
1452 a->battery_provider_registered = true;
1453 }
1454
1455 dbus_message_unref(m);
1456 pa_xfree(provider_path);
1457 }
1458
adapter_deregister_battery_provider(pa_bluetooth_adapter * a)1459 static void adapter_deregister_battery_provider(pa_bluetooth_adapter *a) {
1460 DBusMessage *m, *r;
1461 DBusError error;
1462 char *provider_path;
1463
1464 if (!a->battery_provider_registered) {
1465 pa_log_debug("No battery provider registered for %s", a->path);
1466 return;
1467 }
1468
1469 provider_path = adapter_battery_provider_path(a);
1470
1471 pa_log_debug("Deregistering battery provider at %s", provider_path);
1472
1473 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "UnregisterBatteryProvider"));
1474 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1475
1476 dbus_error_init(&error);
1477 if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1478 pa_log_error(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".UnregisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1479 dbus_error_free(&error);
1480 } else {
1481 dbus_message_unref(r);
1482 a->battery_provider_registered = false;
1483 }
1484
1485 dbus_message_unref(m);
1486
1487 dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1488
1489 pa_xfree(provider_path);
1490 }
1491
adapter_create(pa_bluetooth_discovery * y,const char * path)1492 static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
1493 pa_bluetooth_adapter *a;
1494
1495 pa_assert(y);
1496 pa_assert(path);
1497
1498 a = pa_xnew0(pa_bluetooth_adapter, 1);
1499 a->discovery = y;
1500 a->path = pa_xstrdup(path);
1501 a->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1502
1503 pa_hashmap_put(y->adapters, a->path, a);
1504
1505 return a;
1506 }
1507
adapter_free(pa_bluetooth_adapter * a)1508 static void adapter_free(pa_bluetooth_adapter *a) {
1509 pa_bluetooth_device *d;
1510 void *state;
1511
1512 pa_assert(a);
1513 pa_assert(a->discovery);
1514
1515 adapter_deregister_battery_provider(a);
1516
1517 PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
1518 if (d->adapter == a)
1519 device_set_adapter(d, NULL);
1520
1521 pa_hashmap_free(a->uuids);
1522 pa_xfree(a->path);
1523 pa_xfree(a->address);
1524 pa_xfree(a);
1525 }
1526
adapter_remove(pa_bluetooth_discovery * y,const char * path)1527 static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
1528 pa_bluetooth_adapter *a;
1529
1530 if (!(a = pa_hashmap_remove(y->adapters, path)))
1531 pa_log_warn("Unknown adapter removed %s", path);
1532 else {
1533 pa_log_debug("Adapter %s removed", path);
1534 adapter_free(a);
1535 }
1536 }
1537
parse_device_property(pa_bluetooth_device * d,DBusMessageIter * i)1538 static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
1539 const char *key;
1540 DBusMessageIter variant_i;
1541
1542 pa_assert(d);
1543
1544 key = check_variant_property(i);
1545 if (key == NULL) {
1546 pa_log_error("Received invalid property for device %s", d->path);
1547 return;
1548 }
1549
1550 dbus_message_iter_recurse(i, &variant_i);
1551
1552 switch (dbus_message_iter_get_arg_type(&variant_i)) {
1553
1554 case DBUS_TYPE_STRING: {
1555 const char *value;
1556 dbus_message_iter_get_basic(&variant_i, &value);
1557
1558 if (pa_streq(key, "Alias")) {
1559 pa_xfree(d->alias);
1560 d->alias = pa_xstrdup(value);
1561 pa_log_debug("%s: %s", key, value);
1562 } else if (pa_streq(key, "Address")) {
1563 if (d->properties_received) {
1564 pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
1565 return;
1566 }
1567
1568 if (d->address) {
1569 pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
1570 return;
1571 }
1572
1573 d->address = pa_xstrdup(value);
1574 pa_log_debug("%s: %s", key, value);
1575 }
1576
1577 break;
1578 }
1579
1580 case DBUS_TYPE_OBJECT_PATH: {
1581 const char *value;
1582 dbus_message_iter_get_basic(&variant_i, &value);
1583
1584 if (pa_streq(key, "Adapter")) {
1585
1586 if (d->properties_received) {
1587 pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
1588 return;
1589 }
1590
1591 if (d->adapter_path) {
1592 pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
1593 return;
1594 }
1595
1596 d->adapter_path = pa_xstrdup(value);
1597 pa_log_debug("%s: %s", key, value);
1598 }
1599
1600 break;
1601 }
1602
1603 case DBUS_TYPE_UINT32: {
1604 uint32_t value;
1605 dbus_message_iter_get_basic(&variant_i, &value);
1606
1607 if (pa_streq(key, "Class")) {
1608 d->class_of_device = value;
1609 pa_log_debug("%s: %d", key, value);
1610 }
1611
1612 break;
1613 }
1614
1615 case DBUS_TYPE_ARRAY: {
1616 DBusMessageIter ai;
1617 dbus_message_iter_recurse(&variant_i, &ai);
1618
1619 if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1620 /* bluetoothd never removes UUIDs from a device object so we
1621 * don't need to check for disappeared UUIDs here. */
1622 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1623 const char *value;
1624 char *uuid;
1625
1626 dbus_message_iter_get_basic(&ai, &value);
1627
1628 if (pa_hashmap_get(d->uuids, value)) {
1629 dbus_message_iter_next(&ai);
1630 continue;
1631 }
1632
1633 uuid = pa_xstrdup(value);
1634 pa_hashmap_put(d->uuids, uuid, uuid);
1635
1636 pa_log_debug("%s: %s", key, value);
1637 dbus_message_iter_next(&ai);
1638 }
1639 }
1640
1641 break;
1642 }
1643 }
1644 }
1645
parse_device_properties(pa_bluetooth_device * d,DBusMessageIter * i)1646 static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
1647 DBusMessageIter element_i;
1648
1649 dbus_message_iter_recurse(i, &element_i);
1650
1651 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1652 DBusMessageIter dict_i;
1653
1654 dbus_message_iter_recurse(&element_i, &dict_i);
1655 parse_device_property(d, &dict_i);
1656 dbus_message_iter_next(&element_i);
1657 }
1658
1659 if (!d->properties_received) {
1660 d->properties_received = true;
1661 device_update_valid(d);
1662
1663 if (!d->address || !d->adapter_path || !d->alias)
1664 pa_log_error("Non-optional information missing for device %s", d->path);
1665 }
1666 }
1667
parse_adapter_properties(pa_bluetooth_adapter * a,DBusMessageIter * i,bool is_property_change)1668 static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
1669 DBusMessageIter element_i;
1670
1671 pa_assert(a);
1672
1673 dbus_message_iter_recurse(i, &element_i);
1674
1675 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1676 DBusMessageIter dict_i, variant_i;
1677 const char *key;
1678
1679 dbus_message_iter_recurse(&element_i, &dict_i);
1680
1681 key = check_variant_property(&dict_i);
1682 if (key == NULL) {
1683 pa_log_error("Received invalid property for adapter %s", a->path);
1684 return;
1685 }
1686
1687 dbus_message_iter_recurse(&dict_i, &variant_i);
1688
1689 if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
1690 const char *value;
1691
1692 if (is_property_change) {
1693 pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
1694 return;
1695 }
1696
1697 if (a->address) {
1698 pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
1699 return;
1700 }
1701
1702 dbus_message_iter_get_basic(&variant_i, &value);
1703 a->address = pa_xstrdup(value);
1704 a->valid = true;
1705 } else if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_ARRAY) {
1706 DBusMessageIter ai;
1707 dbus_message_iter_recurse(&variant_i, &ai);
1708
1709 if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1710 pa_hashmap_remove_all(a->uuids);
1711 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1712 const char *value;
1713 char *uuid;
1714
1715 dbus_message_iter_get_basic(&ai, &value);
1716
1717 if (pa_hashmap_get(a->uuids, value)) {
1718 dbus_message_iter_next(&ai);
1719 continue;
1720 }
1721
1722 uuid = pa_xstrdup(value);
1723 pa_hashmap_put(a->uuids, uuid, uuid);
1724
1725 pa_log_debug("%s: %s", key, value);
1726 dbus_message_iter_next(&ai);
1727 }
1728 pa_hook_fire(pa_bluetooth_discovery_hook(a->discovery, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), a);
1729 }
1730 }
1731
1732 dbus_message_iter_next(&element_i);
1733 }
1734 }
1735
register_legacy_sbc_endpoint_reply(DBusPendingCall * pending,void * userdata)1736 static void register_legacy_sbc_endpoint_reply(DBusPendingCall *pending, void *userdata) {
1737 DBusMessage *r;
1738 pa_dbus_pending *p;
1739 pa_bluetooth_discovery *y;
1740 char *endpoint;
1741
1742 pa_assert(pending);
1743 pa_assert_se(p = userdata);
1744 pa_assert_se(y = p->context_data);
1745 pa_assert_se(endpoint = p->call_data);
1746 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1747
1748 if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1749 pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
1750 goto finish;
1751 }
1752
1753 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1754 pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
1755 pa_dbus_get_error_message(r));
1756 goto finish;
1757 }
1758
1759 finish:
1760 dbus_message_unref(r);
1761
1762 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1763 pa_dbus_pending_free(p);
1764
1765 pa_xfree(endpoint);
1766 }
1767
register_legacy_sbc_endpoint(pa_bluetooth_discovery * y,const pa_a2dp_endpoint_conf * endpoint_conf,const char * path,const char * endpoint,const char * uuid)1768 static void register_legacy_sbc_endpoint(pa_bluetooth_discovery *y, const pa_a2dp_endpoint_conf *endpoint_conf, const char *path, const char *endpoint, const char *uuid) {
1769 DBusMessage *m;
1770 DBusMessageIter i, d;
1771 uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
1772 size_t capabilities_size;
1773 uint8_t codec_id;
1774
1775 pa_log_debug("Registering %s on adapter %s", endpoint, path);
1776
1777 codec_id = endpoint_conf->id.codec_id;
1778 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
1779 pa_assert(capabilities_size != 0);
1780
1781 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
1782
1783 dbus_message_iter_init_append(m, &i);
1784 pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
1785 dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1786 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1787 DBUS_TYPE_STRING_AS_STRING
1788 DBUS_TYPE_VARIANT_AS_STRING
1789 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1790 &d);
1791 pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
1792 pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
1793 pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
1794
1795 dbus_message_iter_close_container(&i, &d);
1796
1797 send_and_add_to_pending(y, m, register_legacy_sbc_endpoint_reply, pa_xstrdup(endpoint));
1798 }
1799
register_application_reply(DBusPendingCall * pending,void * userdata)1800 static void register_application_reply(DBusPendingCall *pending, void *userdata) {
1801 DBusMessage *r;
1802 pa_dbus_pending *p;
1803 pa_bluetooth_adapter *a;
1804 pa_bluetooth_discovery *y;
1805 char *path;
1806 bool fallback = true;
1807
1808 pa_assert(pending);
1809 pa_assert_se(p = userdata);
1810 pa_assert_se(y = p->context_data);
1811 pa_assert_se(path = p->call_data);
1812 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1813
1814 if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1815 pa_log_info("Couldn't register media application for adapter %s because it is disabled in BlueZ", path);
1816 goto finish;
1817 }
1818
1819 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1820 pa_log_warn(BLUEZ_MEDIA_INTERFACE ".RegisterApplication() failed: %s: %s",
1821 dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1822 pa_log_warn("Couldn't register media application for adapter %s", path);
1823 goto finish;
1824 }
1825
1826 a = pa_hashmap_get(y->adapters, path);
1827 if (!a) {
1828 pa_log_error("Couldn't register media application for adapter %s because it does not exist anymore", path);
1829 goto finish;
1830 }
1831
1832 fallback = false;
1833 a->application_registered = true;
1834 pa_log_debug("Media application for adapter %s was successfully registered", path);
1835
1836 finish:
1837 dbus_message_unref(r);
1838
1839 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1840 pa_dbus_pending_free(p);
1841
1842 if (fallback) {
1843 /* If bluez does not support RegisterApplication, fallback to old legacy API with just one SBC codec */
1844 const pa_a2dp_endpoint_conf *endpoint_conf;
1845 endpoint_conf = pa_bluetooth_get_a2dp_endpoint_conf("sbc");
1846 pa_assert(endpoint_conf);
1847 register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SINK_ENDPOINT "/sbc",
1848 PA_BLUETOOTH_UUID_A2DP_SINK);
1849 register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SOURCE_ENDPOINT "/sbc",
1850 PA_BLUETOOTH_UUID_A2DP_SOURCE);
1851 pa_log_warn("Only SBC codec is available for A2DP profiles");
1852 }
1853
1854 pa_xfree(path);
1855 }
1856
register_application(pa_bluetooth_adapter * a)1857 static void register_application(pa_bluetooth_adapter *a) {
1858 DBusMessage *m;
1859 DBusMessageIter i, d;
1860 const char *object_manager_path = A2DP_OBJECT_MANAGER_PATH;
1861
1862 if (a->application_registered) {
1863 pa_log_info("Media application is already registered for adapter %s", a->path);
1864 return;
1865 }
1866
1867 pa_log_debug("Registering media application for adapter %s", a->path);
1868
1869 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path,
1870 BLUEZ_MEDIA_INTERFACE, "RegisterApplication"));
1871
1872 dbus_message_iter_init_append(m, &i);
1873 pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object_manager_path));
1874 dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1875 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1876 DBUS_TYPE_STRING_AS_STRING
1877 DBUS_TYPE_VARIANT_AS_STRING
1878 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1879 &d);
1880 dbus_message_iter_close_container(&i, &d);
1881
1882 send_and_add_to_pending(a->discovery, m, register_application_reply, pa_xstrdup(a->path));
1883 }
1884
parse_remote_endpoint_properties(pa_bluetooth_discovery * y,const char * endpoint,DBusMessageIter * i)1885 static void parse_remote_endpoint_properties(pa_bluetooth_discovery *y, const char *endpoint, DBusMessageIter *i) {
1886 DBusMessageIter element_i;
1887 pa_bluetooth_device *device;
1888 pa_hashmap *codec_endpoints;
1889 pa_hashmap *endpoints;
1890 pa_a2dp_codec_id *a2dp_codec_id;
1891 pa_a2dp_codec_capabilities *a2dp_codec_capabilities;
1892 const char *uuid = NULL;
1893 const char *device_path = NULL;
1894 uint8_t codec_id = 0;
1895 bool have_codec_id = false;
1896 const uint8_t *capabilities = NULL;
1897 int capabilities_size = 0;
1898
1899 pa_log_debug("Parsing remote endpoint %s", endpoint);
1900
1901 dbus_message_iter_recurse(i, &element_i);
1902
1903 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1904 DBusMessageIter dict_i, variant_i;
1905 const char *key;
1906
1907 dbus_message_iter_recurse(&element_i, &dict_i);
1908
1909 key = check_variant_property(&dict_i);
1910 if (key == NULL) {
1911 pa_log_error("Received invalid property for remote endpoint %s", endpoint);
1912 return;
1913 }
1914
1915 dbus_message_iter_recurse(&dict_i, &variant_i);
1916
1917 if (pa_streq(key, "UUID")) {
1918 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_STRING) {
1919 pa_log_warn("Remote endpoint %s property 'UUID' is not string, ignoring", endpoint);
1920 return;
1921 }
1922
1923 dbus_message_iter_get_basic(&variant_i, &uuid);
1924 } else if (pa_streq(key, "Codec")) {
1925 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BYTE) {
1926 pa_log_warn("Remote endpoint %s property 'Codec' is not byte, ignoring", endpoint);
1927 return;
1928 }
1929
1930 dbus_message_iter_get_basic(&variant_i, &codec_id);
1931 have_codec_id = true;
1932 } else if (pa_streq(key, "Capabilities")) {
1933 DBusMessageIter array;
1934
1935 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_ARRAY) {
1936 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array, ignoring", endpoint);
1937 return;
1938 }
1939
1940 dbus_message_iter_recurse(&variant_i, &array);
1941 if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) {
1942 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array of bytes, ignoring", endpoint);
1943 return;
1944 }
1945
1946 dbus_message_iter_get_fixed_array(&array, &capabilities, &capabilities_size);
1947 } else if (pa_streq(key, "Device")) {
1948 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_OBJECT_PATH) {
1949 pa_log_warn("Remote endpoint %s property 'Device' is not path, ignoring", endpoint);
1950 return;
1951 }
1952
1953 dbus_message_iter_get_basic(&variant_i, &device_path);
1954 }
1955
1956 dbus_message_iter_next(&element_i);
1957 }
1958
1959 if (!uuid) {
1960 pa_log_warn("Remote endpoint %s does not have property 'UUID', ignoring", endpoint);
1961 return;
1962 }
1963
1964 if (!have_codec_id) {
1965 pa_log_warn("Remote endpoint %s does not have property 'Codec', ignoring", endpoint);
1966 return;
1967 }
1968
1969 if (!capabilities || !capabilities_size) {
1970 pa_log_warn("Remote endpoint %s does not have property 'Capabilities', ignoring", endpoint);
1971 return;
1972 }
1973
1974 if (!device_path) {
1975 pa_log_warn("Remote endpoint %s does not have property 'Device', ignoring", endpoint);
1976 return;
1977 }
1978
1979 device = pa_hashmap_get(y->devices, device_path);
1980 if (!device) {
1981 pa_log_warn("Device for remote endpoint %s was not found", endpoint);
1982 return;
1983 }
1984
1985 if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
1986 codec_endpoints = device->a2dp_sink_endpoints;
1987 } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) {
1988 codec_endpoints = device->a2dp_source_endpoints;
1989 } else {
1990 pa_log_warn("Remote endpoint %s does not have valid property 'UUID', ignoring", endpoint);
1991 return;
1992 }
1993
1994 if (capabilities_size < 0 || capabilities_size > MAX_A2DP_CAPS_SIZE) {
1995 pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
1996 return;
1997 }
1998
1999 a2dp_codec_id = pa_xmalloc0(sizeof(*a2dp_codec_id));
2000 a2dp_codec_id->codec_id = codec_id;
2001 if (codec_id == A2DP_CODEC_VENDOR) {
2002 if ((size_t)capabilities_size < sizeof(a2dp_vendor_codec_t)) {
2003 pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
2004 pa_xfree(a2dp_codec_id);
2005 return;
2006 }
2007 a2dp_codec_id->vendor_id = A2DP_GET_VENDOR_ID(*(a2dp_vendor_codec_t *)capabilities);
2008 a2dp_codec_id->vendor_codec_id = A2DP_GET_CODEC_ID(*(a2dp_vendor_codec_t *)capabilities);
2009 } else {
2010 a2dp_codec_id->vendor_id = 0;
2011 a2dp_codec_id->vendor_codec_id = 0;
2012 }
2013
2014 if (!pa_bluetooth_a2dp_codec_is_available(a2dp_codec_id, pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))) {
2015 pa_xfree(a2dp_codec_id);
2016 return;
2017 }
2018
2019 a2dp_codec_capabilities = pa_xmalloc0(sizeof(*a2dp_codec_capabilities) + capabilities_size);
2020 a2dp_codec_capabilities->size = capabilities_size;
2021 memcpy(a2dp_codec_capabilities->buffer, capabilities, capabilities_size);
2022
2023 endpoints = pa_hashmap_get(codec_endpoints, a2dp_codec_id);
2024 if (!endpoints) {
2025 endpoints = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, pa_xfree);
2026 pa_hashmap_put(codec_endpoints, a2dp_codec_id, endpoints);
2027 }
2028
2029 if (pa_hashmap_remove_and_free(endpoints, endpoint) >= 0)
2030 pa_log_debug("Replacing existing remote endpoint %s", endpoint);
2031 pa_hashmap_put(endpoints, pa_xstrdup(endpoint), a2dp_codec_capabilities);
2032 }
2033
parse_interfaces_and_properties(pa_bluetooth_discovery * y,DBusMessageIter * dict_i)2034 static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
2035 DBusMessageIter element_i;
2036 const char *path;
2037 void *state;
2038 pa_bluetooth_device *d;
2039
2040 pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
2041 dbus_message_iter_get_basic(dict_i, &path);
2042
2043 pa_assert_se(dbus_message_iter_next(dict_i));
2044 pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
2045
2046 dbus_message_iter_recurse(dict_i, &element_i);
2047
2048 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2049 DBusMessageIter iface_i;
2050 const char *interface;
2051
2052 dbus_message_iter_recurse(&element_i, &iface_i);
2053
2054 pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
2055 dbus_message_iter_get_basic(&iface_i, &interface);
2056
2057 pa_assert_se(dbus_message_iter_next(&iface_i));
2058 pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
2059
2060 if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
2061 pa_bluetooth_adapter *a;
2062
2063 if ((a = pa_hashmap_get(y->adapters, path))) {
2064 pa_log_error("Found duplicated D-Bus path for adapter %s", path);
2065 return;
2066 } else
2067 a = adapter_create(y, path);
2068
2069 pa_log_debug("Adapter %s found", path);
2070
2071 parse_adapter_properties(a, &iface_i, false);
2072
2073 if (!a->valid)
2074 return;
2075
2076 register_application(a);
2077 adapter_register_battery_provider(a);
2078 } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
2079
2080 if ((d = pa_hashmap_get(y->devices, path))) {
2081 if (d->properties_received) {
2082 pa_log_error("Found duplicated D-Bus path for device %s", path);
2083 return;
2084 }
2085 } else
2086 d = device_create(y, path);
2087
2088 pa_log_debug("Device %s found", d->path);
2089
2090 parse_device_properties(d, &iface_i);
2091 } else if (pa_streq(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2092 parse_remote_endpoint_properties(y, path, &iface_i);
2093 } else
2094 pa_log_debug("Unknown interface %s found, skipping", interface);
2095
2096 dbus_message_iter_next(&element_i);
2097 }
2098
2099 PA_HASHMAP_FOREACH(d, y->devices, state) {
2100 if (d->properties_received && !d->tried_to_link_with_adapter) {
2101 if (d->adapter_path) {
2102 device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
2103
2104 if (!d->adapter)
2105 pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
2106 else if (!d->adapter->valid)
2107 pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
2108 }
2109
2110 d->tried_to_link_with_adapter = true;
2111 }
2112 }
2113
2114 return;
2115 }
2116
pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery * y,bool is_running)2117 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
2118 pa_assert(y);
2119
2120 pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
2121 if (y->headset_backend != HEADSET_BACKEND_AUTO)
2122 return;
2123
2124 pa_bluetooth_native_backend_enable_shared_profiles(y->native_backend, !is_running);
2125
2126 /* If ofono starts running, all devices that might be connected to the HS roles or HFP AG role
2127 * need to be disconnected, so that the devices can be handled by ofono */
2128 if (is_running) {
2129 void *state;
2130 pa_bluetooth_device *d;
2131
2132 PA_HASHMAP_FOREACH(d, y->devices, state) {
2133 if (pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_AG) || pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_HF)) {
2134 DBusMessage *m;
2135
2136 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, BLUEZ_DEVICE_INTERFACE, "Disconnect"));
2137 dbus_message_set_no_reply(m, true);
2138 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
2139 dbus_message_unref(m);
2140 }
2141 }
2142 }
2143 }
2144
get_managed_objects_reply(DBusPendingCall * pending,void * userdata)2145 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
2146 pa_dbus_pending *p;
2147 pa_bluetooth_discovery *y;
2148 DBusMessage *r;
2149 DBusMessageIter arg_i, element_i;
2150
2151 pa_assert_se(p = userdata);
2152 pa_assert_se(y = p->context_data);
2153 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
2154
2155 if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
2156 pa_log_warn("BlueZ D-Bus ObjectManager not available");
2157 goto finish;
2158 }
2159
2160 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
2161 pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
2162 goto finish;
2163 }
2164
2165 if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
2166 pa_log_error("Invalid reply signature for GetManagedObjects()");
2167 goto finish;
2168 }
2169
2170 dbus_message_iter_recurse(&arg_i, &element_i);
2171 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2172 DBusMessageIter dict_i;
2173
2174 dbus_message_iter_recurse(&element_i, &dict_i);
2175
2176 parse_interfaces_and_properties(y, &dict_i);
2177
2178 dbus_message_iter_next(&element_i);
2179 }
2180
2181 y->objects_listed = true;
2182
2183 if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
2184 y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
2185 if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
2186 y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
2187
2188 finish:
2189 dbus_message_unref(r);
2190
2191 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
2192 pa_dbus_pending_free(p);
2193 }
2194
get_managed_objects(pa_bluetooth_discovery * y)2195 static void get_managed_objects(pa_bluetooth_discovery *y) {
2196 DBusMessage *m;
2197
2198 pa_assert(y);
2199
2200 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", DBUS_INTERFACE_OBJECT_MANAGER,
2201 "GetManagedObjects"));
2202 send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
2203 }
2204
pa_bluetooth_discovery_hook(pa_bluetooth_discovery * y,pa_bluetooth_hook_t hook)2205 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
2206 pa_assert(y);
2207 pa_assert(PA_REFCNT_VALUE(y) > 0);
2208
2209 return &y->hooks[hook];
2210 }
2211
filter_cb(DBusConnection * bus,DBusMessage * m,void * userdata)2212 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
2213 pa_bluetooth_discovery *y;
2214 DBusError err;
2215
2216 pa_assert(bus);
2217 pa_assert(m);
2218 pa_assert_se(y = userdata);
2219
2220 dbus_error_init(&err);
2221
2222 if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
2223 const char *name, *old_owner, *new_owner;
2224
2225 if (!dbus_message_get_args(m, &err,
2226 DBUS_TYPE_STRING, &name,
2227 DBUS_TYPE_STRING, &old_owner,
2228 DBUS_TYPE_STRING, &new_owner,
2229 DBUS_TYPE_INVALID)) {
2230 pa_log_error("Failed to parse " DBUS_INTERFACE_DBUS ".NameOwnerChanged: %s", err.message);
2231 goto fail;
2232 }
2233
2234 if (pa_streq(name, BLUEZ_SERVICE)) {
2235 if (old_owner && *old_owner) {
2236 pa_log_debug("Bluetooth daemon disappeared");
2237 pa_hashmap_remove_all(y->devices);
2238 pa_hashmap_remove_all(y->adapters);
2239 y->objects_listed = false;
2240 if (y->ofono_backend) {
2241 pa_bluetooth_ofono_backend_free(y->ofono_backend);
2242 y->ofono_backend = NULL;
2243 }
2244 if (y->native_backend) {
2245 pa_bluetooth_native_backend_free(y->native_backend);
2246 y->native_backend = NULL;
2247 }
2248 }
2249
2250 if (new_owner && *new_owner) {
2251 pa_log_debug("Bluetooth daemon appeared");
2252 get_managed_objects(y);
2253 }
2254 }
2255
2256 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2257 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded")) {
2258 DBusMessageIter arg_i;
2259
2260 if (!y->objects_listed)
2261 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2262
2263 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
2264 pa_log_error("Invalid signature found in InterfacesAdded");
2265 goto fail;
2266 }
2267
2268 parse_interfaces_and_properties(y, &arg_i);
2269
2270 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2271 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved")) {
2272 const char *p;
2273 DBusMessageIter arg_i;
2274 DBusMessageIter element_i;
2275
2276 if (!y->objects_listed)
2277 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2278
2279 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
2280 pa_log_error("Invalid signature found in InterfacesRemoved");
2281 goto fail;
2282 }
2283
2284 dbus_message_iter_get_basic(&arg_i, &p);
2285
2286 pa_assert_se(dbus_message_iter_next(&arg_i));
2287 pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2288
2289 dbus_message_iter_recurse(&arg_i, &element_i);
2290
2291 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
2292 const char *iface;
2293
2294 dbus_message_iter_get_basic(&element_i, &iface);
2295
2296 if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
2297 device_remove(y, p);
2298 else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
2299 adapter_remove(y, p);
2300 else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
2301 remote_endpoint_remove(y, p);
2302
2303 dbus_message_iter_next(&element_i);
2304 }
2305
2306 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2307
2308 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
2309 DBusMessageIter arg_i;
2310 const char *iface;
2311
2312 if (!y->objects_listed)
2313 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2314
2315 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
2316 pa_log_error("Invalid signature found in PropertiesChanged");
2317 goto fail;
2318 }
2319
2320 dbus_message_iter_get_basic(&arg_i, &iface);
2321
2322 pa_assert_se(dbus_message_iter_next(&arg_i));
2323 pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2324
2325 if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
2326 pa_bluetooth_adapter *a;
2327
2328 pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
2329
2330 if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
2331 pa_log_warn("Properties changed in unknown adapter");
2332 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2333 }
2334
2335 parse_adapter_properties(a, &arg_i, true);
2336
2337 } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
2338 pa_bluetooth_device *d;
2339
2340 pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
2341
2342 if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
2343 pa_log_warn("Properties changed in unknown device");
2344 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2345 }
2346
2347 if (!d->properties_received)
2348 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2349
2350 parse_device_properties(d, &arg_i);
2351 } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
2352 pa_bluetooth_transport *t;
2353
2354 pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
2355
2356 if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
2357 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2358
2359 parse_transport_properties(t, &arg_i);
2360 } else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2361 pa_log_info("Properties changed in remote endpoint %s", dbus_message_get_path(m));
2362
2363 parse_remote_endpoint_properties(y, dbus_message_get_path(m), &arg_i);
2364 }
2365
2366 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2367 }
2368
2369 fail:
2370 dbus_error_free(&err);
2371
2372 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2373 }
2374
pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile)2375 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
2376 switch(profile) {
2377 case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2378 return "a2dp_sink";
2379 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2380 return "a2dp_source";
2381 case PA_BLUETOOTH_PROFILE_HSP_HS:
2382 return "headset_head_unit";
2383 case PA_BLUETOOTH_PROFILE_HSP_AG:
2384 return "headset_audio_gateway";
2385 case PA_BLUETOOTH_PROFILE_HFP_HF:
2386 return "handsfree_head_unit";
2387 case PA_BLUETOOTH_PROFILE_HFP_AG:
2388 return "handsfree_audio_gateway";
2389 case PA_BLUETOOTH_PROFILE_OFF:
2390 return "off";
2391 }
2392
2393 return NULL;
2394 }
2395
2396 /* Returns true when PA has to perform attenuation, false if this is the
2397 * responsibility of the peer.
2398 *
2399 * `peer_profile` is the profile of the peer.
2400 *
2401 * When the peer is in the HFP/HSP Audio Gateway role (PA is in headset role) PA
2402 * has to perform attenuation on both the incoming and outgoing stream. In the
2403 * HandsFree/HeadSet role both are attenuated on the peer.
2404 */
pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile)2405 bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile) {
2406 switch(peer_profile) {
2407 case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2408 return false;
2409 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2410 return true;
2411 case PA_BLUETOOTH_PROFILE_HFP_HF:
2412 case PA_BLUETOOTH_PROFILE_HSP_HS:
2413 return false;
2414 case PA_BLUETOOTH_PROFILE_HFP_AG:
2415 case PA_BLUETOOTH_PROFILE_HSP_AG:
2416 return true;
2417 case PA_BLUETOOTH_PROFILE_OFF:
2418 pa_assert_not_reached();
2419 }
2420 pa_assert_not_reached();
2421 }
2422
pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile)2423 bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile) {
2424 return profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2425 }
2426
a2dp_sep_to_a2dp_endpoint_conf(const char * endpoint)2427 static const pa_a2dp_endpoint_conf *a2dp_sep_to_a2dp_endpoint_conf(const char *endpoint) {
2428 const char *codec_name;
2429
2430 if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
2431 codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
2432 else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
2433 codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
2434 else
2435 return NULL;
2436
2437 return pa_bluetooth_get_a2dp_endpoint_conf(codec_name);
2438 }
2439
endpoint_set_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2440 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2441 pa_bluetooth_discovery *y = userdata;
2442 pa_bluetooth_device *d;
2443 pa_bluetooth_transport *t;
2444 const pa_a2dp_endpoint_conf *endpoint_conf = NULL;
2445 const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
2446 const uint8_t *config = NULL;
2447 int size = 0;
2448 pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
2449 DBusMessageIter args, props;
2450 DBusMessage *r;
2451
2452 if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
2453 pa_log_error("Invalid signature for method SetConfiguration()");
2454 goto fail2;
2455 }
2456
2457 dbus_message_iter_get_basic(&args, &path);
2458
2459 if (pa_hashmap_get(y->transports, path)) {
2460 pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
2461 goto fail2;
2462 }
2463
2464 pa_assert_se(dbus_message_iter_next(&args));
2465
2466 dbus_message_iter_recurse(&args, &props);
2467 if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
2468 goto fail;
2469
2470 endpoint_path = dbus_message_get_path(m);
2471
2472 /* Read transport properties */
2473 while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
2474 const char *key;
2475 DBusMessageIter value, entry;
2476 int var;
2477
2478 dbus_message_iter_recurse(&props, &entry);
2479 dbus_message_iter_get_basic(&entry, &key);
2480
2481 dbus_message_iter_next(&entry);
2482 dbus_message_iter_recurse(&entry, &value);
2483
2484 var = dbus_message_iter_get_arg_type(&value);
2485
2486 if (pa_streq(key, "UUID")) {
2487 if (var != DBUS_TYPE_STRING) {
2488 pa_log_error("Property %s of wrong type %c", key, (char)var);
2489 goto fail;
2490 }
2491
2492 dbus_message_iter_get_basic(&value, &uuid);
2493
2494 if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
2495 p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2496 else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
2497 p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
2498
2499 if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
2500 (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
2501 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
2502 goto fail;
2503 }
2504 } else if (pa_streq(key, "Device")) {
2505 if (var != DBUS_TYPE_OBJECT_PATH) {
2506 pa_log_error("Property %s of wrong type %c", key, (char)var);
2507 goto fail;
2508 }
2509
2510 dbus_message_iter_get_basic(&value, &dev_path);
2511 } else if (pa_streq(key, "Configuration")) {
2512 DBusMessageIter array;
2513
2514 if (var != DBUS_TYPE_ARRAY) {
2515 pa_log_error("Property %s of wrong type %c", key, (char)var);
2516 goto fail;
2517 }
2518
2519 dbus_message_iter_recurse(&value, &array);
2520 var = dbus_message_iter_get_arg_type(&array);
2521 if (var != DBUS_TYPE_BYTE) {
2522 pa_log_error("%s is an array of wrong type %c", key, (char)var);
2523 goto fail;
2524 }
2525
2526 dbus_message_iter_get_fixed_array(&array, &config, &size);
2527
2528 endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2529 pa_assert(endpoint_conf);
2530
2531 if (!endpoint_conf->is_configuration_valid(config, size))
2532 goto fail;
2533 }
2534
2535 dbus_message_iter_next(&props);
2536 }
2537
2538 if (!endpoint_conf)
2539 goto fail2;
2540
2541 if ((d = pa_hashmap_get(y->devices, dev_path))) {
2542 if (!d->valid) {
2543 pa_log_error("Information about device %s is invalid", dev_path);
2544 goto fail2;
2545 }
2546 } else {
2547 /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
2548 pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
2549 d = device_create(y, dev_path);
2550 }
2551
2552 if (d->transports[p] != NULL) {
2553 pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
2554 goto fail2;
2555 }
2556
2557 sender = dbus_message_get_sender(m);
2558
2559 pa_assert_se(r = dbus_message_new_method_return(m));
2560 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2561 dbus_message_unref(r);
2562
2563 t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
2564 t->acquire = bluez5_transport_acquire_cb;
2565 t->release = bluez5_transport_release_cb;
2566 /* A2DP Absolute Volume is optional but BlueZ unconditionally reports
2567 * feature category 2, meaning supporting it is mandatory.
2568 * PulseAudio can and should perform the attenuation anyway in
2569 * the source role as it is the audio rendering device.
2570 */
2571 t->set_source_volume = pa_bluetooth_transport_set_source_volume;
2572
2573 pa_bluetooth_transport_reconfigure(t, &endpoint_conf->bt_codec, a2dp_transport_write, NULL);
2574 pa_bluetooth_transport_put(t);
2575
2576 pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2577 pa_log_info("Selected codec: %s", endpoint_conf->bt_codec.name);
2578
2579 return NULL;
2580
2581 fail:
2582 pa_log_error("Endpoint SetConfiguration(): invalid arguments");
2583
2584 fail2:
2585 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to set configuration"));
2586 return r;
2587 }
2588
endpoint_select_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2589 static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2590 pa_bluetooth_discovery *y = userdata;
2591 const char *endpoint_path;
2592 uint8_t *cap;
2593 int size;
2594 const pa_a2dp_endpoint_conf *endpoint_conf;
2595 uint8_t config[MAX_A2DP_CAPS_SIZE];
2596 uint8_t *config_ptr = config;
2597 size_t config_size;
2598 DBusMessage *r;
2599 DBusError err;
2600
2601 endpoint_path = dbus_message_get_path(m);
2602
2603 dbus_error_init(&err);
2604
2605 if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
2606 pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
2607 dbus_error_free(&err);
2608 goto fail;
2609 }
2610
2611 endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2612 pa_assert(endpoint_conf);
2613
2614 config_size = endpoint_conf->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
2615 if (config_size == 0)
2616 goto fail;
2617
2618 pa_assert_se(r = dbus_message_new_method_return(m));
2619 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
2620
2621 return r;
2622
2623 fail:
2624 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to select configuration"));
2625 return r;
2626 }
2627
endpoint_clear_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2628 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2629 pa_bluetooth_discovery *y = userdata;
2630 pa_bluetooth_transport *t;
2631 DBusMessage *r = NULL;
2632 DBusError err;
2633 const char *path;
2634
2635 dbus_error_init(&err);
2636
2637 if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
2638 pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
2639 dbus_error_free(&err);
2640 goto fail;
2641 }
2642
2643 if ((t = pa_hashmap_get(y->transports, path))) {
2644 pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2645 pa_bluetooth_transport_free(t);
2646 }
2647
2648 if (!dbus_message_get_no_reply(m))
2649 pa_assert_se(r = dbus_message_new_method_return(m));
2650
2651 return r;
2652
2653 fail:
2654 if (!dbus_message_get_no_reply(m))
2655 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to clear configuration"));
2656 return r;
2657 }
2658
endpoint_release(DBusConnection * conn,DBusMessage * m,void * userdata)2659 static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
2660 DBusMessage *r = NULL;
2661
2662 /* From doc/media-api.txt in bluez:
2663 *
2664 * This method gets called when the service daemon
2665 * unregisters the endpoint. An endpoint can use it to do
2666 * cleanup tasks. There is no need to unregister the
2667 * endpoint, because when this method gets called it has
2668 * already been unregistered.
2669 *
2670 * We don't have any cleanup to do. */
2671
2672 /* Reply only if requested. Generally bluetoothd doesn't request a reply
2673 * to the Release() call. Sending replies when not requested on the system
2674 * bus tends to cause errors in syslog from dbus-daemon, because it
2675 * doesn't let unexpected replies through, so it's important to have this
2676 * check here. */
2677 if (!dbus_message_get_no_reply(m))
2678 pa_assert_se(r = dbus_message_new_method_return(m));
2679
2680 return r;
2681 }
2682
endpoint_handler(DBusConnection * c,DBusMessage * m,void * userdata)2683 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2684 struct pa_bluetooth_discovery *y = userdata;
2685 DBusMessage *r = NULL;
2686 const char *path, *interface, *member;
2687
2688 pa_assert(y);
2689
2690 path = dbus_message_get_path(m);
2691 interface = dbus_message_get_interface(m);
2692 member = dbus_message_get_member(m);
2693
2694 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2695
2696 if (!a2dp_sep_to_a2dp_endpoint_conf(path))
2697 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2698
2699 if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2700 const char *xml = ENDPOINT_INTROSPECT_XML;
2701
2702 pa_assert_se(r = dbus_message_new_method_return(m));
2703 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2704
2705 } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
2706 r = endpoint_set_configuration(c, m, userdata);
2707 else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
2708 r = endpoint_select_configuration(c, m, userdata);
2709 else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
2710 r = endpoint_clear_configuration(c, m, userdata);
2711 else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
2712 r = endpoint_release(c, m, userdata);
2713 else
2714 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2715
2716 if (r) {
2717 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2718 dbus_message_unref(r);
2719 }
2720
2721 return DBUS_HANDLER_RESULT_HANDLED;
2722 }
2723
endpoint_init(pa_bluetooth_discovery * y,const char * endpoint)2724 static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
2725 static const DBusObjectPathVTable vtable_endpoint = {
2726 .message_function = endpoint_handler,
2727 };
2728
2729 pa_assert(y);
2730 pa_assert(endpoint);
2731
2732 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
2733 &vtable_endpoint, y));
2734 }
2735
endpoint_done(pa_bluetooth_discovery * y,const char * endpoint)2736 static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
2737 pa_assert(y);
2738 pa_assert(endpoint);
2739
2740 dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
2741 }
2742
append_a2dp_object(DBusMessageIter * iter,const char * endpoint,const char * uuid,uint8_t codec_id,uint8_t * capabilities,uint8_t capabilities_size)2743 static void append_a2dp_object(DBusMessageIter *iter, const char *endpoint, const char *uuid, uint8_t codec_id, uint8_t *capabilities, uint8_t capabilities_size) {
2744 const char *interface_name = BLUEZ_MEDIA_ENDPOINT_INTERFACE;
2745 DBusMessageIter object, array, entry, dict;
2746
2747 dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &object);
2748 pa_assert_se(dbus_message_iter_append_basic(&object, DBUS_TYPE_OBJECT_PATH, &endpoint));
2749
2750 dbus_message_iter_open_container(&object, DBUS_TYPE_ARRAY,
2751 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2752 DBUS_TYPE_STRING_AS_STRING
2753 DBUS_TYPE_ARRAY_AS_STRING
2754 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2755 DBUS_TYPE_STRING_AS_STRING
2756 DBUS_TYPE_VARIANT_AS_STRING
2757 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2758 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2759 &array);
2760
2761 dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
2762 pa_assert_se(dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &interface_name));
2763
2764 dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
2765 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2766 DBUS_TYPE_STRING_AS_STRING
2767 DBUS_TYPE_VARIANT_AS_STRING
2768 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2769 &dict);
2770
2771 pa_dbus_append_basic_variant_dict_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
2772 pa_dbus_append_basic_variant_dict_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec_id);
2773 pa_dbus_append_basic_array_variant_dict_entry(&dict, "Capabilities", DBUS_TYPE_BYTE,
2774 capabilities, capabilities_size);
2775
2776 dbus_message_iter_close_container(&entry, &dict);
2777 dbus_message_iter_close_container(&array, &entry);
2778 dbus_message_iter_close_container(&object, &array);
2779 dbus_message_iter_close_container(iter, &object);
2780 }
2781
object_manager_handler(DBusConnection * c,DBusMessage * m,void * userdata)2782 static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2783 struct pa_bluetooth_discovery *y = userdata;
2784 DBusMessage *r;
2785 const char *path, *interface, *member;
2786
2787 pa_assert(y);
2788
2789 path = dbus_message_get_path(m);
2790 interface = dbus_message_get_interface(m);
2791 member = dbus_message_get_member(m);
2792
2793 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2794
2795 if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2796 const char *xml = OBJECT_MANAGER_INTROSPECT_XML;
2797
2798 pa_assert_se(r = dbus_message_new_method_return(m));
2799 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2800 } else if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
2801 DBusMessageIter iter, array;
2802 int i;
2803
2804 pa_assert_se(r = dbus_message_new_method_return(m));
2805
2806 dbus_message_iter_init_append(r, &iter);
2807 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
2808 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2809 DBUS_TYPE_OBJECT_PATH_AS_STRING
2810 DBUS_TYPE_ARRAY_AS_STRING
2811 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2812 DBUS_TYPE_STRING_AS_STRING
2813 DBUS_TYPE_ARRAY_AS_STRING
2814 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2815 DBUS_TYPE_STRING_AS_STRING
2816 DBUS_TYPE_VARIANT_AS_STRING
2817 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2818 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2819 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2820 &array);
2821
2822 for (i = 0; i < pa_bluetooth_a2dp_endpoint_conf_count(); i++) {
2823 const pa_a2dp_endpoint_conf *endpoint_conf;
2824 uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
2825 uint8_t capabilities_size;
2826 uint8_t codec_id;
2827 char *endpoint;
2828
2829 endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2830
2831 codec_id = endpoint_conf->id.codec_id;
2832
2833 if (endpoint_conf->can_be_supported(false)) {
2834 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2835 pa_assert(capabilities_size != 0);
2836 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2837 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK, codec_id,
2838 capabilities, capabilities_size);
2839 pa_xfree(endpoint);
2840 }
2841
2842 if (endpoint_conf->can_be_supported(true)) {
2843 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2844 pa_assert(capabilities_size != 0);
2845 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2846 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE, codec_id,
2847 capabilities, capabilities_size);
2848 pa_xfree(endpoint);
2849 }
2850 }
2851
2852 dbus_message_iter_close_container(&iter, &array);
2853 } else
2854 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2855
2856 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2857 dbus_message_unref(r);
2858
2859 return DBUS_HANDLER_RESULT_HANDLED;
2860 }
2861
object_manager_init(pa_bluetooth_discovery * y)2862 static void object_manager_init(pa_bluetooth_discovery *y) {
2863 static const DBusObjectPathVTable vtable = {
2864 .message_function = object_manager_handler,
2865 };
2866
2867 pa_assert(y);
2868 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection),
2869 A2DP_OBJECT_MANAGER_PATH, &vtable, y));
2870 }
2871
object_manager_done(pa_bluetooth_discovery * y)2872 static void object_manager_done(pa_bluetooth_discovery *y) {
2873 pa_assert(y);
2874 dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection),
2875 A2DP_OBJECT_MANAGER_PATH);
2876 }
2877
pa_bluetooth_discovery_get(pa_core * c,int headset_backend,bool enable_native_hsp_hs,bool enable_native_hfp_hf,bool enable_msbc)2878 pa_bluetooth_discovery* pa_bluetooth_discovery_get(pa_core *c, int headset_backend, bool enable_native_hsp_hs, bool enable_native_hfp_hf, bool enable_msbc) {
2879 pa_bluetooth_discovery *y;
2880 DBusError err;
2881 DBusConnection *conn;
2882 unsigned i, count;
2883 const pa_a2dp_endpoint_conf *endpoint_conf;
2884 char *endpoint;
2885
2886 pa_bluetooth_a2dp_codec_gst_init();
2887 y = pa_xnew0(pa_bluetooth_discovery, 1);
2888 PA_REFCNT_INIT(y);
2889 y->core = c;
2890 y->headset_backend = headset_backend;
2891 y->enable_native_hsp_hs = enable_native_hsp_hs;
2892 y->enable_native_hfp_hf = enable_native_hfp_hf;
2893 y->enable_msbc = enable_msbc;
2894 y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2895 (pa_free_cb_t) adapter_free);
2896 y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2897 (pa_free_cb_t) device_free);
2898 y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2899 PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2900
2901 for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2902 pa_hook_init(&y->hooks[i], y);
2903
2904 pa_shared_set(c, "bluetooth-discovery", y);
2905
2906 dbus_error_init(&err);
2907
2908 if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
2909 pa_log_error("Failed to get D-Bus connection: %s", err.message);
2910 goto fail;
2911 }
2912
2913 conn = pa_dbus_connection_get(y->connection);
2914
2915 /* dynamic detection of bluetooth audio devices */
2916 if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2917 pa_log_error("Failed to add filter function");
2918 goto fail;
2919 }
2920 y->filter_added = true;
2921
2922 if (pa_dbus_add_matches(conn, &err,
2923 "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'"
2924 ",arg0='" BLUEZ_SERVICE "'",
2925 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',member='InterfacesAdded'",
2926 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
2927 "member='InterfacesRemoved'",
2928 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2929 ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
2930 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2931 ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
2932 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2933 ",arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
2934 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2935 ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
2936 NULL) < 0) {
2937 pa_log_error("Failed to add D-Bus matches: %s", err.message);
2938 goto fail;
2939 }
2940 y->matches_added = true;
2941
2942 object_manager_init(y);
2943
2944 count = pa_bluetooth_a2dp_endpoint_conf_count();
2945 for (i = 0; i < count; i++) {
2946 endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2947 if (endpoint_conf->can_be_supported(false)) {
2948 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2949 endpoint_init(y, endpoint);
2950 pa_xfree(endpoint);
2951 }
2952
2953 if (endpoint_conf->can_be_supported(true)) {
2954 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2955 endpoint_init(y, endpoint);
2956 pa_xfree(endpoint);
2957 }
2958 }
2959
2960 get_managed_objects(y);
2961
2962 return y;
2963
2964 fail:
2965 pa_bluetooth_discovery_unref(y);
2966 dbus_error_free(&err);
2967
2968 return NULL;
2969 }
2970
pa_bluetooth_discovery_ref(pa_bluetooth_discovery * y)2971 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2972 pa_assert(y);
2973 pa_assert(PA_REFCNT_VALUE(y) > 0);
2974
2975 PA_REFCNT_INC(y);
2976
2977 return y;
2978 }
2979
pa_bluetooth_discovery_unref(pa_bluetooth_discovery * y)2980 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2981 unsigned i, count;
2982 const pa_a2dp_endpoint_conf *endpoint_conf;
2983 char *endpoint;
2984
2985 pa_assert(y);
2986 pa_assert(PA_REFCNT_VALUE(y) > 0);
2987
2988 if (PA_REFCNT_DEC(y) > 0)
2989 return;
2990
2991 pa_dbus_free_pending_list(&y->pending);
2992
2993 if (y->ofono_backend)
2994 pa_bluetooth_ofono_backend_free(y->ofono_backend);
2995 if (y->native_backend)
2996 pa_bluetooth_native_backend_free(y->native_backend);
2997
2998 if (y->adapters)
2999 pa_hashmap_free(y->adapters);
3000
3001 if (y->devices)
3002 pa_hashmap_free(y->devices);
3003
3004 if (y->transports) {
3005 pa_assert(pa_hashmap_isempty(y->transports));
3006 pa_hashmap_free(y->transports);
3007 }
3008
3009 if (y->connection) {
3010
3011 if (y->matches_added)
3012 pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
3013 "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',"
3014 "arg0='" BLUEZ_SERVICE "'",
3015 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3016 "member='InterfacesAdded'",
3017 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3018 "member='InterfacesRemoved'",
3019 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3020 "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
3021 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3022 "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
3023 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3024 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
3025 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3026 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
3027 NULL);
3028
3029 if (y->filter_added)
3030 dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
3031
3032 object_manager_done(y);
3033
3034 count = pa_bluetooth_a2dp_endpoint_conf_count();
3035 for (i = 0; i < count; i++) {
3036 endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
3037
3038 if (endpoint_conf->can_be_supported(false)) {
3039 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
3040 endpoint_done(y, endpoint);
3041 pa_xfree(endpoint);
3042 }
3043
3044 if (endpoint_conf->can_be_supported(true)) {
3045 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
3046 endpoint_done(y, endpoint);
3047 pa_xfree(endpoint);
3048 }
3049 }
3050
3051 pa_dbus_connection_unref(y->connection);
3052 }
3053
3054 pa_shared_remove(y->core, "bluetooth-discovery");
3055 pa_xfree(y);
3056 }
3057