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
56struct 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
63struct endpoint_request {
64	DBusMessage		*msg;
65	DBusPendingCall		*call;
66	media_endpoint_cb_t	cb;
67	void			*user_data;
68};
69
70struct 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
85static GSList *adapters = NULL;
86
87static 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
96static 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
107static 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
139static 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
147static 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
158static 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
184static 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
251failed:
252	g_free(endpoint);
253	return NULL;
254}
255
256static 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
282const char *media_endpoint_get_sender(struct media_endpoint *endpoint)
283{
284	return endpoint->sender;
285}
286
287static 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
337static 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
378static 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
401static GDBusMethodTable media_methods[] = {
402	{ "RegisterEndpoint",	"oa{sv}",	"",	register_endpoint },
403	{ "UnregisterEndpoint",	"o",		"",	unregister_endpoint },
404	{ },
405};
406
407static 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
423int 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
448void 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
463size_t media_endpoint_get_capabilities(struct media_endpoint *endpoint,
464					uint8_t **capabilities)
465{
466	*capabilities = endpoint->capabilities;
467	return endpoint->size;
468}
469
470static 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
528done:
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
538static 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
573gboolean 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
612gboolean 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
641void 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);
667done:
668	media_transport_destroy(endpoint->transport);
669	endpoint->transport = NULL;
670}
671
672void 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
695struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
696{
697	return endpoint->sep;
698}
699
700const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
701{
702	return endpoint->uuid;
703}
704
705uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
706{
707	return endpoint->codec;
708}
709
710struct media_transport *media_endpoint_get_transport(
711					struct media_endpoint *endpoint)
712{
713	return endpoint->transport;
714}
715