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
cras_bt_endpoint_suspend(struct cras_bt_endpoint * endpoint)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
cras_bt_endpoint_set_configuration(DBusConnection * conn,DBusMessage * message,void * arg)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
cras_bt_endpoint_select_configuration(DBusConnection * conn,DBusMessage * message,void * arg)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
cras_bt_endpoint_clear_configuration(DBusConnection * conn,DBusMessage * message,void * arg)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
cras_bt_endpoint_release(DBusConnection * conn,DBusMessage * message,void * arg)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
cras_bt_handle_endpoint_message(DBusConnection * conn,DBusMessage * message,void * arg)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
cras_bt_on_register_endpoint(DBusPendingCall * pending_call,void * data)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
cras_bt_register_endpoint(DBusConnection * conn,const struct cras_bt_adapter * adapter,struct cras_bt_endpoint * endpoint)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
cras_bt_on_unregister_endpoint(DBusPendingCall * pending_call,void * data)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
cras_bt_unregister_endpoint(DBusConnection * conn,const struct cras_bt_adapter * adapter,struct cras_bt_endpoint * endpoint)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
cras_bt_register_endpoints(DBusConnection * conn,const struct cras_bt_adapter * adapter)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
cras_bt_endpoint_add(DBusConnection * conn,struct cras_bt_endpoint * endpoint)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
cras_bt_endpoint_rm(DBusConnection * conn,struct cras_bt_endpoint * endpoint)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
cras_bt_endpoint_reset()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
cras_bt_endpoint_get(const char * object_path)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