1 /* Copyright (c) 2013 The Chromium Authors. All rights reserved. 2 * Use of this source code is governed by a BSD-style license that can be 3 * found in the LICENSE file. 4 */ 5 6 #include <dbus/dbus.h> 7 #include <errno.h> 8 #include <stdlib.h> 9 #include <string.h> 10 #include <syslog.h> 11 12 #include "cras_bt_constants.h" 13 #include "cras_bt_adapter.h" 14 #include "cras_bt_endpoint.h" 15 #include "cras_bt_transport.h" 16 #include "utlist.h" 17 18 /* Defined by doc/media-api.txt in the BlueZ source */ 19 #define ENDPOINT_INTROSPECT_XML \ 20 DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \ 21 "<node>\n" \ 22 " <interface name=\"org.bluez.MediaEndpoint\">\n" \ 23 " <method name=\"SetConfiguration\">\n" \ 24 " <arg name=\"transport\" type=\"o\" direction=\"in\"/>\n" \ 25 " <arg name=\"configuration\" type=\"a{sv}\" direction=\"in\"/>\n"\ 26 " </method>\n" \ 27 " <method name=\"SelectConfiguration\">\n" \ 28 " <arg name=\"capabilities\" type=\"ay\" direction=\"in\"/>\n"\ 29 " <arg name=\"configuration\" type=\"ay\" direction=\"out\"/>\n"\ 30 " </method>\n" \ 31 " <method name=\"ClearConfiguration\">\n" \ 32 " </method>\n" \ 33 " <method name=\"Release\">\n" \ 34 " </method>\n" \ 35 " </interface>\n" \ 36 " <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \ 37 " <method name=\"Introspect\">\n" \ 38 " <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \ 39 " </method>\n" \ 40 " </interface>\n" \ 41 "</node>\n" 42 43 44 static void cras_bt_endpoint_suspend(struct cras_bt_endpoint *endpoint) 45 { 46 if (!endpoint->transport) 47 return; 48 49 endpoint->suspend(endpoint, endpoint->transport); 50 51 cras_bt_transport_set_endpoint(endpoint->transport, NULL); 52 endpoint->transport = NULL; 53 } 54 55 static DBusHandlerResult cras_bt_endpoint_set_configuration( 56 DBusConnection *conn, 57 DBusMessage *message, 58 void *arg) 59 { 60 DBusMessageIter message_iter, properties_array_iter; 61 const char *endpoint_path, *transport_path; 62 struct cras_bt_endpoint *endpoint; 63 struct cras_bt_transport *transport; 64 DBusMessage *reply; 65 66 syslog(LOG_DEBUG, "SetConfiguration: %s", 67 dbus_message_get_path(message)); 68 69 endpoint_path = dbus_message_get_path(message); 70 endpoint = cras_bt_endpoint_get(endpoint_path); 71 if (!endpoint) 72 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 73 74 if (!dbus_message_has_signature(message, "oa{sv}")) { 75 syslog(LOG_WARNING, "Bad SetConfiguration message received."); 76 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 77 } 78 79 dbus_message_iter_init(message, &message_iter); 80 81 dbus_message_iter_get_basic(&message_iter, &transport_path); 82 dbus_message_iter_next(&message_iter); 83 84 dbus_message_iter_recurse(&message_iter, &properties_array_iter); 85 86 transport = cras_bt_transport_get(transport_path); 87 if (transport) { 88 cras_bt_transport_update_properties(transport, 89 &properties_array_iter, 90 NULL); 91 } else { 92 transport = cras_bt_transport_create(conn, transport_path); 93 if (transport) { 94 cras_bt_transport_update_properties( 95 transport, 96 &properties_array_iter, 97 NULL); 98 syslog(LOG_INFO, "Bluetooth Transport: %s added", 99 cras_bt_transport_object_path(transport)); 100 } 101 } 102 103 if (!cras_bt_transport_device(transport)) { 104 syslog(LOG_ERR, "Do device found for transport %s", 105 cras_bt_transport_object_path(transport)); 106 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 107 } 108 109 cras_bt_transport_set_endpoint(transport, endpoint); 110 endpoint->transport = transport; 111 endpoint->set_configuration(endpoint, transport); 112 113 reply = dbus_message_new_method_return(message); 114 if (!reply) 115 return DBUS_HANDLER_RESULT_NEED_MEMORY; 116 if (!dbus_connection_send(conn, reply, NULL)) 117 return DBUS_HANDLER_RESULT_NEED_MEMORY; 118 119 dbus_message_unref(reply); 120 return DBUS_HANDLER_RESULT_HANDLED; 121 } 122 123 static DBusHandlerResult cras_bt_endpoint_select_configuration( 124 DBusConnection *conn, 125 DBusMessage *message, 126 void *arg) 127 { 128 DBusError dbus_error; 129 const char *endpoint_path; 130 struct cras_bt_endpoint *endpoint; 131 char buf[4]; 132 void *capabilities, *configuration = buf; 133 int len; 134 DBusMessage *reply; 135 136 syslog(LOG_DEBUG, "SelectConfiguration: %s", 137 dbus_message_get_path(message)); 138 139 endpoint_path = dbus_message_get_path(message); 140 endpoint = cras_bt_endpoint_get(endpoint_path); 141 if (!endpoint) 142 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 143 144 dbus_error_init(&dbus_error); 145 146 if (!dbus_message_get_args(message, &dbus_error, 147 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, 148 &capabilities, &len, 149 DBUS_TYPE_INVALID)) { 150 syslog(LOG_WARNING, "Bad SelectConfiguration method call: %s", 151 dbus_error.message); 152 dbus_error_free(&dbus_error); 153 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 154 } 155 156 if (len > sizeof(configuration) || 157 endpoint->select_configuration(endpoint, capabilities, len, 158 configuration) < 0) { 159 reply = dbus_message_new_error( 160 message, 161 "org.chromium.Cras.Error.UnsupportedConfiguration", 162 "Unable to select configuration from capabilities"); 163 164 if (!dbus_connection_send(conn, reply, NULL)) 165 return DBUS_HANDLER_RESULT_NEED_MEMORY; 166 167 dbus_message_unref(reply); 168 return DBUS_HANDLER_RESULT_HANDLED; 169 } 170 171 reply = dbus_message_new_method_return(message); 172 if (!reply) 173 return DBUS_HANDLER_RESULT_NEED_MEMORY; 174 if (!dbus_message_append_args(reply, 175 DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, 176 &configuration, len, 177 DBUS_TYPE_INVALID)) 178 return DBUS_HANDLER_RESULT_NEED_MEMORY; 179 if (!dbus_connection_send(conn, reply, NULL)) 180 return DBUS_HANDLER_RESULT_NEED_MEMORY; 181 182 dbus_message_unref(reply); 183 return DBUS_HANDLER_RESULT_HANDLED; 184 } 185 186 static DBusHandlerResult cras_bt_endpoint_clear_configuration( 187 DBusConnection *conn, 188 DBusMessage *message, 189 void *arg) 190 { 191 DBusError dbus_error; 192 const char *endpoint_path, *transport_path; 193 struct cras_bt_endpoint *endpoint; 194 struct cras_bt_transport *transport; 195 DBusMessage *reply; 196 197 syslog(LOG_DEBUG, "ClearConfiguration: %s", 198 dbus_message_get_path(message)); 199 200 endpoint_path = dbus_message_get_path(message); 201 endpoint = cras_bt_endpoint_get(endpoint_path); 202 if (!endpoint) 203 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 204 205 dbus_error_init(&dbus_error); 206 207 if (!dbus_message_get_args(message, &dbus_error, 208 DBUS_TYPE_OBJECT_PATH, &transport_path, 209 DBUS_TYPE_INVALID)) { 210 syslog(LOG_WARNING, "Bad ClearConfiguration method call: %s", 211 dbus_error.message); 212 dbus_error_free(&dbus_error); 213 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 214 } 215 216 transport = cras_bt_transport_get(transport_path); 217 218 if (transport == endpoint->transport) 219 cras_bt_endpoint_suspend(endpoint); 220 221 reply = dbus_message_new_method_return(message); 222 if (!reply) 223 return DBUS_HANDLER_RESULT_NEED_MEMORY; 224 if (!dbus_connection_send(conn, reply, NULL)) 225 return DBUS_HANDLER_RESULT_NEED_MEMORY; 226 227 dbus_message_unref(reply); 228 return DBUS_HANDLER_RESULT_HANDLED; 229 } 230 231 static DBusHandlerResult cras_bt_endpoint_release(DBusConnection *conn, 232 DBusMessage *message, 233 void *arg) 234 { 235 const char *endpoint_path; 236 struct cras_bt_endpoint *endpoint; 237 DBusMessage *reply; 238 239 syslog(LOG_DEBUG, "Release: %s", 240 dbus_message_get_path(message)); 241 242 endpoint_path = dbus_message_get_path(message); 243 endpoint = cras_bt_endpoint_get(endpoint_path); 244 if (!endpoint) 245 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 246 247 cras_bt_endpoint_suspend(endpoint); 248 249 reply = dbus_message_new_method_return(message); 250 if (!reply) 251 return DBUS_HANDLER_RESULT_NEED_MEMORY; 252 if (!dbus_connection_send(conn, reply, NULL)) 253 return DBUS_HANDLER_RESULT_NEED_MEMORY; 254 255 dbus_message_unref(reply); 256 return DBUS_HANDLER_RESULT_HANDLED; 257 } 258 259 static DBusHandlerResult cras_bt_handle_endpoint_message(DBusConnection *conn, 260 DBusMessage *message, 261 void *arg) 262 { 263 syslog(LOG_DEBUG, "Endpoint message: %s %s %s", 264 dbus_message_get_path(message), 265 dbus_message_get_interface(message), 266 dbus_message_get_member(message)); 267 268 if (dbus_message_is_method_call(message, 269 DBUS_INTERFACE_INTROSPECTABLE, 270 "Introspect")) { 271 DBusMessage *reply; 272 const char *xml = ENDPOINT_INTROSPECT_XML; 273 274 reply = dbus_message_new_method_return(message); 275 if (!reply) 276 return DBUS_HANDLER_RESULT_NEED_MEMORY; 277 if (!dbus_message_append_args(reply, 278 DBUS_TYPE_STRING, &xml, 279 DBUS_TYPE_INVALID)) 280 return DBUS_HANDLER_RESULT_NEED_MEMORY; 281 if (!dbus_connection_send(conn, reply, NULL)) 282 return DBUS_HANDLER_RESULT_NEED_MEMORY; 283 284 dbus_message_unref(reply); 285 return DBUS_HANDLER_RESULT_HANDLED; 286 287 } else if (dbus_message_is_method_call(message, 288 BLUEZ_INTERFACE_MEDIA_ENDPOINT, 289 "SetConfiguration")) { 290 return cras_bt_endpoint_set_configuration(conn, message, arg); 291 292 } else if (dbus_message_is_method_call(message, 293 BLUEZ_INTERFACE_MEDIA_ENDPOINT, 294 "SelectConfiguration")) { 295 return cras_bt_endpoint_select_configuration( 296 conn, message, arg); 297 298 } else if (dbus_message_is_method_call(message, 299 BLUEZ_INTERFACE_MEDIA_ENDPOINT, 300 "ClearConfiguration")) { 301 return cras_bt_endpoint_clear_configuration(conn, message, arg); 302 303 } else if (dbus_message_is_method_call(message, 304 BLUEZ_INTERFACE_MEDIA_ENDPOINT, 305 "Release")) { 306 return cras_bt_endpoint_release(conn, message, arg); 307 308 } else { 309 syslog(LOG_DEBUG, "%s: %s.%s: Unknown MediaEndpoint message", 310 dbus_message_get_path(message), 311 dbus_message_get_interface(message), 312 dbus_message_get_member(message)); 313 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 314 } 315 } 316 317 318 static void cras_bt_on_register_endpoint(DBusPendingCall *pending_call, 319 void *data) 320 { 321 DBusMessage *reply; 322 323 reply = dbus_pending_call_steal_reply(pending_call); 324 dbus_pending_call_unref(pending_call); 325 326 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { 327 syslog(LOG_WARNING, "RegisterEndpoint returned error: %s", 328 dbus_message_get_error_name(reply)); 329 dbus_message_unref(reply); 330 return; 331 } 332 333 dbus_message_unref(reply); 334 } 335 336 int cras_bt_register_endpoint(DBusConnection *conn, 337 const struct cras_bt_adapter *adapter, 338 struct cras_bt_endpoint *endpoint) 339 { 340 const char *adapter_path, *key; 341 DBusMessage *method_call; 342 DBusMessageIter message_iter; 343 DBusMessageIter properties_array_iter, properties_dict_iter; 344 DBusMessageIter variant_iter, bytes_iter; 345 DBusPendingCall *pending_call; 346 char buf[4]; 347 void *capabilities = buf; 348 int len = sizeof(buf); 349 int error; 350 351 error = endpoint->get_capabilities(endpoint, capabilities, &len); 352 if (error < 0) 353 return error; 354 355 adapter_path = cras_bt_adapter_object_path(adapter); 356 357 method_call = dbus_message_new_method_call(BLUEZ_SERVICE, 358 adapter_path, 359 BLUEZ_INTERFACE_MEDIA, 360 "RegisterEndpoint"); 361 if (!method_call) 362 return -ENOMEM; 363 364 dbus_message_iter_init_append(method_call, &message_iter); 365 dbus_message_iter_append_basic(&message_iter, 366 DBUS_TYPE_OBJECT_PATH, 367 &endpoint->object_path); 368 369 dbus_message_iter_open_container(&message_iter, 370 DBUS_TYPE_ARRAY, 371 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING 372 DBUS_TYPE_STRING_AS_STRING 373 DBUS_TYPE_VARIANT_AS_STRING 374 DBUS_DICT_ENTRY_END_CHAR_AS_STRING, 375 &properties_array_iter); 376 377 key = "UUID"; 378 dbus_message_iter_open_container(&properties_array_iter, 379 DBUS_TYPE_DICT_ENTRY, NULL, 380 &properties_dict_iter); 381 dbus_message_iter_append_basic(&properties_dict_iter, 382 DBUS_TYPE_STRING, &key); 383 dbus_message_iter_open_container(&properties_dict_iter, 384 DBUS_TYPE_VARIANT, 385 DBUS_TYPE_STRING_AS_STRING, 386 &variant_iter); 387 dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_STRING, 388 &endpoint->uuid); 389 dbus_message_iter_close_container(&properties_dict_iter, &variant_iter); 390 dbus_message_iter_close_container(&properties_array_iter, 391 &properties_dict_iter); 392 393 key = "Codec"; 394 dbus_message_iter_open_container(&properties_array_iter, 395 DBUS_TYPE_DICT_ENTRY, NULL, 396 &properties_dict_iter); 397 dbus_message_iter_append_basic(&properties_dict_iter, 398 DBUS_TYPE_STRING, &key); 399 dbus_message_iter_open_container(&properties_dict_iter, 400 DBUS_TYPE_VARIANT, 401 DBUS_TYPE_BYTE_AS_STRING, 402 &variant_iter); 403 dbus_message_iter_append_basic(&variant_iter, DBUS_TYPE_BYTE, 404 &endpoint->codec); 405 dbus_message_iter_close_container(&properties_dict_iter, &variant_iter); 406 dbus_message_iter_close_container(&properties_array_iter, 407 &properties_dict_iter); 408 409 key = "Capabilities"; 410 dbus_message_iter_open_container(&properties_array_iter, 411 DBUS_TYPE_DICT_ENTRY, NULL, 412 &properties_dict_iter); 413 dbus_message_iter_append_basic(&properties_dict_iter, 414 DBUS_TYPE_STRING, &key); 415 dbus_message_iter_open_container(&properties_dict_iter, 416 DBUS_TYPE_VARIANT, 417 DBUS_TYPE_ARRAY_AS_STRING 418 DBUS_TYPE_BYTE_AS_STRING, 419 &variant_iter); 420 dbus_message_iter_open_container(&variant_iter, DBUS_TYPE_ARRAY, 421 DBUS_TYPE_BYTE_AS_STRING, 422 &bytes_iter); 423 dbus_message_iter_append_fixed_array(&bytes_iter, DBUS_TYPE_BYTE, 424 &capabilities, len); 425 dbus_message_iter_close_container(&variant_iter, &bytes_iter); 426 dbus_message_iter_close_container(&properties_dict_iter, &variant_iter); 427 dbus_message_iter_close_container(&properties_array_iter, 428 &properties_dict_iter); 429 430 dbus_message_iter_close_container(&message_iter, 431 &properties_array_iter); 432 433 if (!dbus_connection_send_with_reply(conn, method_call, &pending_call, 434 DBUS_TIMEOUT_USE_DEFAULT)) { 435 dbus_message_unref(method_call); 436 return -ENOMEM; 437 } 438 439 dbus_message_unref(method_call); 440 if (!pending_call) 441 return -EIO; 442 443 if (!dbus_pending_call_set_notify(pending_call, 444 cras_bt_on_register_endpoint, 445 NULL, NULL)) { 446 dbus_pending_call_cancel(pending_call); 447 dbus_pending_call_unref(pending_call); 448 return -ENOMEM; 449 } 450 451 return 0; 452 } 453 454 static void cras_bt_on_unregister_endpoint(DBusPendingCall *pending_call, 455 void *data) 456 { 457 DBusMessage *reply; 458 459 reply = dbus_pending_call_steal_reply(pending_call); 460 dbus_pending_call_unref(pending_call); 461 462 if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { 463 syslog(LOG_WARNING, "UnregisterEndpoint returned error: %s", 464 dbus_message_get_error_name(reply)); 465 dbus_message_unref(reply); 466 return; 467 } 468 469 dbus_message_unref(reply); 470 } 471 472 int cras_bt_unregister_endpoint(DBusConnection *conn, 473 const struct cras_bt_adapter *adapter, 474 struct cras_bt_endpoint *endpoint) 475 { 476 const char *adapter_path; 477 DBusMessage *method_call; 478 DBusPendingCall *pending_call; 479 480 adapter_path = cras_bt_adapter_object_path(adapter); 481 482 method_call = dbus_message_new_method_call(BLUEZ_SERVICE, 483 adapter_path, 484 BLUEZ_INTERFACE_MEDIA, 485 "UnregisterEndpoint"); 486 if (!method_call) 487 return -ENOMEM; 488 489 if (!dbus_message_append_args(method_call, 490 DBUS_TYPE_OBJECT_PATH, 491 &endpoint->object_path, 492 DBUS_TYPE_INVALID)) 493 return -ENOMEM; 494 495 if (!dbus_connection_send_with_reply(conn, method_call, &pending_call, 496 DBUS_TIMEOUT_USE_DEFAULT)) { 497 dbus_message_unref(method_call); 498 return -ENOMEM; 499 } 500 501 dbus_message_unref(method_call); 502 if (!pending_call) 503 return -EIO; 504 505 if (!dbus_pending_call_set_notify(pending_call, 506 cras_bt_on_unregister_endpoint, 507 NULL, NULL)) { 508 dbus_pending_call_cancel(pending_call); 509 dbus_pending_call_unref(pending_call); 510 return -ENOMEM; 511 } 512 513 return 0; 514 } 515 516 517 /* Available endpoints */ 518 static struct cras_bt_endpoint *endpoints; 519 520 int cras_bt_register_endpoints(DBusConnection *conn, 521 const struct cras_bt_adapter *adapter) 522 { 523 struct cras_bt_endpoint *endpoint; 524 525 DL_FOREACH(endpoints, endpoint) 526 cras_bt_register_endpoint(conn, adapter, endpoint); 527 528 return 0; 529 } 530 531 int cras_bt_endpoint_add(DBusConnection *conn, 532 struct cras_bt_endpoint *endpoint) 533 { 534 static const DBusObjectPathVTable endpoint_vtable = { 535 .message_function = cras_bt_handle_endpoint_message 536 }; 537 538 DBusError dbus_error; 539 struct cras_bt_adapter **adapters; 540 size_t num_adapters, i; 541 542 DL_APPEND(endpoints, endpoint); 543 544 dbus_error_init(&dbus_error); 545 546 if (!dbus_connection_register_object_path(conn, 547 endpoint->object_path, 548 &endpoint_vtable, 549 &dbus_error)) { 550 syslog(LOG_WARNING, 551 "Couldn't register Bluetooth endpoint: %s: %s", 552 endpoint->object_path, dbus_error.message); 553 dbus_error_free(&dbus_error); 554 return -ENOMEM; 555 } 556 557 num_adapters = cras_bt_adapter_get_list(&adapters); 558 for (i = 0; i < num_adapters; ++i) 559 cras_bt_register_endpoint(conn, adapters[i], endpoint); 560 free(adapters); 561 562 return 0; 563 } 564 565 void cras_bt_endpoint_rm(DBusConnection *conn, 566 struct cras_bt_endpoint *endpoint) 567 { 568 struct cras_bt_adapter **adapters; 569 size_t num_adapters, i; 570 571 num_adapters = cras_bt_adapter_get_list(&adapters); 572 for (i = 0; i < num_adapters; ++i) 573 cras_bt_unregister_endpoint(conn, adapters[i], endpoint); 574 free(adapters); 575 576 dbus_connection_unregister_object_path(conn, endpoint->object_path); 577 578 DL_DELETE(endpoints, endpoint); 579 } 580 581 void cras_bt_endpoint_reset() 582 { 583 struct cras_bt_endpoint *endpoint; 584 585 DL_FOREACH(endpoints, endpoint) 586 cras_bt_endpoint_suspend(endpoint); 587 } 588 589 struct cras_bt_endpoint *cras_bt_endpoint_get(const char *object_path) 590 { 591 struct cras_bt_endpoint *endpoint; 592 593 DL_FOREACH(endpoints, endpoint) { 594 if (strcmp(endpoint->object_path, object_path) == 0) 595 return endpoint; 596 } 597 598 return NULL; 599 } 600