• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  *  BlueZ - Bluetooth protocol stack for Linux
4  *
5  *  Copyright (C) 2006-2007  Nokia Corporation
6  *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
7  *
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or
12  *  (at your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful,
15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  *  GNU General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License
20  *  along with this program; if not, write to the Free Software
21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  *
23  */
24 
25 #ifdef HAVE_CONFIG_H
26 #include <config.h>
27 #endif
28 
29 #include <errno.h>
30 
31 #include <glib.h>
32 #include <gdbus.h>
33 
34 #include "../src/adapter.h"
35 #include "../src/dbus-common.h"
36 
37 #include "log.h"
38 #include "error.h"
39 #include "device.h"
40 #include "avdtp.h"
41 #include "media.h"
42 #include "transport.h"
43 #include "a2dp.h"
44 #include "headset.h"
45 #include "manager.h"
46 
47 #ifndef DBUS_TYPE_UNIX_FD
48 #define DBUS_TYPE_UNIX_FD -1
49 #endif
50 
51 #define MEDIA_INTERFACE "org.bluez.Media"
52 #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint"
53 
54 #define REQUEST_TIMEOUT (3 * 1000)		/* 3 seconds */
55 
56 struct media_adapter {
57 	bdaddr_t		src;		/* Adapter address */
58 	char			*path;		/* Adapter path */
59 	DBusConnection		*conn;		/* Adapter connection */
60 	GSList			*endpoints;	/* Endpoints list */
61 };
62 
63 struct endpoint_request {
64 	DBusMessage		*msg;
65 	DBusPendingCall		*call;
66 	media_endpoint_cb_t	cb;
67 	void			*user_data;
68 };
69 
70 struct media_endpoint {
71 	struct a2dp_sep		*sep;
72 	char			*sender;	/* Endpoint DBus bus id */
73 	char			*path;		/* Endpoint object path */
74 	char			*uuid;		/* Endpoint property UUID */
75 	uint8_t			codec;		/* Endpoint codec */
76 	uint8_t			*capabilities;	/* Endpoint property capabilities */
77 	size_t			size;		/* Endpoint capabilities size */
78 	guint			hs_watch;
79 	guint			watch;
80 	struct endpoint_request *request;
81 	struct media_transport	*transport;
82 	struct media_adapter	*adapter;
83 };
84 
85 static GSList *adapters = NULL;
86 
endpoint_request_free(struct endpoint_request * request)87 static void endpoint_request_free(struct endpoint_request *request)
88 {
89 	if (request->call)
90 		dbus_pending_call_unref(request->call);
91 
92 	dbus_message_unref(request->msg);
93 	g_free(request);
94 }
95 
media_endpoint_cancel(struct media_endpoint * endpoint)96 static void media_endpoint_cancel(struct media_endpoint *endpoint)
97 {
98 	struct endpoint_request *request = endpoint->request;
99 
100 	if (request->call)
101 		dbus_pending_call_cancel(request->call);
102 
103 	endpoint_request_free(request);
104 	endpoint->request = NULL;
105 }
106 
media_endpoint_remove(struct media_endpoint * endpoint)107 static void media_endpoint_remove(struct media_endpoint *endpoint)
108 {
109 	struct media_adapter *adapter = endpoint->adapter;
110 
111 	if (g_slist_find(adapter->endpoints, endpoint) == NULL)
112 		return;
113 
114 	info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
115 			endpoint->path);
116 
117 	adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
118 
119 	if (endpoint->sep)
120 		a2dp_remove_sep(endpoint->sep);
121 
122 	if (endpoint->hs_watch)
123 		headset_remove_state_cb(endpoint->hs_watch);
124 
125 	if (endpoint->request)
126 		media_endpoint_cancel(endpoint);
127 
128 	if (endpoint->transport)
129 		media_transport_destroy(endpoint->transport);
130 
131 	g_dbus_remove_watch(adapter->conn, endpoint->watch);
132 	g_free(endpoint->capabilities);
133 	g_free(endpoint->sender);
134 	g_free(endpoint->path);
135 	g_free(endpoint->uuid);
136 	g_free(endpoint);
137 }
138 
media_endpoint_exit(DBusConnection * connection,void * user_data)139 static void media_endpoint_exit(DBusConnection *connection, void *user_data)
140 {
141 	struct media_endpoint *endpoint = user_data;
142 
143 	endpoint->watch = 0;
144 	media_endpoint_remove(endpoint);
145 }
146 
headset_setconf_cb(struct media_endpoint * endpoint,void * ret,int size,void * user_data)147 static void headset_setconf_cb(struct media_endpoint *endpoint, void *ret,
148 						int size, void *user_data)
149 {
150 	struct audio_device *dev = user_data;
151 
152 	if (ret != NULL)
153 		return;
154 
155 	headset_set_state(dev, HEADSET_STATE_DISCONNECTED);
156 }
157 
headset_state_changed(struct audio_device * dev,headset_state_t old_state,headset_state_t new_state,void * user_data)158 static void headset_state_changed(struct audio_device *dev,
159 					headset_state_t old_state,
160 					headset_state_t new_state,
161 					void *user_data)
162 {
163 	struct media_endpoint *endpoint = user_data;
164 
165 	DBG("");
166 
167 	switch (new_state) {
168 	case HEADSET_STATE_DISCONNECTED:
169 		media_endpoint_clear_configuration(endpoint);
170 		break;
171 	case HEADSET_STATE_CONNECTING:
172 		media_endpoint_set_configuration(endpoint, dev, NULL, 0,
173 						headset_setconf_cb, dev);
174 		break;
175 	case HEADSET_STATE_CONNECTED:
176 		break;
177 	case HEADSET_STATE_PLAY_IN_PROGRESS:
178 		break;
179 	case HEADSET_STATE_PLAYING:
180 		break;
181 	}
182 }
183 
media_endpoint_create(struct media_adapter * adapter,const char * sender,const char * path,const char * uuid,gboolean delay_reporting,uint8_t codec,uint8_t * capabilities,int size,int * err)184 static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
185 						const char *sender,
186 						const char *path,
187 						const char *uuid,
188 						gboolean delay_reporting,
189 						uint8_t codec,
190 						uint8_t *capabilities,
191 						int size,
192 						int *err)
193 {
194 	struct media_endpoint *endpoint;
195 
196 	endpoint = g_new0(struct media_endpoint, 1);
197 	endpoint->sender = g_strdup(sender);
198 	endpoint->path = g_strdup(path);
199 	endpoint->uuid = g_strdup(uuid);
200 	endpoint->codec = codec;
201 
202 	if (size > 0) {
203 		endpoint->capabilities = g_new(uint8_t, size);
204 		memcpy(endpoint->capabilities, capabilities, size);
205 		endpoint->size = size;
206 	}
207 
208 	endpoint->adapter = adapter;
209 
210 	if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
211 		endpoint->sep = a2dp_add_sep(&adapter->src,
212 					AVDTP_SEP_TYPE_SOURCE, codec,
213 					delay_reporting, endpoint, err);
214 		if (endpoint->sep == NULL)
215 			goto failed;
216 	} else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
217 		endpoint->sep = a2dp_add_sep(&adapter->src,
218 						AVDTP_SEP_TYPE_SINK, codec,
219 						delay_reporting, endpoint, err);
220 		if (endpoint->sep == NULL)
221 			goto failed;
222 	} else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
223 					g_strcmp0(uuid, HSP_AG_UUID) == 0) {
224 		struct audio_device *dev;
225 
226 		endpoint->hs_watch = headset_add_state_cb(headset_state_changed,
227 								endpoint);
228 		dev = manager_find_device(NULL, &adapter->src, BDADDR_ANY,
229 						AUDIO_HEADSET_INTERFACE, TRUE);
230 		if (dev)
231 			media_endpoint_set_configuration(endpoint, dev, NULL,
232 							0, headset_setconf_cb,
233 							dev);
234 	} else {
235 		if (err)
236 			*err = -EINVAL;
237 		goto failed;
238 	}
239 
240 	endpoint->watch = g_dbus_add_disconnect_watch(adapter->conn, sender,
241 						media_endpoint_exit, endpoint,
242 						NULL);
243 
244 	adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
245 	info("Endpoint registered: sender=%s path=%s", sender, path);
246 
247 	if (err)
248 		*err = 0;
249 	return endpoint;
250 
251 failed:
252 	g_free(endpoint);
253 	return NULL;
254 }
255 
media_adapter_find_endpoint(struct media_adapter * adapter,const char * sender,const char * path,const char * uuid)256 static struct media_endpoint *media_adapter_find_endpoint(
257 						struct media_adapter *adapter,
258 						const char *sender,
259 						const char *path,
260 						const char *uuid)
261 {
262 	GSList *l;
263 
264 	for (l = adapter->endpoints; l; l = l->next) {
265 		struct media_endpoint *endpoint = l->data;
266 
267 		if (sender && g_strcmp0(endpoint->sender, sender) != 0)
268 			continue;
269 
270 		if (path && g_strcmp0(endpoint->path, path) != 0)
271 			continue;
272 
273 		if (uuid && g_strcmp0(endpoint->uuid, uuid) != 0)
274 			continue;
275 
276 		return endpoint;
277 	}
278 
279 	return NULL;
280 }
281 
media_endpoint_get_sender(struct media_endpoint * endpoint)282 const char *media_endpoint_get_sender(struct media_endpoint *endpoint)
283 {
284 	return endpoint->sender;
285 }
286 
parse_properties(DBusMessageIter * props,const char ** uuid,gboolean * delay_reporting,uint8_t * codec,uint8_t ** capabilities,int * size)287 static int parse_properties(DBusMessageIter *props, const char **uuid,
288 				gboolean *delay_reporting, uint8_t *codec,
289 				uint8_t **capabilities, int *size)
290 {
291 	gboolean has_uuid = FALSE;
292 	gboolean has_codec = FALSE;
293 
294 	while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
295 		const char *key;
296 		DBusMessageIter value, entry;
297 		int var;
298 
299 		dbus_message_iter_recurse(props, &entry);
300 		dbus_message_iter_get_basic(&entry, &key);
301 
302 		dbus_message_iter_next(&entry);
303 		dbus_message_iter_recurse(&entry, &value);
304 
305 		var = dbus_message_iter_get_arg_type(&value);
306 		if (strcasecmp(key, "UUID") == 0) {
307 			if (var != DBUS_TYPE_STRING)
308 				return -EINVAL;
309 			dbus_message_iter_get_basic(&value, uuid);
310 			has_uuid = TRUE;
311 		} else if (strcasecmp(key, "Codec") == 0) {
312 			if (var != DBUS_TYPE_BYTE)
313 				return -EINVAL;
314 			dbus_message_iter_get_basic(&value, codec);
315 			has_codec = TRUE;
316 		} else if (strcasecmp(key, "DelayReporting") == 0) {
317 			if (var != DBUS_TYPE_BOOLEAN)
318 				return -EINVAL;
319 			dbus_message_iter_get_basic(&value, delay_reporting);
320 		} else if (strcasecmp(key, "Capabilities") == 0) {
321 			DBusMessageIter array;
322 
323 			if (var != DBUS_TYPE_ARRAY)
324 				return -EINVAL;
325 
326 			dbus_message_iter_recurse(&value, &array);
327 			dbus_message_iter_get_fixed_array(&array, capabilities,
328 							size);
329 		}
330 
331 		dbus_message_iter_next(props);
332 	}
333 
334 	return (has_uuid && has_codec) ? 0 : -EINVAL;
335 }
336 
register_endpoint(DBusConnection * conn,DBusMessage * msg,void * data)337 static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
338 					void *data)
339 {
340 	struct media_adapter *adapter = data;
341 	DBusMessageIter args, props;
342 	const char *sender, *path, *uuid;
343 	gboolean delay_reporting = FALSE;
344 	uint8_t codec;
345 	uint8_t *capabilities;
346 	int size = 0;
347 	int err;
348 
349 	sender = dbus_message_get_sender(msg);
350 
351 	dbus_message_iter_init(msg, &args);
352 
353 	dbus_message_iter_get_basic(&args, &path);
354 	dbus_message_iter_next(&args);
355 
356 	if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
357 		return btd_error_already_exists(msg);
358 
359 	dbus_message_iter_recurse(&args, &props);
360 	if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
361 		return btd_error_invalid_args(msg);
362 
363 	if (parse_properties(&props, &uuid, &delay_reporting, &codec,
364 						&capabilities, &size) < 0)
365 		return btd_error_invalid_args(msg);
366 
367 	if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
368 				codec, capabilities, size, &err) == FALSE) {
369 		if (err == -EPROTONOSUPPORT)
370 			return btd_error_not_supported(msg);
371 		else
372 			return btd_error_invalid_args(msg);
373 	}
374 
375 	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
376 }
377 
unregister_endpoint(DBusConnection * conn,DBusMessage * msg,void * data)378 static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
379 					void *data)
380 {
381 	struct media_adapter *adapter = data;
382 	struct media_endpoint *endpoint;
383 	const char *sender, *path;
384 
385 	if (!dbus_message_get_args(msg, NULL,
386 				DBUS_TYPE_OBJECT_PATH, &path,
387 				DBUS_TYPE_INVALID))
388 		return NULL;
389 
390 	sender = dbus_message_get_sender(msg);
391 
392 	endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
393 	if (endpoint == NULL)
394 		return btd_error_does_not_exist(msg);
395 
396 	media_endpoint_remove(endpoint);
397 
398 	return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
399 }
400 
401 static GDBusMethodTable media_methods[] = {
402 	{ "RegisterEndpoint",	"oa{sv}",	"",	register_endpoint },
403 	{ "UnregisterEndpoint",	"o",		"",	unregister_endpoint },
404 	{ },
405 };
406 
path_free(void * data)407 static void path_free(void *data)
408 {
409 	struct media_adapter *adapter = data;
410 
411 	g_slist_foreach(adapter->endpoints, (GFunc) media_endpoint_release,
412 									NULL);
413 	g_slist_free(adapter->endpoints);
414 
415 	dbus_connection_unref(adapter->conn);
416 
417 	adapters = g_slist_remove(adapters, adapter);
418 
419 	g_free(adapter->path);
420 	g_free(adapter);
421 }
422 
media_register(DBusConnection * conn,const char * path,const bdaddr_t * src)423 int media_register(DBusConnection *conn, const char *path, const bdaddr_t *src)
424 {
425 	struct media_adapter *adapter;
426 
427 	if (DBUS_TYPE_UNIX_FD < 0)
428 		return -EPERM;
429 
430 	adapter = g_new0(struct media_adapter, 1);
431 	adapter->conn = dbus_connection_ref(conn);
432 	bacpy(&adapter->src, src);
433 	adapter->path = g_strdup(path);
434 
435 	if (!g_dbus_register_interface(conn, path, MEDIA_INTERFACE,
436 					media_methods, NULL, NULL,
437 					adapter, path_free)) {
438 		error("D-Bus failed to register %s path", path);
439 		path_free(adapter);
440 		return -1;
441 	}
442 
443 	adapters = g_slist_append(adapters, adapter);
444 
445 	return 0;
446 }
447 
media_unregister(const char * path)448 void media_unregister(const char *path)
449 {
450 	GSList *l;
451 
452 	for (l = adapters; l; l = l->next) {
453 		struct media_adapter *adapter = l->data;
454 
455 		if (g_strcmp0(path, adapter->path) == 0) {
456 			g_dbus_unregister_interface(adapter->conn, path,
457 							MEDIA_INTERFACE);
458 			return;
459 		}
460 	}
461 }
462 
media_endpoint_get_capabilities(struct media_endpoint * endpoint,uint8_t ** capabilities)463 size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
464 					uint8_t **capabilities)
465 {
466 	*capabilities = endpoint->capabilities;
467 	return endpoint->size;
468 }
469 
endpoint_reply(DBusPendingCall * call,void * user_data)470 static void endpoint_reply(DBusPendingCall *call, void *user_data)
471 {
472 	struct media_endpoint *endpoint = user_data;
473 	struct endpoint_request *request = endpoint->request;
474 	DBusMessage *reply;
475 	DBusError err;
476 	gboolean value;
477 	void *ret = NULL;
478 	int size = -1;
479 
480 	/* steal_reply will always return non-NULL since the callback
481 	 * is only called after a reply has been received */
482 	reply = dbus_pending_call_steal_reply(call);
483 
484 	dbus_error_init(&err);
485 	if (dbus_set_error_from_message(&err, reply)) {
486 		error("Endpoint replied with an error: %s",
487 				err.name);
488 
489 		/* Clear endpoint configuration in case of NO_REPLY error */
490 		if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
491 			if (request->cb)
492 				request->cb(endpoint, NULL, size,
493 							request->user_data);
494 			media_endpoint_clear_configuration(endpoint);
495 			dbus_message_unref(reply);
496 			dbus_error_free(&err);
497 			return;
498 		}
499 
500 		dbus_error_free(&err);
501 		goto done;
502 	}
503 
504 	dbus_error_init(&err);
505 	if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
506 				"SelectConfiguration")) {
507 		DBusMessageIter args, array;
508 		uint8_t *configuration;
509 
510 		dbus_message_iter_init(reply, &args);
511 
512 		dbus_message_iter_recurse(&args, &array);
513 
514 		dbus_message_iter_get_fixed_array(&array, &configuration, &size);
515 
516 		ret = configuration;
517 		goto done;
518 	} else  if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
519 		error("Wrong reply signature: %s", err.message);
520 		dbus_error_free(&err);
521 		goto done;
522 	}
523 
524 	size = 1;
525 	value = TRUE;
526 	ret = &value;
527 
528 done:
529 	dbus_message_unref(reply);
530 
531 	if (request->cb)
532 		request->cb(endpoint, ret, size, request->user_data);
533 
534 	endpoint_request_free(request);
535 	endpoint->request = NULL;
536 }
537 
media_endpoint_async_call(DBusConnection * conn,DBusMessage * msg,struct media_endpoint * endpoint,media_endpoint_cb_t cb,void * user_data)538 static gboolean media_endpoint_async_call(DBusConnection *conn,
539 					DBusMessage *msg,
540 					struct media_endpoint *endpoint,
541 					media_endpoint_cb_t cb,
542 					void *user_data)
543 {
544 	struct endpoint_request *request;
545 
546 	if (endpoint->request)
547 		return FALSE;
548 
549 	request = g_new0(struct endpoint_request, 1);
550 
551 	/* Timeout should be less than avdtp request timeout (4 seconds) */
552 	if (dbus_connection_send_with_reply(conn, msg, &request->call,
553 						REQUEST_TIMEOUT) == FALSE) {
554 		error("D-Bus send failed");
555 		g_free(request);
556 		return FALSE;
557 	}
558 
559 	dbus_pending_call_set_notify(request->call, endpoint_reply, endpoint, NULL);
560 
561 	request->msg = msg;
562 	request->cb = cb;
563 	request->user_data = user_data;
564 	endpoint->request = request;
565 
566 	DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
567 			dbus_message_get_destination(msg),
568 			dbus_message_get_path(msg));
569 
570 	return TRUE;
571 }
572 
media_endpoint_set_configuration(struct media_endpoint * endpoint,struct audio_device * device,uint8_t * configuration,size_t size,media_endpoint_cb_t cb,void * user_data)573 gboolean media_endpoint_set_configuration(struct media_endpoint *endpoint,
574 					struct audio_device *device,
575 					uint8_t *configuration, size_t size,
576 					media_endpoint_cb_t cb,
577 					void *user_data)
578 {
579 	DBusConnection *conn;
580 	DBusMessage *msg;
581 	const char *path;
582 	DBusMessageIter iter;
583 
584 	if (endpoint->transport != NULL || endpoint->request != NULL)
585 		return FALSE;
586 
587 	conn = endpoint->adapter->conn;
588 
589 	endpoint->transport = media_transport_create(conn, endpoint, device,
590 						configuration, size);
591 	if (endpoint->transport == NULL)
592 		return FALSE;
593 
594 	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
595 						MEDIA_ENDPOINT_INTERFACE,
596 						"SetConfiguration");
597 	if (msg == NULL) {
598 		error("Couldn't allocate D-Bus message");
599 		return FALSE;
600 	}
601 
602 	dbus_message_iter_init_append(msg, &iter);
603 
604 	path = media_transport_get_path(endpoint->transport);
605 	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
606 
607 	transport_get_properties(endpoint->transport, &iter);
608 
609 	return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
610 }
611 
media_endpoint_select_configuration(struct media_endpoint * endpoint,uint8_t * capabilities,size_t length,media_endpoint_cb_t cb,void * user_data)612 gboolean media_endpoint_select_configuration(struct media_endpoint *endpoint,
613 						uint8_t *capabilities,
614 						size_t length,
615 						media_endpoint_cb_t cb,
616 						void *user_data)
617 {
618 	DBusConnection *conn;
619 	DBusMessage *msg;
620 
621 	if (endpoint->request != NULL)
622 		return FALSE;
623 
624 	conn = endpoint->adapter->conn;
625 
626 	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
627 						MEDIA_ENDPOINT_INTERFACE,
628 						"SelectConfiguration");
629 	if (msg == NULL) {
630 		error("Couldn't allocate D-Bus message");
631 		return FALSE;
632 	}
633 
634 	dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
635 					&capabilities, length,
636 					DBUS_TYPE_INVALID);
637 
638 	return media_endpoint_async_call(conn, msg, endpoint, cb, user_data);
639 }
640 
media_endpoint_clear_configuration(struct media_endpoint * endpoint)641 void media_endpoint_clear_configuration(struct media_endpoint *endpoint)
642 {
643 	DBusConnection *conn;
644 	DBusMessage *msg;
645 	const char *path;
646 
647 	if (endpoint->transport == NULL)
648 		return;
649 
650 	if (endpoint->request)
651 		media_endpoint_cancel(endpoint);
652 
653 	conn = endpoint->adapter->conn;
654 
655 	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
656 						MEDIA_ENDPOINT_INTERFACE,
657 						"ClearConfiguration");
658 	if (msg == NULL) {
659 		error("Couldn't allocate D-Bus message");
660 		goto done;
661 	}
662 
663 	path = media_transport_get_path(endpoint->transport);
664 	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
665 							DBUS_TYPE_INVALID);
666 	g_dbus_send_message(conn, msg);
667 done:
668 	media_transport_destroy(endpoint->transport);
669 	endpoint->transport = NULL;
670 }
671 
media_endpoint_release(struct media_endpoint * endpoint)672 void media_endpoint_release(struct media_endpoint *endpoint)
673 {
674 	DBusMessage *msg;
675 
676 	DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
677 
678 	/* already exit */
679 	if (endpoint->watch == 0)
680 		return;
681 
682 	msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
683 						MEDIA_ENDPOINT_INTERFACE,
684 						"Release");
685 	if (msg == NULL) {
686 		error("Couldn't allocate D-Bus message");
687 		return;
688 	}
689 
690 	g_dbus_send_message(endpoint->adapter->conn, msg);
691 
692 	media_endpoint_remove(endpoint);
693 }
694 
media_endpoint_get_sep(struct media_endpoint * endpoint)695 struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
696 {
697 	return endpoint->sep;
698 }
699 
media_endpoint_get_uuid(struct media_endpoint * endpoint)700 const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
701 {
702 	return endpoint->uuid;
703 }
704 
media_endpoint_get_codec(struct media_endpoint * endpoint)705 uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
706 {
707 	return endpoint->codec;
708 }
709 
media_endpoint_get_transport(struct media_endpoint * endpoint)710 struct media_transport *media_endpoint_get_transport(
711 					struct media_endpoint *endpoint)
712 {
713 	return endpoint->transport;
714 }
715