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