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_device_by_address(pa_bluetooth_discovery * y,const char * remote,const char * local)1205 pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local) {
1206 pa_bluetooth_device *d;
1207 void *state = NULL;
1208
1209 pa_assert(y);
1210 pa_assert(PA_REFCNT_VALUE(y) > 0);
1211 pa_assert(remote);
1212 pa_assert(local);
1213
1214 while ((d = pa_hashmap_iterate(y->devices, &state, NULL)))
1215 if (d->valid && pa_streq(d->address, remote) && pa_streq(d->adapter->address, local))
1216 return d;
1217
1218 return NULL;
1219 }
1220
device_free(pa_bluetooth_device * d)1221 static void device_free(pa_bluetooth_device *d) {
1222 unsigned i;
1223
1224 pa_assert(d);
1225
1226 device_stop_waiting_for_profiles(d);
1227
1228 pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_UNLINK], d);
1229
1230 for (i = 0; i < PA_BLUETOOTH_PROFILE_COUNT; i++) {
1231 pa_bluetooth_transport *t;
1232
1233 if (!(t = d->transports[i]))
1234 continue;
1235
1236 pa_bluetooth_transport_free(t);
1237 }
1238
1239 if (d->uuids)
1240 pa_hashmap_free(d->uuids);
1241 if (d->a2dp_sink_endpoints)
1242 pa_hashmap_free(d->a2dp_sink_endpoints);
1243 if (d->a2dp_source_endpoints)
1244 pa_hashmap_free(d->a2dp_source_endpoints);
1245
1246 pa_xfree(d->path);
1247 pa_xfree(d->alias);
1248 pa_xfree(d->address);
1249 pa_xfree(d->adapter_path);
1250 pa_xfree(d);
1251 }
1252
device_remove(pa_bluetooth_discovery * y,const char * path)1253 static void device_remove(pa_bluetooth_discovery *y, const char *path) {
1254 pa_bluetooth_device *d;
1255
1256 if (!(d = pa_hashmap_remove(y->devices, path)))
1257 pa_log_warn("Unknown device removed %s", path);
1258 else {
1259 pa_log_debug("Device %s removed", path);
1260 device_free(d);
1261 }
1262 }
1263
device_set_valid(pa_bluetooth_device * device,bool valid)1264 static void device_set_valid(pa_bluetooth_device *device, bool valid) {
1265 bool old_any_connected;
1266
1267 pa_assert(device);
1268
1269 if (valid == device->valid)
1270 return;
1271
1272 old_any_connected = pa_bluetooth_device_any_transport_connected(device);
1273 device->valid = valid;
1274
1275 if (pa_bluetooth_device_any_transport_connected(device) != old_any_connected)
1276 pa_hook_fire(&device->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED], device);
1277 }
1278
device_update_valid(pa_bluetooth_device * d)1279 static void device_update_valid(pa_bluetooth_device *d) {
1280 pa_assert(d);
1281
1282 if (!d->properties_received) {
1283 pa_assert(!d->valid);
1284 return;
1285 }
1286
1287 /* Check if mandatory properties are set. */
1288 if (!d->address || !d->adapter_path || !d->alias) {
1289 device_set_valid(d, false);
1290 return;
1291 }
1292
1293 if (!d->adapter || !d->adapter->valid) {
1294 device_set_valid(d, false);
1295 return;
1296 }
1297
1298 device_set_valid(d, true);
1299 }
1300
device_set_adapter(pa_bluetooth_device * device,pa_bluetooth_adapter * adapter)1301 static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter *adapter) {
1302 pa_assert(device);
1303
1304 if (adapter == device->adapter)
1305 return;
1306
1307 device->adapter = adapter;
1308
1309 device_update_valid(device);
1310 }
1311
append_battery_provider_properties(pa_bluetooth_device * d,DBusMessageIter * entry,bool only_percentage)1312 static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *entry, bool only_percentage) {
1313 static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
1314 DBusMessageIter dict;
1315
1316 pa_assert_se(dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &interface_name));
1317
1318 pa_assert_se(dbus_message_iter_open_container(entry, DBUS_TYPE_ARRAY,
1319 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1320 DBUS_TYPE_STRING_AS_STRING
1321 DBUS_TYPE_VARIANT_AS_STRING
1322 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1323 &dict));
1324
1325 pa_dbus_append_basic_variant_dict_entry(&dict, "Percentage", DBUS_TYPE_BYTE, &d->battery_level);
1326
1327 if (!only_percentage) {
1328 pa_assert(d->battery_source);
1329 pa_dbus_append_basic_variant_dict_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &d->path);
1330 pa_dbus_append_basic_variant_dict_entry(&dict, "Source", DBUS_TYPE_STRING, &d->battery_source);
1331 }
1332
1333 pa_assert_se(dbus_message_iter_close_container(entry, &dict));
1334 }
1335
append_battery_provider(pa_bluetooth_device * d,DBusMessageIter * object)1336 static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object) {
1337 char *battery_path = device_battery_provider_path(d);
1338 DBusMessageIter array, entry;
1339
1340 pa_assert_se(dbus_message_iter_append_basic(object, DBUS_TYPE_OBJECT_PATH, &battery_path));
1341
1342 pa_assert_se(dbus_message_iter_open_container(object, DBUS_TYPE_ARRAY,
1343 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1344 DBUS_TYPE_STRING_AS_STRING
1345 DBUS_TYPE_ARRAY_AS_STRING
1346 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1347 DBUS_TYPE_STRING_AS_STRING
1348 DBUS_TYPE_VARIANT_AS_STRING
1349 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1350 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1351 &array));
1352
1353 pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry));
1354 append_battery_provider_properties(d, &entry, false);
1355 pa_assert_se(dbus_message_iter_close_container(&array, &entry));
1356 pa_assert_se(dbus_message_iter_close_container(object, &array));
1357
1358 pa_xfree(battery_path);
1359 }
1360
battery_provider_handler(DBusConnection * c,DBusMessage * m,void * userdata)1361 static DBusHandlerResult battery_provider_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
1362 pa_bluetooth_adapter *a = userdata;
1363 DBusMessage *r = NULL;
1364 const char *path, *interface, *member;
1365
1366 pa_assert(a);
1367
1368 path = dbus_message_get_path(m);
1369 interface = dbus_message_get_interface(m);
1370 member = dbus_message_get_member(m);
1371
1372 pa_log_debug("%s %s %s", path, interface, member);
1373
1374 if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
1375 DBusMessageIter iter, array, object;
1376 pa_bluetooth_device *d;
1377 void *state;
1378
1379 pa_assert_se(r = dbus_message_new_method_return(m));
1380
1381 dbus_message_iter_init_append(r, &iter);
1382 pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
1383 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1384 DBUS_TYPE_OBJECT_PATH_AS_STRING
1385 DBUS_TYPE_ARRAY_AS_STRING
1386 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1387 DBUS_TYPE_STRING_AS_STRING
1388 DBUS_TYPE_ARRAY_AS_STRING
1389 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1390 DBUS_TYPE_STRING_AS_STRING
1391 DBUS_TYPE_VARIANT_AS_STRING
1392 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1393 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
1394 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1395 &array));
1396
1397 PA_HASHMAP_FOREACH(d, a->discovery->devices, state) {
1398
1399 if (d->has_battery_level) {
1400 pa_log_debug("%s: battery level = %d", d->path, d->battery_level);
1401 pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &object));
1402 append_battery_provider(d, &object);
1403 pa_assert_se(dbus_message_iter_close_container(&array, &object));
1404 }
1405 }
1406
1407 pa_assert_se(dbus_message_iter_close_container(&iter, &array));
1408 } else
1409 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
1410
1411 pa_assert_se(dbus_connection_send(c, r, NULL));
1412 dbus_message_unref(r);
1413
1414 return DBUS_HANDLER_RESULT_HANDLED;
1415 }
1416
adapter_register_battery_provider(pa_bluetooth_adapter * a)1417 static void adapter_register_battery_provider(pa_bluetooth_adapter *a) {
1418 DBusMessage *m, *r;
1419 DBusError error;
1420
1421 static const DBusObjectPathVTable vtable_profile = {
1422 .message_function = battery_provider_handler,
1423 };
1424
1425 char *provider_path = adapter_battery_provider_path(a);
1426
1427 pa_log_debug("Registering battery provider for %s at %s", a->path, provider_path);
1428
1429 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path, &vtable_profile, a));
1430
1431 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "RegisterBatteryProvider"));
1432 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1433
1434 dbus_error_init(&error);
1435 if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1436 if (dbus_error_has_name(&error, DBUS_ERROR_UNKNOWN_METHOD))
1437 pa_log_notice("Could not find " BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE
1438 ".RegisterBatteryProvider(), is bluetoothd started with experimental features enabled (-E flag)?");
1439 else
1440 pa_log_warn(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".RegisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1441 dbus_error_free(&error);
1442 dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1443 } else {
1444 dbus_message_unref(r);
1445 a->battery_provider_registered = true;
1446 }
1447
1448 dbus_message_unref(m);
1449 pa_xfree(provider_path);
1450 }
1451
adapter_deregister_battery_provider(pa_bluetooth_adapter * a)1452 static void adapter_deregister_battery_provider(pa_bluetooth_adapter *a) {
1453 DBusMessage *m, *r;
1454 DBusError error;
1455 char *provider_path;
1456
1457 if (!a->battery_provider_registered) {
1458 pa_log_debug("No battery provider registered for %s", a->path);
1459 return;
1460 }
1461
1462 provider_path = adapter_battery_provider_path(a);
1463
1464 pa_log_debug("Deregistering battery provider at %s", provider_path);
1465
1466 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "UnregisterBatteryProvider"));
1467 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
1468
1469 dbus_error_init(&error);
1470 if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
1471 pa_log_error(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".UnregisterBatteryProvider() Failed: %s:%s", error.name, error.message);
1472 dbus_error_free(&error);
1473 } else {
1474 dbus_message_unref(r);
1475 a->battery_provider_registered = false;
1476 }
1477
1478 dbus_message_unref(m);
1479
1480 dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
1481
1482 pa_xfree(provider_path);
1483 }
1484
adapter_create(pa_bluetooth_discovery * y,const char * path)1485 static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
1486 pa_bluetooth_adapter *a;
1487
1488 pa_assert(y);
1489 pa_assert(path);
1490
1491 a = pa_xnew0(pa_bluetooth_adapter, 1);
1492 a->discovery = y;
1493 a->path = pa_xstrdup(path);
1494 a->uuids = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL, pa_xfree);
1495
1496 pa_hashmap_put(y->adapters, a->path, a);
1497
1498 return a;
1499 }
1500
adapter_free(pa_bluetooth_adapter * a)1501 static void adapter_free(pa_bluetooth_adapter *a) {
1502 pa_bluetooth_device *d;
1503 void *state;
1504
1505 pa_assert(a);
1506 pa_assert(a->discovery);
1507
1508 adapter_deregister_battery_provider(a);
1509
1510 PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
1511 if (d->adapter == a)
1512 device_set_adapter(d, NULL);
1513
1514 pa_hashmap_free(a->uuids);
1515 pa_xfree(a->path);
1516 pa_xfree(a->address);
1517 pa_xfree(a);
1518 }
1519
adapter_remove(pa_bluetooth_discovery * y,const char * path)1520 static void adapter_remove(pa_bluetooth_discovery *y, const char *path) {
1521 pa_bluetooth_adapter *a;
1522
1523 if (!(a = pa_hashmap_remove(y->adapters, path)))
1524 pa_log_warn("Unknown adapter removed %s", path);
1525 else {
1526 pa_log_debug("Adapter %s removed", path);
1527 adapter_free(a);
1528 }
1529 }
1530
parse_device_property(pa_bluetooth_device * d,DBusMessageIter * i)1531 static void parse_device_property(pa_bluetooth_device *d, DBusMessageIter *i) {
1532 const char *key;
1533 DBusMessageIter variant_i;
1534
1535 pa_assert(d);
1536
1537 key = check_variant_property(i);
1538 if (key == NULL) {
1539 pa_log_error("Received invalid property for device %s", d->path);
1540 return;
1541 }
1542
1543 dbus_message_iter_recurse(i, &variant_i);
1544
1545 switch (dbus_message_iter_get_arg_type(&variant_i)) {
1546
1547 case DBUS_TYPE_STRING: {
1548 const char *value;
1549 dbus_message_iter_get_basic(&variant_i, &value);
1550
1551 if (pa_streq(key, "Alias")) {
1552 pa_xfree(d->alias);
1553 d->alias = pa_xstrdup(value);
1554 pa_log_debug("%s: %s", key, value);
1555 } else if (pa_streq(key, "Address")) {
1556 if (d->properties_received) {
1557 pa_log_warn("Device property 'Address' expected to be constant but changed for %s, ignoring", d->path);
1558 return;
1559 }
1560
1561 if (d->address) {
1562 pa_log_warn("Device %s: Received a duplicate 'Address' property, ignoring", d->path);
1563 return;
1564 }
1565
1566 d->address = pa_xstrdup(value);
1567 pa_log_debug("%s: %s", key, value);
1568 }
1569
1570 break;
1571 }
1572
1573 case DBUS_TYPE_OBJECT_PATH: {
1574 const char *value;
1575 dbus_message_iter_get_basic(&variant_i, &value);
1576
1577 if (pa_streq(key, "Adapter")) {
1578
1579 if (d->properties_received) {
1580 pa_log_warn("Device property 'Adapter' expected to be constant but changed for %s, ignoring", d->path);
1581 return;
1582 }
1583
1584 if (d->adapter_path) {
1585 pa_log_warn("Device %s: Received a duplicate 'Adapter' property, ignoring", d->path);
1586 return;
1587 }
1588
1589 d->adapter_path = pa_xstrdup(value);
1590 pa_log_debug("%s: %s", key, value);
1591 }
1592
1593 break;
1594 }
1595
1596 case DBUS_TYPE_UINT32: {
1597 uint32_t value;
1598 dbus_message_iter_get_basic(&variant_i, &value);
1599
1600 if (pa_streq(key, "Class")) {
1601 d->class_of_device = value;
1602 pa_log_debug("%s: %d", key, value);
1603 }
1604
1605 break;
1606 }
1607
1608 case DBUS_TYPE_ARRAY: {
1609 DBusMessageIter ai;
1610 dbus_message_iter_recurse(&variant_i, &ai);
1611
1612 if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1613 /* bluetoothd never removes UUIDs from a device object so we
1614 * don't need to check for disappeared UUIDs here. */
1615 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1616 const char *value;
1617 char *uuid;
1618
1619 dbus_message_iter_get_basic(&ai, &value);
1620
1621 if (pa_hashmap_get(d->uuids, value)) {
1622 dbus_message_iter_next(&ai);
1623 continue;
1624 }
1625
1626 uuid = pa_xstrdup(value);
1627 pa_hashmap_put(d->uuids, uuid, uuid);
1628
1629 pa_log_debug("%s: %s", key, value);
1630 dbus_message_iter_next(&ai);
1631 }
1632 }
1633
1634 break;
1635 }
1636 }
1637 }
1638
parse_device_properties(pa_bluetooth_device * d,DBusMessageIter * i)1639 static void parse_device_properties(pa_bluetooth_device *d, DBusMessageIter *i) {
1640 DBusMessageIter element_i;
1641
1642 dbus_message_iter_recurse(i, &element_i);
1643
1644 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1645 DBusMessageIter dict_i;
1646
1647 dbus_message_iter_recurse(&element_i, &dict_i);
1648 parse_device_property(d, &dict_i);
1649 dbus_message_iter_next(&element_i);
1650 }
1651
1652 if (!d->properties_received) {
1653 d->properties_received = true;
1654 device_update_valid(d);
1655
1656 if (!d->address || !d->adapter_path || !d->alias)
1657 pa_log_error("Non-optional information missing for device %s", d->path);
1658 }
1659 }
1660
parse_adapter_properties(pa_bluetooth_adapter * a,DBusMessageIter * i,bool is_property_change)1661 static void parse_adapter_properties(pa_bluetooth_adapter *a, DBusMessageIter *i, bool is_property_change) {
1662 DBusMessageIter element_i;
1663
1664 pa_assert(a);
1665
1666 dbus_message_iter_recurse(i, &element_i);
1667
1668 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1669 DBusMessageIter dict_i, variant_i;
1670 const char *key;
1671
1672 dbus_message_iter_recurse(&element_i, &dict_i);
1673
1674 key = check_variant_property(&dict_i);
1675 if (key == NULL) {
1676 pa_log_error("Received invalid property for adapter %s", a->path);
1677 return;
1678 }
1679
1680 dbus_message_iter_recurse(&dict_i, &variant_i);
1681
1682 if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_STRING && pa_streq(key, "Address")) {
1683 const char *value;
1684
1685 if (is_property_change) {
1686 pa_log_warn("Adapter property 'Address' expected to be constant but changed for %s, ignoring", a->path);
1687 return;
1688 }
1689
1690 if (a->address) {
1691 pa_log_warn("Adapter %s received a duplicate 'Address' property, ignoring", a->path);
1692 return;
1693 }
1694
1695 dbus_message_iter_get_basic(&variant_i, &value);
1696 a->address = pa_xstrdup(value);
1697 a->valid = true;
1698 } else if (dbus_message_iter_get_arg_type(&variant_i) == DBUS_TYPE_ARRAY) {
1699 DBusMessageIter ai;
1700 dbus_message_iter_recurse(&variant_i, &ai);
1701
1702 if (dbus_message_iter_get_arg_type(&ai) == DBUS_TYPE_STRING && pa_streq(key, "UUIDs")) {
1703 pa_hashmap_remove_all(a->uuids);
1704 while (dbus_message_iter_get_arg_type(&ai) != DBUS_TYPE_INVALID) {
1705 const char *value;
1706 char *uuid;
1707
1708 dbus_message_iter_get_basic(&ai, &value);
1709
1710 if (pa_hashmap_get(a->uuids, value)) {
1711 dbus_message_iter_next(&ai);
1712 continue;
1713 }
1714
1715 uuid = pa_xstrdup(value);
1716 pa_hashmap_put(a->uuids, uuid, uuid);
1717
1718 pa_log_debug("%s: %s", key, value);
1719 dbus_message_iter_next(&ai);
1720 }
1721 pa_hook_fire(pa_bluetooth_discovery_hook(a->discovery, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), a);
1722 }
1723 }
1724
1725 dbus_message_iter_next(&element_i);
1726 }
1727 }
1728
register_legacy_sbc_endpoint_reply(DBusPendingCall * pending,void * userdata)1729 static void register_legacy_sbc_endpoint_reply(DBusPendingCall *pending, void *userdata) {
1730 DBusMessage *r;
1731 pa_dbus_pending *p;
1732 pa_bluetooth_discovery *y;
1733 char *endpoint;
1734
1735 pa_assert(pending);
1736 pa_assert_se(p = userdata);
1737 pa_assert_se(y = p->context_data);
1738 pa_assert_se(endpoint = p->call_data);
1739 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1740
1741 if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1742 pa_log_info("Couldn't register endpoint %s because it is disabled in BlueZ", endpoint);
1743 goto finish;
1744 }
1745
1746 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1747 pa_log_error(BLUEZ_MEDIA_INTERFACE ".RegisterEndpoint() failed: %s: %s", dbus_message_get_error_name(r),
1748 pa_dbus_get_error_message(r));
1749 goto finish;
1750 }
1751
1752 finish:
1753 dbus_message_unref(r);
1754
1755 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1756 pa_dbus_pending_free(p);
1757
1758 pa_xfree(endpoint);
1759 }
1760
register_legacy_sbc_endpoint(pa_bluetooth_discovery * y,const pa_a2dp_endpoint_conf * endpoint_conf,const char * path,const char * endpoint,const char * uuid)1761 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) {
1762 DBusMessage *m;
1763 DBusMessageIter i, d;
1764 uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
1765 size_t capabilities_size;
1766 uint8_t codec_id;
1767
1768 pa_log_debug("Registering %s on adapter %s", endpoint, path);
1769
1770 codec_id = endpoint_conf->id.codec_id;
1771 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
1772 pa_assert(capabilities_size != 0);
1773
1774 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, path, BLUEZ_MEDIA_INTERFACE, "RegisterEndpoint"));
1775
1776 dbus_message_iter_init_append(m, &i);
1777 pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &endpoint));
1778 dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1779 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1780 DBUS_TYPE_STRING_AS_STRING
1781 DBUS_TYPE_VARIANT_AS_STRING
1782 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1783 &d);
1784 pa_dbus_append_basic_variant_dict_entry(&d, "UUID", DBUS_TYPE_STRING, &uuid);
1785 pa_dbus_append_basic_variant_dict_entry(&d, "Codec", DBUS_TYPE_BYTE, &codec_id);
1786 pa_dbus_append_basic_array_variant_dict_entry(&d, "Capabilities", DBUS_TYPE_BYTE, &capabilities, capabilities_size);
1787
1788 dbus_message_iter_close_container(&i, &d);
1789
1790 send_and_add_to_pending(y, m, register_legacy_sbc_endpoint_reply, pa_xstrdup(endpoint));
1791 }
1792
register_application_reply(DBusPendingCall * pending,void * userdata)1793 static void register_application_reply(DBusPendingCall *pending, void *userdata) {
1794 DBusMessage *r;
1795 pa_dbus_pending *p;
1796 pa_bluetooth_adapter *a;
1797 pa_bluetooth_discovery *y;
1798 char *path;
1799 bool fallback = true;
1800
1801 pa_assert(pending);
1802 pa_assert_se(p = userdata);
1803 pa_assert_se(y = p->context_data);
1804 pa_assert_se(path = p->call_data);
1805 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
1806
1807 if (dbus_message_is_error(r, BLUEZ_ERROR_NOT_SUPPORTED)) {
1808 pa_log_info("Couldn't register media application for adapter %s because it is disabled in BlueZ", path);
1809 goto finish;
1810 }
1811
1812 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
1813 pa_log_warn(BLUEZ_MEDIA_INTERFACE ".RegisterApplication() failed: %s: %s",
1814 dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
1815 pa_log_warn("Couldn't register media application for adapter %s", path);
1816 goto finish;
1817 }
1818
1819 a = pa_hashmap_get(y->adapters, path);
1820 if (!a) {
1821 pa_log_error("Couldn't register media application for adapter %s because it does not exist anymore", path);
1822 goto finish;
1823 }
1824
1825 fallback = false;
1826 a->application_registered = true;
1827 pa_log_debug("Media application for adapter %s was successfully registered", path);
1828
1829 finish:
1830 dbus_message_unref(r);
1831
1832 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
1833 pa_dbus_pending_free(p);
1834
1835 if (fallback) {
1836 /* If bluez does not support RegisterApplication, fallback to old legacy API with just one SBC codec */
1837 const pa_a2dp_endpoint_conf *endpoint_conf;
1838 endpoint_conf = pa_bluetooth_get_a2dp_endpoint_conf("sbc");
1839 pa_assert(endpoint_conf);
1840 register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SINK_ENDPOINT "/sbc",
1841 PA_BLUETOOTH_UUID_A2DP_SINK);
1842 register_legacy_sbc_endpoint(y, endpoint_conf, path, A2DP_SOURCE_ENDPOINT "/sbc",
1843 PA_BLUETOOTH_UUID_A2DP_SOURCE);
1844 pa_log_warn("Only SBC codec is available for A2DP profiles");
1845 }
1846
1847 pa_xfree(path);
1848 }
1849
register_application(pa_bluetooth_adapter * a)1850 static void register_application(pa_bluetooth_adapter *a) {
1851 DBusMessage *m;
1852 DBusMessageIter i, d;
1853 const char *object_manager_path = A2DP_OBJECT_MANAGER_PATH;
1854
1855 if (a->application_registered) {
1856 pa_log_info("Media application is already registered for adapter %s", a->path);
1857 return;
1858 }
1859
1860 pa_log_debug("Registering media application for adapter %s", a->path);
1861
1862 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path,
1863 BLUEZ_MEDIA_INTERFACE, "RegisterApplication"));
1864
1865 dbus_message_iter_init_append(m, &i);
1866 pa_assert_se(dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH, &object_manager_path));
1867 dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
1868 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
1869 DBUS_TYPE_STRING_AS_STRING
1870 DBUS_TYPE_VARIANT_AS_STRING
1871 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
1872 &d);
1873 dbus_message_iter_close_container(&i, &d);
1874
1875 send_and_add_to_pending(a->discovery, m, register_application_reply, pa_xstrdup(a->path));
1876 }
1877
parse_remote_endpoint_properties(pa_bluetooth_discovery * y,const char * endpoint,DBusMessageIter * i)1878 static void parse_remote_endpoint_properties(pa_bluetooth_discovery *y, const char *endpoint, DBusMessageIter *i) {
1879 DBusMessageIter element_i;
1880 pa_bluetooth_device *device;
1881 pa_hashmap *codec_endpoints;
1882 pa_hashmap *endpoints;
1883 pa_a2dp_codec_id *a2dp_codec_id;
1884 pa_a2dp_codec_capabilities *a2dp_codec_capabilities;
1885 const char *uuid = NULL;
1886 const char *device_path = NULL;
1887 uint8_t codec_id = 0;
1888 bool have_codec_id = false;
1889 const uint8_t *capabilities = NULL;
1890 int capabilities_size = 0;
1891
1892 pa_log_debug("Parsing remote endpoint %s", endpoint);
1893
1894 dbus_message_iter_recurse(i, &element_i);
1895
1896 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
1897 DBusMessageIter dict_i, variant_i;
1898 const char *key;
1899
1900 dbus_message_iter_recurse(&element_i, &dict_i);
1901
1902 key = check_variant_property(&dict_i);
1903 if (key == NULL) {
1904 pa_log_error("Received invalid property for remote endpoint %s", endpoint);
1905 return;
1906 }
1907
1908 dbus_message_iter_recurse(&dict_i, &variant_i);
1909
1910 if (pa_streq(key, "UUID")) {
1911 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_STRING) {
1912 pa_log_warn("Remote endpoint %s property 'UUID' is not string, ignoring", endpoint);
1913 return;
1914 }
1915
1916 dbus_message_iter_get_basic(&variant_i, &uuid);
1917 } else if (pa_streq(key, "Codec")) {
1918 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_BYTE) {
1919 pa_log_warn("Remote endpoint %s property 'Codec' is not byte, ignoring", endpoint);
1920 return;
1921 }
1922
1923 dbus_message_iter_get_basic(&variant_i, &codec_id);
1924 have_codec_id = true;
1925 } else if (pa_streq(key, "Capabilities")) {
1926 DBusMessageIter array;
1927
1928 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_ARRAY) {
1929 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array, ignoring", endpoint);
1930 return;
1931 }
1932
1933 dbus_message_iter_recurse(&variant_i, &array);
1934 if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_BYTE) {
1935 pa_log_warn("Remote endpoint %s property 'Capabilities' is not array of bytes, ignoring", endpoint);
1936 return;
1937 }
1938
1939 dbus_message_iter_get_fixed_array(&array, &capabilities, &capabilities_size);
1940 } else if (pa_streq(key, "Device")) {
1941 if (dbus_message_iter_get_arg_type(&variant_i) != DBUS_TYPE_OBJECT_PATH) {
1942 pa_log_warn("Remote endpoint %s property 'Device' is not path, ignoring", endpoint);
1943 return;
1944 }
1945
1946 dbus_message_iter_get_basic(&variant_i, &device_path);
1947 }
1948
1949 dbus_message_iter_next(&element_i);
1950 }
1951
1952 if (!uuid) {
1953 pa_log_warn("Remote endpoint %s does not have property 'UUID', ignoring", endpoint);
1954 return;
1955 }
1956
1957 if (!have_codec_id) {
1958 pa_log_warn("Remote endpoint %s does not have property 'Codec', ignoring", endpoint);
1959 return;
1960 }
1961
1962 if (!capabilities || !capabilities_size) {
1963 pa_log_warn("Remote endpoint %s does not have property 'Capabilities', ignoring", endpoint);
1964 return;
1965 }
1966
1967 if (!device_path) {
1968 pa_log_warn("Remote endpoint %s does not have property 'Device', ignoring", endpoint);
1969 return;
1970 }
1971
1972 device = pa_hashmap_get(y->devices, device_path);
1973 if (!device) {
1974 pa_log_warn("Device for remote endpoint %s was not found", endpoint);
1975 return;
1976 }
1977
1978 if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK)) {
1979 codec_endpoints = device->a2dp_sink_endpoints;
1980 } else if (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE)) {
1981 codec_endpoints = device->a2dp_source_endpoints;
1982 } else {
1983 pa_log_warn("Remote endpoint %s does not have valid property 'UUID', ignoring", endpoint);
1984 return;
1985 }
1986
1987 if (capabilities_size < 0 || capabilities_size > MAX_A2DP_CAPS_SIZE) {
1988 pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
1989 return;
1990 }
1991
1992 a2dp_codec_id = pa_xmalloc0(sizeof(*a2dp_codec_id));
1993 a2dp_codec_id->codec_id = codec_id;
1994 if (codec_id == A2DP_CODEC_VENDOR) {
1995 if ((size_t)capabilities_size < sizeof(a2dp_vendor_codec_t)) {
1996 pa_log_warn("Remote endpoint %s does not have valid property 'Capabilities', ignoring", endpoint);
1997 pa_xfree(a2dp_codec_id);
1998 return;
1999 }
2000 a2dp_codec_id->vendor_id = A2DP_GET_VENDOR_ID(*(a2dp_vendor_codec_t *)capabilities);
2001 a2dp_codec_id->vendor_codec_id = A2DP_GET_CODEC_ID(*(a2dp_vendor_codec_t *)capabilities);
2002 } else {
2003 a2dp_codec_id->vendor_id = 0;
2004 a2dp_codec_id->vendor_codec_id = 0;
2005 }
2006
2007 if (!pa_bluetooth_a2dp_codec_is_available(a2dp_codec_id, pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK))) {
2008 pa_xfree(a2dp_codec_id);
2009 return;
2010 }
2011
2012 a2dp_codec_capabilities = pa_xmalloc0(sizeof(*a2dp_codec_capabilities) + capabilities_size);
2013 a2dp_codec_capabilities->size = capabilities_size;
2014 memcpy(a2dp_codec_capabilities->buffer, capabilities, capabilities_size);
2015
2016 endpoints = pa_hashmap_get(codec_endpoints, a2dp_codec_id);
2017 if (!endpoints) {
2018 endpoints = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, pa_xfree, pa_xfree);
2019 pa_hashmap_put(codec_endpoints, a2dp_codec_id, endpoints);
2020 }
2021
2022 if (pa_hashmap_remove_and_free(endpoints, endpoint) >= 0)
2023 pa_log_debug("Replacing existing remote endpoint %s", endpoint);
2024 pa_hashmap_put(endpoints, pa_xstrdup(endpoint), a2dp_codec_capabilities);
2025 }
2026
parse_interfaces_and_properties(pa_bluetooth_discovery * y,DBusMessageIter * dict_i)2027 static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessageIter *dict_i) {
2028 DBusMessageIter element_i;
2029 const char *path;
2030 void *state;
2031 pa_bluetooth_device *d;
2032
2033 pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_OBJECT_PATH);
2034 dbus_message_iter_get_basic(dict_i, &path);
2035
2036 pa_assert_se(dbus_message_iter_next(dict_i));
2037 pa_assert(dbus_message_iter_get_arg_type(dict_i) == DBUS_TYPE_ARRAY);
2038
2039 dbus_message_iter_recurse(dict_i, &element_i);
2040
2041 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2042 DBusMessageIter iface_i;
2043 const char *interface;
2044
2045 dbus_message_iter_recurse(&element_i, &iface_i);
2046
2047 pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_STRING);
2048 dbus_message_iter_get_basic(&iface_i, &interface);
2049
2050 pa_assert_se(dbus_message_iter_next(&iface_i));
2051 pa_assert(dbus_message_iter_get_arg_type(&iface_i) == DBUS_TYPE_ARRAY);
2052
2053 if (pa_streq(interface, BLUEZ_ADAPTER_INTERFACE)) {
2054 pa_bluetooth_adapter *a;
2055
2056 if ((a = pa_hashmap_get(y->adapters, path))) {
2057 pa_log_error("Found duplicated D-Bus path for adapter %s", path);
2058 return;
2059 } else
2060 a = adapter_create(y, path);
2061
2062 pa_log_debug("Adapter %s found", path);
2063
2064 parse_adapter_properties(a, &iface_i, false);
2065
2066 if (!a->valid)
2067 return;
2068
2069 register_application(a);
2070 adapter_register_battery_provider(a);
2071 } else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
2072
2073 if ((d = pa_hashmap_get(y->devices, path))) {
2074 if (d->properties_received) {
2075 pa_log_error("Found duplicated D-Bus path for device %s", path);
2076 return;
2077 }
2078 } else
2079 d = device_create(y, path);
2080
2081 pa_log_debug("Device %s found", d->path);
2082
2083 parse_device_properties(d, &iface_i);
2084 } else if (pa_streq(interface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2085 parse_remote_endpoint_properties(y, path, &iface_i);
2086 } else
2087 pa_log_debug("Unknown interface %s found, skipping", interface);
2088
2089 dbus_message_iter_next(&element_i);
2090 }
2091
2092 PA_HASHMAP_FOREACH(d, y->devices, state) {
2093 if (d->properties_received && !d->tried_to_link_with_adapter) {
2094 if (d->adapter_path) {
2095 device_set_adapter(d, pa_hashmap_get(d->discovery->adapters, d->adapter_path));
2096
2097 if (!d->adapter)
2098 pa_log("Device %s points to a nonexistent adapter %s.", d->path, d->adapter_path);
2099 else if (!d->adapter->valid)
2100 pa_log("Device %s points to an invalid adapter %s.", d->path, d->adapter_path);
2101 }
2102
2103 d->tried_to_link_with_adapter = true;
2104 }
2105 }
2106
2107 return;
2108 }
2109
pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery * y,bool is_running)2110 void pa_bluetooth_discovery_set_ofono_running(pa_bluetooth_discovery *y, bool is_running) {
2111 pa_assert(y);
2112
2113 pa_log_debug("oFono is running: %s", pa_yes_no(is_running));
2114 if (y->headset_backend != HEADSET_BACKEND_AUTO)
2115 return;
2116
2117 pa_bluetooth_native_backend_enable_shared_profiles(y->native_backend, !is_running);
2118
2119 /* If ofono starts running, all devices that might be connected to the HS roles or HFP AG role
2120 * need to be disconnected, so that the devices can be handled by ofono */
2121 if (is_running) {
2122 void *state;
2123 pa_bluetooth_device *d;
2124
2125 PA_HASHMAP_FOREACH(d, y->devices, state) {
2126 if (pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_AG) || pa_bluetooth_device_supports_profile(d, PA_BLUETOOTH_PROFILE_HFP_HF)) {
2127 DBusMessage *m;
2128
2129 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, d->path, BLUEZ_DEVICE_INTERFACE, "Disconnect"));
2130 dbus_message_set_no_reply(m, true);
2131 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), m, NULL));
2132 dbus_message_unref(m);
2133 }
2134 }
2135 }
2136 }
2137
get_managed_objects_reply(DBusPendingCall * pending,void * userdata)2138 static void get_managed_objects_reply(DBusPendingCall *pending, void *userdata) {
2139 pa_dbus_pending *p;
2140 pa_bluetooth_discovery *y;
2141 DBusMessage *r;
2142 DBusMessageIter arg_i, element_i;
2143
2144 pa_assert_se(p = userdata);
2145 pa_assert_se(y = p->context_data);
2146 pa_assert_se(r = dbus_pending_call_steal_reply(pending));
2147
2148 if (dbus_message_is_error(r, DBUS_ERROR_UNKNOWN_METHOD)) {
2149 pa_log_warn("BlueZ D-Bus ObjectManager not available");
2150 goto finish;
2151 }
2152
2153 if (dbus_message_get_type(r) == DBUS_MESSAGE_TYPE_ERROR) {
2154 pa_log_error("GetManagedObjects() failed: %s: %s", dbus_message_get_error_name(r), pa_dbus_get_error_message(r));
2155 goto finish;
2156 }
2157
2158 if (!dbus_message_iter_init(r, &arg_i) || !pa_streq(dbus_message_get_signature(r), "a{oa{sa{sv}}}")) {
2159 pa_log_error("Invalid reply signature for GetManagedObjects()");
2160 goto finish;
2161 }
2162
2163 dbus_message_iter_recurse(&arg_i, &element_i);
2164 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_DICT_ENTRY) {
2165 DBusMessageIter dict_i;
2166
2167 dbus_message_iter_recurse(&element_i, &dict_i);
2168
2169 parse_interfaces_and_properties(y, &dict_i);
2170
2171 dbus_message_iter_next(&element_i);
2172 }
2173
2174 y->objects_listed = true;
2175
2176 if (!y->native_backend && y->headset_backend != HEADSET_BACKEND_OFONO)
2177 y->native_backend = pa_bluetooth_native_backend_new(y->core, y, (y->headset_backend == HEADSET_BACKEND_NATIVE));
2178 if (!y->ofono_backend && y->headset_backend != HEADSET_BACKEND_NATIVE)
2179 y->ofono_backend = pa_bluetooth_ofono_backend_new(y->core, y);
2180
2181 finish:
2182 dbus_message_unref(r);
2183
2184 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p);
2185 pa_dbus_pending_free(p);
2186 }
2187
get_managed_objects(pa_bluetooth_discovery * y)2188 static void get_managed_objects(pa_bluetooth_discovery *y) {
2189 DBusMessage *m;
2190
2191 pa_assert(y);
2192
2193 pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, "/", DBUS_INTERFACE_OBJECT_MANAGER,
2194 "GetManagedObjects"));
2195 send_and_add_to_pending(y, m, get_managed_objects_reply, NULL);
2196 }
2197
pa_bluetooth_discovery_hook(pa_bluetooth_discovery * y,pa_bluetooth_hook_t hook)2198 pa_hook* pa_bluetooth_discovery_hook(pa_bluetooth_discovery *y, pa_bluetooth_hook_t hook) {
2199 pa_assert(y);
2200 pa_assert(PA_REFCNT_VALUE(y) > 0);
2201
2202 return &y->hooks[hook];
2203 }
2204
filter_cb(DBusConnection * bus,DBusMessage * m,void * userdata)2205 static DBusHandlerResult filter_cb(DBusConnection *bus, DBusMessage *m, void *userdata) {
2206 pa_bluetooth_discovery *y;
2207 DBusError err;
2208
2209 pa_assert(bus);
2210 pa_assert(m);
2211 pa_assert_se(y = userdata);
2212
2213 dbus_error_init(&err);
2214
2215 if (dbus_message_is_signal(m, DBUS_INTERFACE_DBUS, "NameOwnerChanged")) {
2216 const char *name, *old_owner, *new_owner;
2217
2218 if (!dbus_message_get_args(m, &err,
2219 DBUS_TYPE_STRING, &name,
2220 DBUS_TYPE_STRING, &old_owner,
2221 DBUS_TYPE_STRING, &new_owner,
2222 DBUS_TYPE_INVALID)) {
2223 pa_log_error("Failed to parse " DBUS_INTERFACE_DBUS ".NameOwnerChanged: %s", err.message);
2224 goto fail;
2225 }
2226
2227 if (pa_streq(name, BLUEZ_SERVICE)) {
2228 if (old_owner && *old_owner) {
2229 pa_log_debug("Bluetooth daemon disappeared");
2230 pa_hashmap_remove_all(y->devices);
2231 pa_hashmap_remove_all(y->adapters);
2232 y->objects_listed = false;
2233 if (y->ofono_backend) {
2234 pa_bluetooth_ofono_backend_free(y->ofono_backend);
2235 y->ofono_backend = NULL;
2236 }
2237 if (y->native_backend) {
2238 pa_bluetooth_native_backend_free(y->native_backend);
2239 y->native_backend = NULL;
2240 }
2241 }
2242
2243 if (new_owner && *new_owner) {
2244 pa_log_debug("Bluetooth daemon appeared");
2245 get_managed_objects(y);
2246 }
2247 }
2248
2249 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2250 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded")) {
2251 DBusMessageIter arg_i;
2252
2253 if (!y->objects_listed)
2254 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2255
2256 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oa{sa{sv}}")) {
2257 pa_log_error("Invalid signature found in InterfacesAdded");
2258 goto fail;
2259 }
2260
2261 parse_interfaces_and_properties(y, &arg_i);
2262
2263 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2264 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesRemoved")) {
2265 const char *p;
2266 DBusMessageIter arg_i;
2267 DBusMessageIter element_i;
2268
2269 if (!y->objects_listed)
2270 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2271
2272 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "oas")) {
2273 pa_log_error("Invalid signature found in InterfacesRemoved");
2274 goto fail;
2275 }
2276
2277 dbus_message_iter_get_basic(&arg_i, &p);
2278
2279 pa_assert_se(dbus_message_iter_next(&arg_i));
2280 pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2281
2282 dbus_message_iter_recurse(&arg_i, &element_i);
2283
2284 while (dbus_message_iter_get_arg_type(&element_i) == DBUS_TYPE_STRING) {
2285 const char *iface;
2286
2287 dbus_message_iter_get_basic(&element_i, &iface);
2288
2289 if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE))
2290 device_remove(y, p);
2291 else if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE))
2292 adapter_remove(y, p);
2293 else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE))
2294 remote_endpoint_remove(y, p);
2295
2296 dbus_message_iter_next(&element_i);
2297 }
2298
2299 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2300
2301 } else if (dbus_message_is_signal(m, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged")) {
2302 DBusMessageIter arg_i;
2303 const char *iface;
2304
2305 if (!y->objects_listed)
2306 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; /* No reply received yet from GetManagedObjects */
2307
2308 if (!dbus_message_iter_init(m, &arg_i) || !pa_streq(dbus_message_get_signature(m), "sa{sv}as")) {
2309 pa_log_error("Invalid signature found in PropertiesChanged");
2310 goto fail;
2311 }
2312
2313 dbus_message_iter_get_basic(&arg_i, &iface);
2314
2315 pa_assert_se(dbus_message_iter_next(&arg_i));
2316 pa_assert(dbus_message_iter_get_arg_type(&arg_i) == DBUS_TYPE_ARRAY);
2317
2318 if (pa_streq(iface, BLUEZ_ADAPTER_INTERFACE)) {
2319 pa_bluetooth_adapter *a;
2320
2321 pa_log_debug("Properties changed in adapter %s", dbus_message_get_path(m));
2322
2323 if (!(a = pa_hashmap_get(y->adapters, dbus_message_get_path(m)))) {
2324 pa_log_warn("Properties changed in unknown adapter");
2325 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2326 }
2327
2328 parse_adapter_properties(a, &arg_i, true);
2329
2330 } else if (pa_streq(iface, BLUEZ_DEVICE_INTERFACE)) {
2331 pa_bluetooth_device *d;
2332
2333 pa_log_debug("Properties changed in device %s", dbus_message_get_path(m));
2334
2335 if (!(d = pa_hashmap_get(y->devices, dbus_message_get_path(m)))) {
2336 pa_log_warn("Properties changed in unknown device");
2337 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2338 }
2339
2340 if (!d->properties_received)
2341 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2342
2343 parse_device_properties(d, &arg_i);
2344 } else if (pa_streq(iface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
2345 pa_bluetooth_transport *t;
2346
2347 pa_log_debug("Properties changed in transport %s", dbus_message_get_path(m));
2348
2349 if (!(t = pa_hashmap_get(y->transports, dbus_message_get_path(m))))
2350 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2351
2352 parse_transport_properties(t, &arg_i);
2353 } else if (pa_streq(iface, BLUEZ_MEDIA_ENDPOINT_INTERFACE)) {
2354 pa_log_info("Properties changed in remote endpoint %s", dbus_message_get_path(m));
2355
2356 parse_remote_endpoint_properties(y, dbus_message_get_path(m), &arg_i);
2357 }
2358
2359 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2360 }
2361
2362 fail:
2363 dbus_error_free(&err);
2364
2365 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2366 }
2367
pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile)2368 const char *pa_bluetooth_profile_to_string(pa_bluetooth_profile_t profile) {
2369 switch(profile) {
2370 case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2371 return "a2dp_sink";
2372 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2373 return "a2dp_source";
2374 case PA_BLUETOOTH_PROFILE_HSP_HS:
2375 return "headset_head_unit";
2376 case PA_BLUETOOTH_PROFILE_HSP_AG:
2377 return "headset_audio_gateway";
2378 case PA_BLUETOOTH_PROFILE_HFP_HF:
2379 return "handsfree_head_unit";
2380 case PA_BLUETOOTH_PROFILE_HFP_AG:
2381 return "handsfree_audio_gateway";
2382 case PA_BLUETOOTH_PROFILE_OFF:
2383 return "off";
2384 }
2385
2386 return NULL;
2387 }
2388
2389 /* Returns true when PA has to perform attenuation, false if this is the
2390 * responsibility of the peer.
2391 *
2392 * `peer_profile` is the profile of the peer.
2393 *
2394 * When the peer is in the HFP/HSP Audio Gateway role (PA is in headset role) PA
2395 * has to perform attenuation on both the incoming and outgoing stream. In the
2396 * HandsFree/HeadSet role both are attenuated on the peer.
2397 */
pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile)2398 bool pa_bluetooth_profile_should_attenuate_volume(pa_bluetooth_profile_t peer_profile) {
2399 switch(peer_profile) {
2400 case PA_BLUETOOTH_PROFILE_A2DP_SINK:
2401 return false;
2402 case PA_BLUETOOTH_PROFILE_A2DP_SOURCE:
2403 return true;
2404 case PA_BLUETOOTH_PROFILE_HFP_HF:
2405 case PA_BLUETOOTH_PROFILE_HSP_HS:
2406 return false;
2407 case PA_BLUETOOTH_PROFILE_HFP_AG:
2408 case PA_BLUETOOTH_PROFILE_HSP_AG:
2409 return true;
2410 case PA_BLUETOOTH_PROFILE_OFF:
2411 pa_assert_not_reached();
2412 }
2413 pa_assert_not_reached();
2414 }
2415
pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile)2416 bool pa_bluetooth_profile_is_a2dp(pa_bluetooth_profile_t profile) {
2417 return profile == PA_BLUETOOTH_PROFILE_A2DP_SINK || profile == PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2418 }
2419
a2dp_sep_to_a2dp_endpoint_conf(const char * endpoint)2420 static const pa_a2dp_endpoint_conf *a2dp_sep_to_a2dp_endpoint_conf(const char *endpoint) {
2421 const char *codec_name;
2422
2423 if (pa_startswith(endpoint, A2DP_SINK_ENDPOINT "/"))
2424 codec_name = endpoint + strlen(A2DP_SINK_ENDPOINT "/");
2425 else if (pa_startswith(endpoint, A2DP_SOURCE_ENDPOINT "/"))
2426 codec_name = endpoint + strlen(A2DP_SOURCE_ENDPOINT "/");
2427 else
2428 return NULL;
2429
2430 return pa_bluetooth_get_a2dp_endpoint_conf(codec_name);
2431 }
2432
endpoint_set_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2433 static DBusMessage *endpoint_set_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2434 pa_bluetooth_discovery *y = userdata;
2435 pa_bluetooth_device *d;
2436 pa_bluetooth_transport *t;
2437 const pa_a2dp_endpoint_conf *endpoint_conf = NULL;
2438 const char *sender, *path, *endpoint_path, *dev_path = NULL, *uuid = NULL;
2439 const uint8_t *config = NULL;
2440 int size = 0;
2441 pa_bluetooth_profile_t p = PA_BLUETOOTH_PROFILE_OFF;
2442 DBusMessageIter args, props;
2443 DBusMessage *r;
2444
2445 if (!dbus_message_iter_init(m, &args) || !pa_streq(dbus_message_get_signature(m), "oa{sv}")) {
2446 pa_log_error("Invalid signature for method SetConfiguration()");
2447 goto fail2;
2448 }
2449
2450 dbus_message_iter_get_basic(&args, &path);
2451
2452 if (pa_hashmap_get(y->transports, path)) {
2453 pa_log_error("Endpoint SetConfiguration(): Transport %s is already configured.", path);
2454 goto fail2;
2455 }
2456
2457 pa_assert_se(dbus_message_iter_next(&args));
2458
2459 dbus_message_iter_recurse(&args, &props);
2460 if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
2461 goto fail;
2462
2463 endpoint_path = dbus_message_get_path(m);
2464
2465 /* Read transport properties */
2466 while (dbus_message_iter_get_arg_type(&props) == DBUS_TYPE_DICT_ENTRY) {
2467 const char *key;
2468 DBusMessageIter value, entry;
2469 int var;
2470
2471 dbus_message_iter_recurse(&props, &entry);
2472 dbus_message_iter_get_basic(&entry, &key);
2473
2474 dbus_message_iter_next(&entry);
2475 dbus_message_iter_recurse(&entry, &value);
2476
2477 var = dbus_message_iter_get_arg_type(&value);
2478
2479 if (pa_streq(key, "UUID")) {
2480 if (var != DBUS_TYPE_STRING) {
2481 pa_log_error("Property %s of wrong type %c", key, (char)var);
2482 goto fail;
2483 }
2484
2485 dbus_message_iter_get_basic(&value, &uuid);
2486
2487 if (pa_startswith(endpoint_path, A2DP_SINK_ENDPOINT "/"))
2488 p = PA_BLUETOOTH_PROFILE_A2DP_SOURCE;
2489 else if (pa_startswith(endpoint_path, A2DP_SOURCE_ENDPOINT "/"))
2490 p = PA_BLUETOOTH_PROFILE_A2DP_SINK;
2491
2492 if ((pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SOURCE) && p != PA_BLUETOOTH_PROFILE_A2DP_SINK) ||
2493 (pa_streq(uuid, PA_BLUETOOTH_UUID_A2DP_SINK) && p != PA_BLUETOOTH_PROFILE_A2DP_SOURCE)) {
2494 pa_log_error("UUID %s of transport %s incompatible with endpoint %s", uuid, path, endpoint_path);
2495 goto fail;
2496 }
2497 } else if (pa_streq(key, "Device")) {
2498 if (var != DBUS_TYPE_OBJECT_PATH) {
2499 pa_log_error("Property %s of wrong type %c", key, (char)var);
2500 goto fail;
2501 }
2502
2503 dbus_message_iter_get_basic(&value, &dev_path);
2504 } else if (pa_streq(key, "Configuration")) {
2505 DBusMessageIter array;
2506
2507 if (var != DBUS_TYPE_ARRAY) {
2508 pa_log_error("Property %s of wrong type %c", key, (char)var);
2509 goto fail;
2510 }
2511
2512 dbus_message_iter_recurse(&value, &array);
2513 var = dbus_message_iter_get_arg_type(&array);
2514 if (var != DBUS_TYPE_BYTE) {
2515 pa_log_error("%s is an array of wrong type %c", key, (char)var);
2516 goto fail;
2517 }
2518
2519 dbus_message_iter_get_fixed_array(&array, &config, &size);
2520
2521 endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2522 pa_assert(endpoint_conf);
2523
2524 if (!endpoint_conf->is_configuration_valid(config, size))
2525 goto fail;
2526 }
2527
2528 dbus_message_iter_next(&props);
2529 }
2530
2531 if (!endpoint_conf)
2532 goto fail2;
2533
2534 if ((d = pa_hashmap_get(y->devices, dev_path))) {
2535 if (!d->valid) {
2536 pa_log_error("Information about device %s is invalid", dev_path);
2537 goto fail2;
2538 }
2539 } else {
2540 /* InterfacesAdded signal is probably on its way, device_info_valid is kept as 0. */
2541 pa_log_warn("SetConfiguration() received for unknown device %s", dev_path);
2542 d = device_create(y, dev_path);
2543 }
2544
2545 if (d->transports[p] != NULL) {
2546 pa_log_error("Cannot configure transport %s because profile %s is already used", path, pa_bluetooth_profile_to_string(p));
2547 goto fail2;
2548 }
2549
2550 sender = dbus_message_get_sender(m);
2551
2552 pa_assert_se(r = dbus_message_new_method_return(m));
2553 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2554 dbus_message_unref(r);
2555
2556 t = pa_bluetooth_transport_new(d, sender, path, p, config, size);
2557 t->acquire = bluez5_transport_acquire_cb;
2558 t->release = bluez5_transport_release_cb;
2559 /* A2DP Absolute Volume is optional but BlueZ unconditionally reports
2560 * feature category 2, meaning supporting it is mandatory.
2561 * PulseAudio can and should perform the attenuation anyway in
2562 * the source role as it is the audio rendering device.
2563 */
2564 t->set_source_volume = pa_bluetooth_transport_set_source_volume;
2565
2566 pa_bluetooth_transport_reconfigure(t, &endpoint_conf->bt_codec, a2dp_transport_write, NULL);
2567 pa_bluetooth_transport_put(t);
2568
2569 pa_log_debug("Transport %s available for profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2570 pa_log_info("Selected codec: %s", endpoint_conf->bt_codec.name);
2571
2572 return NULL;
2573
2574 fail:
2575 pa_log_error("Endpoint SetConfiguration(): invalid arguments");
2576
2577 fail2:
2578 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to set configuration"));
2579 return r;
2580 }
2581
endpoint_select_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2582 static DBusMessage *endpoint_select_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2583 pa_bluetooth_discovery *y = userdata;
2584 const char *endpoint_path;
2585 uint8_t *cap;
2586 int size;
2587 const pa_a2dp_endpoint_conf *endpoint_conf;
2588 uint8_t config[MAX_A2DP_CAPS_SIZE];
2589 uint8_t *config_ptr = config;
2590 size_t config_size;
2591 DBusMessage *r;
2592 DBusError err;
2593
2594 endpoint_path = dbus_message_get_path(m);
2595
2596 dbus_error_init(&err);
2597
2598 if (!dbus_message_get_args(m, &err, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &cap, &size, DBUS_TYPE_INVALID)) {
2599 pa_log_error("Endpoint SelectConfiguration(): %s", err.message);
2600 dbus_error_free(&err);
2601 goto fail;
2602 }
2603
2604 endpoint_conf = a2dp_sep_to_a2dp_endpoint_conf(endpoint_path);
2605 pa_assert(endpoint_conf);
2606
2607 config_size = endpoint_conf->fill_preferred_configuration(&y->core->default_sample_spec, cap, size, config);
2608 if (config_size == 0)
2609 goto fail;
2610
2611 pa_assert_se(r = dbus_message_new_method_return(m));
2612 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &config_ptr, config_size, DBUS_TYPE_INVALID));
2613
2614 return r;
2615
2616 fail:
2617 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to select configuration"));
2618 return r;
2619 }
2620
endpoint_clear_configuration(DBusConnection * conn,DBusMessage * m,void * userdata)2621 static DBusMessage *endpoint_clear_configuration(DBusConnection *conn, DBusMessage *m, void *userdata) {
2622 pa_bluetooth_discovery *y = userdata;
2623 pa_bluetooth_transport *t;
2624 DBusMessage *r = NULL;
2625 DBusError err;
2626 const char *path;
2627
2628 dbus_error_init(&err);
2629
2630 if (!dbus_message_get_args(m, &err, DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) {
2631 pa_log_error("Endpoint ClearConfiguration(): %s", err.message);
2632 dbus_error_free(&err);
2633 goto fail;
2634 }
2635
2636 if ((t = pa_hashmap_get(y->transports, path))) {
2637 pa_log_debug("Clearing transport %s profile %s", t->path, pa_bluetooth_profile_to_string(t->profile));
2638 pa_bluetooth_transport_free(t);
2639 }
2640
2641 if (!dbus_message_get_no_reply(m))
2642 pa_assert_se(r = dbus_message_new_method_return(m));
2643
2644 return r;
2645
2646 fail:
2647 if (!dbus_message_get_no_reply(m))
2648 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to clear configuration"));
2649 return r;
2650 }
2651
endpoint_release(DBusConnection * conn,DBusMessage * m,void * userdata)2652 static DBusMessage *endpoint_release(DBusConnection *conn, DBusMessage *m, void *userdata) {
2653 DBusMessage *r = NULL;
2654
2655 /* From doc/media-api.txt in bluez:
2656 *
2657 * This method gets called when the service daemon
2658 * unregisters the endpoint. An endpoint can use it to do
2659 * cleanup tasks. There is no need to unregister the
2660 * endpoint, because when this method gets called it has
2661 * already been unregistered.
2662 *
2663 * We don't have any cleanup to do. */
2664
2665 /* Reply only if requested. Generally bluetoothd doesn't request a reply
2666 * to the Release() call. Sending replies when not requested on the system
2667 * bus tends to cause errors in syslog from dbus-daemon, because it
2668 * doesn't let unexpected replies through, so it's important to have this
2669 * check here. */
2670 if (!dbus_message_get_no_reply(m))
2671 pa_assert_se(r = dbus_message_new_method_return(m));
2672
2673 return r;
2674 }
2675
endpoint_handler(DBusConnection * c,DBusMessage * m,void * userdata)2676 static DBusHandlerResult endpoint_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2677 struct pa_bluetooth_discovery *y = userdata;
2678 DBusMessage *r = NULL;
2679 const char *path, *interface, *member;
2680
2681 pa_assert(y);
2682
2683 path = dbus_message_get_path(m);
2684 interface = dbus_message_get_interface(m);
2685 member = dbus_message_get_member(m);
2686
2687 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2688
2689 if (!a2dp_sep_to_a2dp_endpoint_conf(path))
2690 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2691
2692 if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2693 const char *xml = ENDPOINT_INTROSPECT_XML;
2694
2695 pa_assert_se(r = dbus_message_new_method_return(m));
2696 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2697
2698 } else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SetConfiguration"))
2699 r = endpoint_set_configuration(c, m, userdata);
2700 else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "SelectConfiguration"))
2701 r = endpoint_select_configuration(c, m, userdata);
2702 else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "ClearConfiguration"))
2703 r = endpoint_clear_configuration(c, m, userdata);
2704 else if (dbus_message_is_method_call(m, BLUEZ_MEDIA_ENDPOINT_INTERFACE, "Release"))
2705 r = endpoint_release(c, m, userdata);
2706 else
2707 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2708
2709 if (r) {
2710 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2711 dbus_message_unref(r);
2712 }
2713
2714 return DBUS_HANDLER_RESULT_HANDLED;
2715 }
2716
endpoint_init(pa_bluetooth_discovery * y,const char * endpoint)2717 static void endpoint_init(pa_bluetooth_discovery *y, const char *endpoint) {
2718 static const DBusObjectPathVTable vtable_endpoint = {
2719 .message_function = endpoint_handler,
2720 };
2721
2722 pa_assert(y);
2723 pa_assert(endpoint);
2724
2725 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection), endpoint,
2726 &vtable_endpoint, y));
2727 }
2728
endpoint_done(pa_bluetooth_discovery * y,const char * endpoint)2729 static void endpoint_done(pa_bluetooth_discovery *y, const char *endpoint) {
2730 pa_assert(y);
2731 pa_assert(endpoint);
2732
2733 dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection), endpoint);
2734 }
2735
append_a2dp_object(DBusMessageIter * iter,const char * endpoint,const char * uuid,uint8_t codec_id,uint8_t * capabilities,uint8_t capabilities_size)2736 static void append_a2dp_object(DBusMessageIter *iter, const char *endpoint, const char *uuid, uint8_t codec_id, uint8_t *capabilities, uint8_t capabilities_size) {
2737 const char *interface_name = BLUEZ_MEDIA_ENDPOINT_INTERFACE;
2738 DBusMessageIter object, array, entry, dict;
2739
2740 dbus_message_iter_open_container(iter, DBUS_TYPE_DICT_ENTRY, NULL, &object);
2741 pa_assert_se(dbus_message_iter_append_basic(&object, DBUS_TYPE_OBJECT_PATH, &endpoint));
2742
2743 dbus_message_iter_open_container(&object, DBUS_TYPE_ARRAY,
2744 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2745 DBUS_TYPE_STRING_AS_STRING
2746 DBUS_TYPE_ARRAY_AS_STRING
2747 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2748 DBUS_TYPE_STRING_AS_STRING
2749 DBUS_TYPE_VARIANT_AS_STRING
2750 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2751 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2752 &array);
2753
2754 dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
2755 pa_assert_se(dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &interface_name));
2756
2757 dbus_message_iter_open_container(&entry, DBUS_TYPE_ARRAY,
2758 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2759 DBUS_TYPE_STRING_AS_STRING
2760 DBUS_TYPE_VARIANT_AS_STRING
2761 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2762 &dict);
2763
2764 pa_dbus_append_basic_variant_dict_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
2765 pa_dbus_append_basic_variant_dict_entry(&dict, "Codec", DBUS_TYPE_BYTE, &codec_id);
2766 pa_dbus_append_basic_array_variant_dict_entry(&dict, "Capabilities", DBUS_TYPE_BYTE,
2767 capabilities, capabilities_size);
2768
2769 dbus_message_iter_close_container(&entry, &dict);
2770 dbus_message_iter_close_container(&array, &entry);
2771 dbus_message_iter_close_container(&object, &array);
2772 dbus_message_iter_close_container(iter, &object);
2773 }
2774
object_manager_handler(DBusConnection * c,DBusMessage * m,void * userdata)2775 static DBusHandlerResult object_manager_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
2776 struct pa_bluetooth_discovery *y = userdata;
2777 DBusMessage *r;
2778 const char *path, *interface, *member;
2779
2780 pa_assert(y);
2781
2782 path = dbus_message_get_path(m);
2783 interface = dbus_message_get_interface(m);
2784 member = dbus_message_get_member(m);
2785
2786 pa_log_debug("dbus: path=%s, interface=%s, member=%s", path, interface, member);
2787
2788 if (dbus_message_is_method_call(m, DBUS_INTERFACE_INTROSPECTABLE, "Introspect")) {
2789 const char *xml = OBJECT_MANAGER_INTROSPECT_XML;
2790
2791 pa_assert_se(r = dbus_message_new_method_return(m));
2792 pa_assert_se(dbus_message_append_args(r, DBUS_TYPE_STRING, &xml, DBUS_TYPE_INVALID));
2793 } else if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
2794 DBusMessageIter iter, array;
2795 int i;
2796
2797 pa_assert_se(r = dbus_message_new_method_return(m));
2798
2799 dbus_message_iter_init_append(r, &iter);
2800 dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
2801 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2802 DBUS_TYPE_OBJECT_PATH_AS_STRING
2803 DBUS_TYPE_ARRAY_AS_STRING
2804 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2805 DBUS_TYPE_STRING_AS_STRING
2806 DBUS_TYPE_ARRAY_AS_STRING
2807 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
2808 DBUS_TYPE_STRING_AS_STRING
2809 DBUS_TYPE_VARIANT_AS_STRING
2810 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2811 DBUS_DICT_ENTRY_END_CHAR_AS_STRING
2812 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
2813 &array);
2814
2815 for (i = 0; i < pa_bluetooth_a2dp_endpoint_conf_count(); i++) {
2816 const pa_a2dp_endpoint_conf *endpoint_conf;
2817 uint8_t capabilities[MAX_A2DP_CAPS_SIZE];
2818 uint8_t capabilities_size;
2819 uint8_t codec_id;
2820 char *endpoint;
2821
2822 endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2823
2824 codec_id = endpoint_conf->id.codec_id;
2825
2826 if (endpoint_conf->can_be_supported(false)) {
2827 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2828 pa_assert(capabilities_size != 0);
2829 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2830 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SINK, codec_id,
2831 capabilities, capabilities_size);
2832 pa_xfree(endpoint);
2833 }
2834
2835 if (endpoint_conf->can_be_supported(true)) {
2836 capabilities_size = endpoint_conf->fill_capabilities(capabilities);
2837 pa_assert(capabilities_size != 0);
2838 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2839 append_a2dp_object(&array, endpoint, PA_BLUETOOTH_UUID_A2DP_SOURCE, codec_id,
2840 capabilities, capabilities_size);
2841 pa_xfree(endpoint);
2842 }
2843 }
2844
2845 dbus_message_iter_close_container(&iter, &array);
2846 } else
2847 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
2848
2849 pa_assert_se(dbus_connection_send(pa_dbus_connection_get(y->connection), r, NULL));
2850 dbus_message_unref(r);
2851
2852 return DBUS_HANDLER_RESULT_HANDLED;
2853 }
2854
object_manager_init(pa_bluetooth_discovery * y)2855 static void object_manager_init(pa_bluetooth_discovery *y) {
2856 static const DBusObjectPathVTable vtable = {
2857 .message_function = object_manager_handler,
2858 };
2859
2860 pa_assert(y);
2861 pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(y->connection),
2862 A2DP_OBJECT_MANAGER_PATH, &vtable, y));
2863 }
2864
object_manager_done(pa_bluetooth_discovery * y)2865 static void object_manager_done(pa_bluetooth_discovery *y) {
2866 pa_assert(y);
2867 dbus_connection_unregister_object_path(pa_dbus_connection_get(y->connection),
2868 A2DP_OBJECT_MANAGER_PATH);
2869 }
2870
pa_bluetooth_discovery_get(pa_core * c,int headset_backend,bool enable_native_hsp_hs,bool enable_native_hfp_hf,bool enable_msbc)2871 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) {
2872 pa_bluetooth_discovery *y;
2873 DBusError err;
2874 DBusConnection *conn;
2875 unsigned i, count;
2876 const pa_a2dp_endpoint_conf *endpoint_conf;
2877 char *endpoint;
2878
2879 pa_bluetooth_a2dp_codec_gst_init();
2880 y = pa_xnew0(pa_bluetooth_discovery, 1);
2881 PA_REFCNT_INIT(y);
2882 y->core = c;
2883 y->headset_backend = headset_backend;
2884 y->enable_native_hsp_hs = enable_native_hsp_hs;
2885 y->enable_native_hfp_hf = enable_native_hfp_hf;
2886 y->enable_msbc = enable_msbc;
2887 y->adapters = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2888 (pa_free_cb_t) adapter_free);
2889 y->devices = pa_hashmap_new_full(pa_idxset_string_hash_func, pa_idxset_string_compare_func, NULL,
2890 (pa_free_cb_t) device_free);
2891 y->transports = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
2892 PA_LLIST_HEAD_INIT(pa_dbus_pending, y->pending);
2893
2894 for (i = 0; i < PA_BLUETOOTH_HOOK_MAX; i++)
2895 pa_hook_init(&y->hooks[i], y);
2896
2897 pa_shared_set(c, "bluetooth-discovery", y);
2898
2899 dbus_error_init(&err);
2900
2901 if (!(y->connection = pa_dbus_bus_get(y->core, DBUS_BUS_SYSTEM, &err))) {
2902 pa_log_error("Failed to get D-Bus connection: %s", err.message);
2903 goto fail;
2904 }
2905
2906 conn = pa_dbus_connection_get(y->connection);
2907
2908 /* dynamic detection of bluetooth audio devices */
2909 if (!dbus_connection_add_filter(conn, filter_cb, y, NULL)) {
2910 pa_log_error("Failed to add filter function");
2911 goto fail;
2912 }
2913 y->filter_added = true;
2914
2915 if (pa_dbus_add_matches(conn, &err,
2916 "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged'"
2917 ",arg0='" BLUEZ_SERVICE "'",
2918 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',member='InterfacesAdded'",
2919 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
2920 "member='InterfacesRemoved'",
2921 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2922 ",arg0='" BLUEZ_ADAPTER_INTERFACE "'",
2923 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2924 ",arg0='" BLUEZ_DEVICE_INTERFACE "'",
2925 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2926 ",arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
2927 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',member='PropertiesChanged'"
2928 ",arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
2929 NULL) < 0) {
2930 pa_log_error("Failed to add D-Bus matches: %s", err.message);
2931 goto fail;
2932 }
2933 y->matches_added = true;
2934
2935 object_manager_init(y);
2936
2937 count = pa_bluetooth_a2dp_endpoint_conf_count();
2938 for (i = 0; i < count; i++) {
2939 endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
2940 if (endpoint_conf->can_be_supported(false)) {
2941 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
2942 endpoint_init(y, endpoint);
2943 pa_xfree(endpoint);
2944 }
2945
2946 if (endpoint_conf->can_be_supported(true)) {
2947 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
2948 endpoint_init(y, endpoint);
2949 pa_xfree(endpoint);
2950 }
2951 }
2952
2953 get_managed_objects(y);
2954
2955 return y;
2956
2957 fail:
2958 pa_bluetooth_discovery_unref(y);
2959 dbus_error_free(&err);
2960
2961 return NULL;
2962 }
2963
pa_bluetooth_discovery_ref(pa_bluetooth_discovery * y)2964 pa_bluetooth_discovery* pa_bluetooth_discovery_ref(pa_bluetooth_discovery *y) {
2965 pa_assert(y);
2966 pa_assert(PA_REFCNT_VALUE(y) > 0);
2967
2968 PA_REFCNT_INC(y);
2969
2970 return y;
2971 }
2972
pa_bluetooth_discovery_unref(pa_bluetooth_discovery * y)2973 void pa_bluetooth_discovery_unref(pa_bluetooth_discovery *y) {
2974 unsigned i, count;
2975 const pa_a2dp_endpoint_conf *endpoint_conf;
2976 char *endpoint;
2977
2978 pa_assert(y);
2979 pa_assert(PA_REFCNT_VALUE(y) > 0);
2980
2981 if (PA_REFCNT_DEC(y) > 0)
2982 return;
2983
2984 pa_dbus_free_pending_list(&y->pending);
2985
2986 if (y->ofono_backend)
2987 pa_bluetooth_ofono_backend_free(y->ofono_backend);
2988 if (y->native_backend)
2989 pa_bluetooth_native_backend_free(y->native_backend);
2990
2991 if (y->adapters)
2992 pa_hashmap_free(y->adapters);
2993
2994 if (y->devices)
2995 pa_hashmap_free(y->devices);
2996
2997 if (y->transports) {
2998 pa_assert(pa_hashmap_isempty(y->transports));
2999 pa_hashmap_free(y->transports);
3000 }
3001
3002 if (y->connection) {
3003
3004 if (y->matches_added)
3005 pa_dbus_remove_matches(pa_dbus_connection_get(y->connection),
3006 "type='signal',sender='" DBUS_SERVICE_DBUS "',interface='" DBUS_INTERFACE_DBUS "',member='NameOwnerChanged',"
3007 "arg0='" BLUEZ_SERVICE "'",
3008 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3009 "member='InterfacesAdded'",
3010 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_OBJECT_MANAGER "',"
3011 "member='InterfacesRemoved'",
3012 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3013 "member='PropertiesChanged',arg0='" BLUEZ_ADAPTER_INTERFACE "'",
3014 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3015 "member='PropertiesChanged',arg0='" BLUEZ_DEVICE_INTERFACE "'",
3016 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3017 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_ENDPOINT_INTERFACE "'",
3018 "type='signal',sender='" BLUEZ_SERVICE "',interface='" DBUS_INTERFACE_PROPERTIES "',"
3019 "member='PropertiesChanged',arg0='" BLUEZ_MEDIA_TRANSPORT_INTERFACE "'",
3020 NULL);
3021
3022 if (y->filter_added)
3023 dbus_connection_remove_filter(pa_dbus_connection_get(y->connection), filter_cb, y);
3024
3025 object_manager_done(y);
3026
3027 count = pa_bluetooth_a2dp_endpoint_conf_count();
3028 for (i = 0; i < count; i++) {
3029 endpoint_conf = pa_bluetooth_a2dp_endpoint_conf_iter(i);
3030
3031 if (endpoint_conf->can_be_supported(false)) {
3032 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SINK_ENDPOINT, endpoint_conf->bt_codec.name);
3033 endpoint_done(y, endpoint);
3034 pa_xfree(endpoint);
3035 }
3036
3037 if (endpoint_conf->can_be_supported(true)) {
3038 endpoint = pa_sprintf_malloc("%s/%s", A2DP_SOURCE_ENDPOINT, endpoint_conf->bt_codec.name);
3039 endpoint_done(y, endpoint);
3040 pa_xfree(endpoint);
3041 }
3042 }
3043
3044 pa_dbus_connection_unref(y->connection);
3045 }
3046
3047 pa_shared_remove(y->core, "bluetooth-discovery");
3048 pa_xfree(y);
3049 }
3050