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 105static 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 118static 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 131struct 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 153static 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 170static 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 195pa_bluetooth_profile_status_t profile_status_get(pa_bluetooth_discovery *y, pa_bluetooth_profile_t profile) { 196 return y->profiles_status[profile]; 197} 198 199void 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 203pa_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 228void 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 244static 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 257bool 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 322static 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 329static 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 344static 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 352static 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 385static 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 392struct 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 400static 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 440bool 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 509void 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 570static 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 620static 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 626static 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 632static 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 676void 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 684void 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 692void 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 705static 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 747finish: 748 dbus_message_unref(r); 749 return ret; 750} 751 752static 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 783static 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 829finish: 830 dbus_message_unref(r); 831 832 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); 833 pa_dbus_pending_free(p); 834} 835 836static 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 856void 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 868static 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 907bool 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 */ 923static 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 */ 929static 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 934static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object); 935static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *object, bool only_percentage); 936 937void 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 */ 981void 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 1016static 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 1030static 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 1078static 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 1096static 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 1110static 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 1132static 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 1149static 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 1168pa_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 1181bool 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 1189bool 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 1197bool 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 1205pa_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 1221static 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 1253static 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 1264static 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 1279static 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 1301static 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 1312static 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 1336static 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 1361static 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 1417static 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 1452static 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 1485static 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 1501static 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 1520static 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 1531static 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 1639static 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 1661static 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 1729static 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 1752finish: 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 1761static 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 1793static 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 1829finish: 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 1850static 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 1878static 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 2027static 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 2110void 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 2138static 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 2181finish: 2182 dbus_message_unref(r); 2183 2184 PA_LLIST_REMOVE(pa_dbus_pending, y->pending, p); 2185 pa_dbus_pending_free(p); 2186} 2187 2188static 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 2198pa_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 2205static 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 2362fail: 2363 dbus_error_free(&err); 2364 2365 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 2366} 2367 2368const 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 */ 2398bool 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 2416bool 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 2420static 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 2433static 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 2574fail: 2575 pa_log_error("Endpoint SetConfiguration(): invalid arguments"); 2576 2577fail2: 2578 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to set configuration")); 2579 return r; 2580} 2581 2582static 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 2616fail: 2617 pa_assert_se(r = dbus_message_new_error(m, BLUEZ_ERROR_INVALID_ARGUMENTS, "Unable to select configuration")); 2618 return r; 2619} 2620 2621static 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 2646fail: 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 2652static 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 2676static 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 2717static 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 2729static 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 2736static 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 2775static 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 2855static 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 2865static 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 2871pa_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 2957fail: 2958 pa_bluetooth_discovery_unref(y); 2959 dbus_error_free(&err); 2960 2961 return NULL; 2962} 2963 2964pa_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 2973void 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