3 * BlueZ - Bluetooth protocol stack for Linux
5 * Copyright (C) 2006-2007 Nokia Corporation
6 * Copyright (C) 2004-2009 Marcel Holtmann <marcel@holtmann.org>
7 * Copyright (C) 2011 BMW Car IT GmbH. All rights reserved.
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
35 #include "lib/bluetooth.h"
39 #include "gdbus/gdbus.h"
41 #include "src/plugin.h"
42 #include "src/adapter.h"
43 #include "src/device.h"
44 #include "src/dbus-common.h"
45 #include "src/profile.h"
47 #ifdef __TIZEN_PATCH__
48 #include "src/service.h"
51 #include "src/uuid-helper.h"
53 #include "src/error.h"
54 #include "src/shared/queue.h"
58 #include "transport.h"
61 #ifdef __TIZEN_PATCH__
66 #define MEDIA_INTERFACE "org.bluez.Media1"
67 #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
68 #define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
70 #define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */
72 #ifdef __TIZEN_PATCH__
73 #define SINK_SUSPEND_TIMEOUT 4 /* 4 seconds */
75 unsigned int suspend_timer_id = 0;
78 struct media_adapter {
79 struct btd_adapter *btd_adapter;
80 GSList *endpoints; /* Endpoints list */
81 GSList *players; /* Players list */
84 struct endpoint_request {
85 struct media_endpoint *endpoint;
87 DBusPendingCall *call;
88 media_endpoint_cb_t cb;
89 GDestroyNotify destroy;
93 struct media_endpoint {
95 char *sender; /* Endpoint DBus bus id */
96 char *path; /* Endpoint object path */
97 char *uuid; /* Endpoint property UUID */
98 uint8_t codec; /* Endpoint codec */
99 uint8_t *capabilities; /* Endpoint property capabilities */
100 size_t size; /* Endpoint capabilities size */
105 struct media_adapter *adapter;
109 struct media_player {
110 struct media_adapter *adapter;
111 struct avrcp_player *player;
112 char *sender; /* Player DBus bus id */
113 char *path; /* Player object path */
114 GHashTable *settings; /* Player settings */
115 GHashTable *track; /* Player current track */
117 guint properties_watch;
119 #ifdef __TIZEN_PATCH__
134 static GSList *adapters = NULL;
136 #ifdef __TIZEN_PATCH__
137 static gboolean set_avrcp_status = FALSE;
138 static gboolean send_track_changed_event = FALSE;
140 static int media_set_sink_callback(struct btd_device *device,
141 struct media_player *mp);
142 static void media_sink_state_changed_cb(struct btd_service *service,
143 sink_state_t old_state,
144 sink_state_t new_state,
146 void media_stop_suspend_timer(void);
147 struct media_player *media_adapter_get_player(struct media_adapter *adapter);
148 static struct media_adapter *find_adapter(struct btd_device *device);
149 static uint32_t get_position(void *user_data);
152 static void endpoint_request_free(struct endpoint_request *request)
155 dbus_pending_call_unref(request->call);
157 if (request->destroy)
158 request->destroy(request->user_data);
160 dbus_message_unref(request->msg);
164 static void media_endpoint_cancel(struct endpoint_request *request)
166 struct media_endpoint *endpoint = request->endpoint;
169 dbus_pending_call_cancel(request->call);
171 endpoint->requests = g_slist_remove(endpoint->requests, request);
174 request->cb(endpoint, NULL, -1, request->user_data);
176 endpoint_request_free(request);
179 static void media_endpoint_cancel_all(struct media_endpoint *endpoint)
181 while (endpoint->requests != NULL)
182 media_endpoint_cancel(endpoint->requests->data);
185 static void media_endpoint_destroy(struct media_endpoint *endpoint)
187 DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
189 media_endpoint_cancel_all(endpoint);
191 g_slist_free_full(endpoint->transports,
192 (GDestroyNotify) media_transport_destroy);
194 g_dbus_remove_watch(btd_get_dbus_connection(), endpoint->watch);
195 g_free(endpoint->capabilities);
196 g_free(endpoint->sender);
197 g_free(endpoint->path);
198 g_free(endpoint->uuid);
202 static struct media_endpoint *media_adapter_find_endpoint(
203 struct media_adapter *adapter,
210 for (l = adapter->endpoints; l; l = l->next) {
211 struct media_endpoint *endpoint = l->data;
213 if (sender && g_strcmp0(endpoint->sender, sender) != 0)
216 if (path && g_strcmp0(endpoint->path, path) != 0)
219 if (uuid && strcasecmp(endpoint->uuid, uuid) != 0)
228 static void media_endpoint_remove(struct media_endpoint *endpoint)
230 struct media_adapter *adapter = endpoint->adapter;
233 a2dp_remove_sep(endpoint->sep);
237 info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
240 adapter->endpoints = g_slist_remove(adapter->endpoints, endpoint);
242 if (media_adapter_find_endpoint(adapter, NULL, NULL,
243 endpoint->uuid) == NULL)
244 btd_profile_remove_custom_prop(endpoint->uuid,
247 media_endpoint_destroy(endpoint);
250 static void media_endpoint_exit(DBusConnection *connection, void *user_data)
252 struct media_endpoint *endpoint = user_data;
255 media_endpoint_remove(endpoint);
258 static void clear_configuration(struct media_endpoint *endpoint,
259 struct media_transport *transport)
263 #ifdef __TIZEN_PATCH__
264 struct media_player *mp;
267 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
268 MEDIA_ENDPOINT_INTERFACE,
269 "ClearConfiguration");
271 error("Couldn't allocate D-Bus message");
275 path = media_transport_get_path(transport);
276 dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
278 g_dbus_send_message(btd_get_dbus_connection(), msg);
280 endpoint->transports = g_slist_remove(endpoint->transports, transport);
281 #ifdef __TIZEN_PATCH__
282 if ((mp = media_adapter_get_player(endpoint->adapter)))
283 if (mp->sink_watch) {
284 sink_remove_state_cb(mp->sink_watch);
287 media_stop_suspend_timer();
289 media_transport_destroy(transport);
292 static void clear_endpoint(struct media_endpoint *endpoint)
294 media_endpoint_cancel_all(endpoint);
296 while (endpoint->transports != NULL)
297 clear_configuration(endpoint, endpoint->transports->data);
300 static void endpoint_reply(DBusPendingCall *call, void *user_data)
302 struct endpoint_request *request = user_data;
303 struct media_endpoint *endpoint = request->endpoint;
310 /* steal_reply will always return non-NULL since the callback
311 * is only called after a reply has been received */
312 reply = dbus_pending_call_steal_reply(call);
314 dbus_error_init(&err);
315 if (dbus_set_error_from_message(&err, reply)) {
316 error("Endpoint replied with an error: %s",
319 /* Clear endpoint configuration in case of NO_REPLY error */
320 if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
321 clear_endpoint(endpoint);
322 dbus_message_unref(reply);
323 dbus_error_free(&err);
327 dbus_error_free(&err);
331 if (dbus_message_is_method_call(request->msg, MEDIA_ENDPOINT_INTERFACE,
332 "SelectConfiguration")) {
333 DBusMessageIter args, array;
334 uint8_t *configuration;
336 dbus_message_iter_init(reply, &args);
338 dbus_message_iter_recurse(&args, &array);
340 dbus_message_iter_get_fixed_array(&array, &configuration, &size);
344 } else if (!dbus_message_get_args(reply, &err, DBUS_TYPE_INVALID)) {
345 error("Wrong reply signature: %s", err.message);
346 dbus_error_free(&err);
355 dbus_message_unref(reply);
358 request->cb(endpoint, ret, size, request->user_data);
360 endpoint->requests = g_slist_remove(endpoint->requests, request);
361 endpoint_request_free(request);
364 static gboolean media_endpoint_async_call(DBusMessage *msg,
365 struct media_endpoint *endpoint,
366 media_endpoint_cb_t cb,
368 GDestroyNotify destroy)
370 struct endpoint_request *request;
372 request = g_new0(struct endpoint_request, 1);
374 /* Timeout should be less than avdtp request timeout (4 seconds) */
375 if (g_dbus_send_message_with_reply(btd_get_dbus_connection(),
377 REQUEST_TIMEOUT) == FALSE) {
378 error("D-Bus send failed");
383 dbus_pending_call_set_notify(request->call, endpoint_reply, request,
386 request->endpoint = endpoint;
389 request->destroy = destroy;
390 request->user_data = user_data;
392 endpoint->requests = g_slist_append(endpoint->requests, request);
394 DBG("Calling %s: name = %s path = %s", dbus_message_get_member(msg),
395 dbus_message_get_destination(msg),
396 dbus_message_get_path(msg));
401 static gboolean select_configuration(struct media_endpoint *endpoint,
402 uint8_t *capabilities,
404 media_endpoint_cb_t cb,
406 GDestroyNotify destroy)
410 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
411 MEDIA_ENDPOINT_INTERFACE,
412 "SelectConfiguration");
414 error("Couldn't allocate D-Bus message");
418 dbus_message_append_args(msg, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
419 &capabilities, length,
422 return media_endpoint_async_call(msg, endpoint, cb, user_data, destroy);
425 static int transport_device_cmp(gconstpointer data, gconstpointer user_data)
427 struct media_transport *transport = (struct media_transport *) data;
428 const struct btd_device *device = user_data;
429 const struct btd_device *dev = media_transport_get_dev(transport);
437 static struct media_transport *find_device_transport(
438 struct media_endpoint *endpoint,
439 struct btd_device *device)
443 match = g_slist_find_custom(endpoint->transports, device,
444 transport_device_cmp);
451 #ifdef __TIZEN_PATCH__
452 struct media_player * media_adapter_get_player(struct media_adapter * adapter)
457 for (l = adapter->players; l; l = l->next) {
458 struct media_player *mp = l->data;
465 void media_stop_suspend_timer(void)
467 if (suspend_timer_id > 0) {
468 DBG("Removing sink suspend timer");
469 g_source_remove(suspend_timer_id);
470 suspend_timer_id = 0;
474 gboolean media_reset_mp_status(gpointer user_data)
476 struct media_player *mp = user_data;
479 /* PlayBackStatus already reset; so return */
480 if (g_strcmp0(mp->status, "playing") != 0)
483 mp->position = get_position(mp);
484 g_timer_start(mp->timer);
487 mp->status = g_strdup("paused");
488 suspend_timer_id = 0;
489 avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
494 static void media_sink_state_changed_cb(struct btd_service *service,
495 sink_state_t old_state,
496 sink_state_t new_state,
499 struct media_player *mp = user_data;
502 /* Check if A2DP streaming is suspended */
503 if ((old_state == SINK_STATE_PLAYING) &&
504 (new_state == SINK_STATE_CONNECTED)) {
506 /* Check AVRCP play back status */
507 if (g_strcmp0(mp->status, "playing") != 0)
510 media_stop_suspend_timer();
512 /* PlayBackStatus is still PLAYING; start a timer */
513 suspend_timer_id = g_timeout_add_seconds(SINK_SUSPEND_TIMEOUT,
514 media_reset_mp_status, mp);
515 DBG("SINK SUSPEND TIMEOUT started");
518 /* Check if A2DP streaming is started */
519 if ((old_state == SINK_STATE_CONNECTED) &&
520 (new_state == SINK_STATE_PLAYING)) {
522 struct btd_device *device = btd_service_get_device(service);
525 media_stop_suspend_timer();
527 /* NULL packet streaming during initial connection */
528 if (set_avrcp_status == FALSE) {
529 set_avrcp_status = TRUE;
533 /* Check for BMW, Audi, VW car kit */
534 device_get_name(device, name, sizeof(name));
535 DBG("Name : %s", name);
536 if ((g_str_has_prefix(name, "BMW") == TRUE) ||
537 (g_str_has_prefix(name, "Audi") == TRUE) ||
538 (g_str_has_prefix(name, "VW BT") == TRUE)) {
540 /* Check AVRCP play back status */
541 if (g_strcmp0(mp->status, "playing") == 0)
545 mp->status = g_strdup("playing");
546 avrcp_player_event(mp->player,
547 AVRCP_EVENT_STATUS_CHANGED, mp->status);
552 static int media_set_sink_callback(struct btd_device *device,
553 struct media_player *mp)
555 struct btd_service *service;
558 service = btd_device_get_service(device, A2DP_SINK_UUID);
562 mp->sink_watch = sink_add_state_cb(service, media_sink_state_changed_cb, mp);
568 struct a2dp_config_data {
569 struct a2dp_setup *setup;
570 a2dp_endpoint_config_t cb;
573 static gboolean set_configuration(struct media_endpoint *endpoint,
574 uint8_t *configuration, size_t size,
575 media_endpoint_cb_t cb,
577 GDestroyNotify destroy)
579 struct a2dp_config_data *data = user_data;
580 struct btd_device *device = a2dp_setup_get_device(data->setup);
581 DBusConnection *conn = btd_get_dbus_connection();
584 DBusMessageIter iter;
585 struct media_transport *transport;
586 #ifdef __TIZEN_PATCH__
587 struct media_adapter *adapter;
588 struct media_player *mp;
591 transport = find_device_transport(endpoint, device);
593 if (transport != NULL)
596 transport = media_transport_create(device, configuration, size,
598 if (transport == NULL)
601 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
602 MEDIA_ENDPOINT_INTERFACE,
605 error("Couldn't allocate D-Bus message");
606 media_transport_destroy(transport);
610 #ifdef __TIZEN_PATCH__
611 set_avrcp_status = FALSE;
612 adapter = find_adapter(device);
613 if ((mp = media_adapter_get_player(adapter)))
614 media_set_sink_callback(device, mp);
617 endpoint->transports = g_slist_append(endpoint->transports, transport);
619 dbus_message_iter_init_append(msg, &iter);
621 path = media_transport_get_path(transport);
622 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
624 g_dbus_get_properties(conn, path, "org.bluez.MediaTransport1", &iter);
626 return media_endpoint_async_call(msg, endpoint, cb, user_data, destroy);
629 static void release_endpoint(struct media_endpoint *endpoint)
633 DBG("sender=%s path=%s", endpoint->sender, endpoint->path);
636 if (endpoint->watch == 0)
639 clear_endpoint(endpoint);
641 msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
642 MEDIA_ENDPOINT_INTERFACE,
645 error("Couldn't allocate D-Bus message");
649 g_dbus_send_message(btd_get_dbus_connection(), msg);
652 media_endpoint_remove(endpoint);
655 static const char *get_name(struct a2dp_sep *sep, void *user_data)
657 struct media_endpoint *endpoint = user_data;
659 return endpoint->sender;
662 static size_t get_capabilities(struct a2dp_sep *sep, uint8_t **capabilities,
665 struct media_endpoint *endpoint = user_data;
667 *capabilities = endpoint->capabilities;
668 return endpoint->size;
671 struct a2dp_select_data {
672 struct a2dp_setup *setup;
673 a2dp_endpoint_select_t cb;
676 static void select_cb(struct media_endpoint *endpoint, void *ret, int size,
679 struct a2dp_select_data *data = user_data;
681 data->cb(data->setup, ret, size);
684 static int select_config(struct a2dp_sep *sep, uint8_t *capabilities,
685 size_t length, struct a2dp_setup *setup,
686 a2dp_endpoint_select_t cb, void *user_data)
688 struct media_endpoint *endpoint = user_data;
689 struct a2dp_select_data *data;
691 data = g_new0(struct a2dp_select_data, 1);
695 if (select_configuration(endpoint, capabilities, length,
696 select_cb, data, g_free) == TRUE)
703 static void config_cb(struct media_endpoint *endpoint, void *ret, int size,
706 struct a2dp_config_data *data = user_data;
708 data->cb(data->setup, ret ? TRUE : FALSE);
711 static int set_config(struct a2dp_sep *sep, uint8_t *configuration,
713 struct a2dp_setup *setup,
714 a2dp_endpoint_config_t cb,
717 struct media_endpoint *endpoint = user_data;
718 struct a2dp_config_data *data;
720 data = g_new0(struct a2dp_config_data, 1);
724 if (set_configuration(endpoint, configuration, length, config_cb, data,
732 static void clear_config(struct a2dp_sep *sep, void *user_data)
734 struct media_endpoint *endpoint = user_data;
736 clear_endpoint(endpoint);
739 static void set_delay(struct a2dp_sep *sep, uint16_t delay, void *user_data)
741 struct media_endpoint *endpoint = user_data;
743 if (endpoint->transports == NULL)
746 media_transport_update_delay(endpoint->transports->data, delay);
749 static struct a2dp_endpoint a2dp_endpoint = {
750 .get_name = get_name,
751 .get_capabilities = get_capabilities,
752 .select_configuration = select_config,
753 .set_configuration = set_config,
754 .clear_configuration = clear_config,
755 .set_delay = set_delay
758 static void a2dp_destroy_endpoint(void *user_data)
760 struct media_endpoint *endpoint = user_data;
762 endpoint->sep = NULL;
763 release_endpoint(endpoint);
766 static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
767 gboolean delay_reporting,
770 endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
771 AVDTP_SEP_TYPE_SOURCE, endpoint->codec,
772 delay_reporting, &a2dp_endpoint,
773 endpoint, a2dp_destroy_endpoint, err);
774 if (endpoint->sep == NULL)
780 static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
781 gboolean delay_reporting,
784 endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
785 AVDTP_SEP_TYPE_SINK, endpoint->codec,
786 delay_reporting, &a2dp_endpoint,
787 endpoint, a2dp_destroy_endpoint, err);
788 if (endpoint->sep == NULL)
794 static struct media_adapter *find_adapter(struct btd_device *device)
798 for (l = adapters; l; l = l->next) {
799 struct media_adapter *adapter = l->data;
801 if (adapter->btd_adapter == device_get_adapter(device))
808 static bool endpoint_properties_exists(const char *uuid,
809 struct btd_device *dev,
812 struct media_adapter *adapter;
814 adapter = find_adapter(dev);
818 if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL)
824 static void append_endpoint(struct media_endpoint *endpoint,
825 DBusMessageIter *dict)
827 DBusMessageIter entry, var, props;
829 dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
832 dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
835 dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, "a{sv}",
838 dbus_message_iter_open_container(&var, DBUS_TYPE_ARRAY,
839 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
840 DBUS_TYPE_STRING_AS_STRING
841 DBUS_TYPE_VARIANT_AS_STRING
842 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
845 dict_append_entry(&props, "Path", DBUS_TYPE_OBJECT_PATH,
847 dict_append_entry(&props, "Codec", DBUS_TYPE_BYTE, &endpoint->codec);
848 dict_append_array(&props, "Capabilities", DBUS_TYPE_BYTE,
849 &endpoint->capabilities, endpoint->size);
851 dbus_message_iter_close_container(&var, &props);
852 dbus_message_iter_close_container(&entry, &var);
853 dbus_message_iter_close_container(dict, &entry);
856 static bool endpoint_properties_get(const char *uuid,
857 struct btd_device *dev,
858 DBusMessageIter *iter,
861 struct media_adapter *adapter;
862 DBusMessageIter dict;
865 adapter = find_adapter(dev);
869 dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
870 DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
871 DBUS_TYPE_STRING_AS_STRING
872 DBUS_TYPE_VARIANT_AS_STRING
873 DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
876 for (l = adapter->endpoints; l; l = l->next) {
877 struct media_endpoint *endpoint = l->data;
879 if (strcasecmp(endpoint->uuid, uuid) != 0)
882 append_endpoint(endpoint, &dict);
885 dbus_message_iter_close_container(iter, &dict);
890 static struct media_endpoint *media_endpoint_create(struct media_adapter *adapter,
894 gboolean delay_reporting,
896 uint8_t *capabilities,
900 struct media_endpoint *endpoint;
903 endpoint = g_new0(struct media_endpoint, 1);
904 endpoint->sender = g_strdup(sender);
905 endpoint->path = g_strdup(path);
906 endpoint->uuid = g_strdup(uuid);
907 endpoint->codec = codec;
910 endpoint->capabilities = g_new(uint8_t, size);
911 memcpy(endpoint->capabilities, capabilities, size);
912 endpoint->size = size;
915 endpoint->adapter = adapter;
917 if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0)
918 succeeded = endpoint_init_a2dp_source(endpoint,
919 delay_reporting, err);
920 else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0)
921 succeeded = endpoint_init_a2dp_sink(endpoint,
922 delay_reporting, err);
923 else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
924 strcasecmp(uuid, HSP_AG_UUID) == 0)
926 else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
927 strcasecmp(uuid, HSP_HS_UUID) == 0)
937 media_endpoint_destroy(endpoint);
941 endpoint->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
942 sender, media_endpoint_exit,
945 if (media_adapter_find_endpoint(adapter, NULL, NULL, uuid) == NULL) {
946 btd_profile_add_custom_prop(uuid, "a{sv}", "MediaEndpoints",
947 endpoint_properties_exists,
948 endpoint_properties_get,
952 adapter->endpoints = g_slist_append(adapter->endpoints, endpoint);
953 info("Endpoint registered: sender=%s path=%s", sender, path);
960 static int parse_properties(DBusMessageIter *props, const char **uuid,
961 gboolean *delay_reporting, uint8_t *codec,
962 uint8_t **capabilities, int *size)
964 gboolean has_uuid = FALSE;
965 gboolean has_codec = FALSE;
967 while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
969 DBusMessageIter value, entry;
972 dbus_message_iter_recurse(props, &entry);
973 dbus_message_iter_get_basic(&entry, &key);
975 dbus_message_iter_next(&entry);
976 dbus_message_iter_recurse(&entry, &value);
978 var = dbus_message_iter_get_arg_type(&value);
979 if (strcasecmp(key, "UUID") == 0) {
980 if (var != DBUS_TYPE_STRING)
982 dbus_message_iter_get_basic(&value, uuid);
984 } else if (strcasecmp(key, "Codec") == 0) {
985 if (var != DBUS_TYPE_BYTE)
987 dbus_message_iter_get_basic(&value, codec);
989 } else if (strcasecmp(key, "DelayReporting") == 0) {
990 if (var != DBUS_TYPE_BOOLEAN)
992 dbus_message_iter_get_basic(&value, delay_reporting);
993 } else if (strcasecmp(key, "Capabilities") == 0) {
994 DBusMessageIter array;
996 if (var != DBUS_TYPE_ARRAY)
999 dbus_message_iter_recurse(&value, &array);
1000 dbus_message_iter_get_fixed_array(&array, capabilities,
1004 dbus_message_iter_next(props);
1007 return (has_uuid && has_codec) ? 0 : -EINVAL;
1010 static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
1013 struct media_adapter *adapter = data;
1014 DBusMessageIter args, props;
1015 const char *sender, *path, *uuid;
1016 gboolean delay_reporting = FALSE;
1018 uint8_t *capabilities;
1022 sender = dbus_message_get_sender(msg);
1024 dbus_message_iter_init(msg, &args);
1026 dbus_message_iter_get_basic(&args, &path);
1027 dbus_message_iter_next(&args);
1029 if (media_adapter_find_endpoint(adapter, sender, path, NULL) != NULL)
1030 return btd_error_already_exists(msg);
1032 dbus_message_iter_recurse(&args, &props);
1033 if (dbus_message_iter_get_arg_type(&props) != DBUS_TYPE_DICT_ENTRY)
1034 return btd_error_invalid_args(msg);
1036 if (parse_properties(&props, &uuid, &delay_reporting, &codec,
1037 &capabilities, &size) < 0)
1038 return btd_error_invalid_args(msg);
1040 if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
1041 codec, capabilities, size, &err) == NULL) {
1042 if (err == -EPROTONOSUPPORT)
1043 return btd_error_not_supported(msg);
1045 return btd_error_invalid_args(msg);
1048 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1051 static DBusMessage *unregister_endpoint(DBusConnection *conn, DBusMessage *msg,
1054 struct media_adapter *adapter = data;
1055 struct media_endpoint *endpoint;
1056 const char *sender, *path;
1058 if (!dbus_message_get_args(msg, NULL,
1059 DBUS_TYPE_OBJECT_PATH, &path,
1061 return btd_error_invalid_args(msg);
1063 sender = dbus_message_get_sender(msg);
1065 endpoint = media_adapter_find_endpoint(adapter, sender, path, NULL);
1066 if (endpoint == NULL)
1067 return btd_error_does_not_exist(msg);
1069 media_endpoint_remove(endpoint);
1071 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
1074 static struct media_player *media_adapter_find_player(
1075 struct media_adapter *adapter,
1081 for (l = adapter->players; l; l = l->next) {
1082 struct media_player *mp = l->data;
1084 if (sender && g_strcmp0(mp->sender, sender) != 0)
1087 if (path && g_strcmp0(mp->path, path) != 0)
1096 static void release_player(struct media_player *mp)
1100 DBG("sender=%s path=%s", mp->sender, mp->path);
1102 msg = dbus_message_new_method_call(mp->sender, mp->path,
1103 MEDIA_PLAYER_INTERFACE,
1106 error("Couldn't allocate D-Bus message");
1110 g_dbus_send_message(btd_get_dbus_connection(), msg);
1113 static void media_player_free(gpointer data)
1115 DBusConnection *conn = btd_get_dbus_connection();
1116 struct media_player *mp = data;
1117 struct media_adapter *adapter = mp->adapter;
1120 adapter->players = g_slist_remove(adapter->players, mp);
1124 g_dbus_remove_watch(conn, mp->watch);
1125 g_dbus_remove_watch(conn, mp->properties_watch);
1126 g_dbus_remove_watch(conn, mp->seek_watch);
1129 g_hash_table_unref(mp->track);
1132 g_hash_table_unref(mp->settings);
1134 #ifdef __TIZEN_PATCH__
1135 media_stop_suspend_timer();
1138 sink_remove_state_cb(mp->sink_watch);
1141 g_timer_destroy(mp->timer);
1148 static void media_player_destroy(struct media_player *mp)
1150 struct media_adapter *adapter = mp->adapter;
1152 DBG("sender=%s path=%s", mp->sender, mp->path);
1155 struct avrcp_player *player = mp->player;
1157 adapter->players = g_slist_remove(adapter->players, mp);
1158 avrcp_unregister_player(player);
1162 media_player_free(mp);
1165 static void media_player_remove(struct media_player *mp)
1167 info("Player unregistered: sender=%s path=%s", mp->sender, mp->path);
1169 media_player_destroy(mp);
1172 static GList *list_settings(void *user_data)
1174 struct media_player *mp = user_data;
1178 if (mp->settings == NULL)
1181 return g_hash_table_get_keys(mp->settings);
1184 static const char *get_setting(const char *key, void *user_data)
1186 struct media_player *mp = user_data;
1190 return g_hash_table_lookup(mp->settings, key);
1193 static void set_shuffle_setting(DBusMessageIter *iter, const char *value)
1195 const char *key = "Shuffle";
1197 DBusMessageIter var;
1199 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
1200 dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
1201 DBUS_TYPE_BOOLEAN_AS_STRING,
1203 val = strcasecmp(value, "off") != 0;
1204 dbus_message_iter_append_basic(&var, DBUS_TYPE_BOOLEAN, &val);
1205 dbus_message_iter_close_container(iter, &var);
1208 static const char *repeat_to_loop_status(const char *value)
1210 if (strcasecmp(value, "off") == 0)
1212 else if (strcasecmp(value, "singletrack") == 0)
1214 else if (strcasecmp(value, "alltracks") == 0)
1216 else if (strcasecmp(value, "group") == 0)
1222 static void set_repeat_setting(DBusMessageIter *iter, const char *value)
1224 const char *key = "LoopStatus";
1226 DBusMessageIter var;
1228 dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &key);
1229 dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
1230 DBUS_TYPE_STRING_AS_STRING,
1232 val = repeat_to_loop_status(value);
1233 dbus_message_iter_append_basic(&var, DBUS_TYPE_STRING, &val);
1234 dbus_message_iter_close_container(iter, &var);
1237 static int set_setting(const char *key, const char *value, void *user_data)
1239 struct media_player *mp = user_data;
1240 const char *iface = MEDIA_PLAYER_INTERFACE;
1242 DBusMessageIter iter;
1245 DBG("%s = %s", key, value);
1247 curval = g_hash_table_lookup(mp->settings, key);
1251 if (strcasecmp(curval, value) == 0)
1254 msg = dbus_message_new_method_call(mp->sender, mp->path,
1255 DBUS_INTERFACE_PROPERTIES, "Set");
1257 error("Couldn't allocate D-Bus message");
1261 dbus_message_iter_init_append(msg, &iter);
1262 dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &iface);
1264 if (strcasecmp(key, "Shuffle") == 0)
1265 set_shuffle_setting(&iter, value);
1266 else if (strcasecmp(key, "Repeat") == 0)
1267 set_repeat_setting(&iter, value);
1269 g_dbus_send_message(btd_get_dbus_connection(), msg);
1274 static GList *list_metadata(void *user_data)
1276 struct media_player *mp = user_data;
1280 if (mp->track == NULL)
1283 return g_hash_table_get_keys(mp->track);
1286 static uint64_t get_uid(void *user_data)
1288 struct media_player *mp = user_data;
1290 DBG("%p", mp->track);
1292 if (mp->track == NULL)
1295 #ifdef __TIZEN_PATCH__
1296 if (!g_hash_table_lookup(mp->track, "Title"))
1303 static const char *get_metadata(const char *key, void *user_data)
1305 struct media_player *mp = user_data;
1309 if (mp->track == NULL)
1312 return g_hash_table_lookup(mp->track, key);
1315 static const char *get_status(void *user_data)
1317 struct media_player *mp = user_data;
1322 static uint32_t get_position(void *user_data)
1324 struct media_player *mp = user_data;
1328 if (mp->status == NULL || strcasecmp(mp->status, "Playing") != 0)
1329 return mp->position;
1331 timedelta = g_timer_elapsed(mp->timer, NULL);
1333 sec = (uint32_t) timedelta;
1334 msec = (uint32_t) ((timedelta - sec) * 1000);
1336 return mp->position + sec * 1000 + msec;
1339 static uint32_t get_duration(void *user_data)
1341 struct media_player *mp = user_data;
1343 return mp->duration;
1346 static void set_volume(uint8_t volume, struct btd_device *dev, void *user_data)
1348 struct media_player *mp = user_data;
1351 if (mp->volume == volume)
1354 mp->volume = volume;
1356 for (l = mp->adapter->endpoints; l; l = l->next) {
1357 struct media_endpoint *endpoint = l->data;
1358 struct media_transport *transport;
1360 /* Volume is A2DP only */
1361 if (endpoint->sep == NULL)
1364 transport = find_device_transport(endpoint, dev);
1365 if (transport == NULL)
1368 media_transport_update_volume(transport, volume);
1372 static bool media_player_send(struct media_player *mp, const char *name)
1376 msg = dbus_message_new_method_call(mp->sender, mp->path,
1377 MEDIA_PLAYER_INTERFACE, name);
1379 error("Couldn't allocate D-Bus message");
1383 g_dbus_send_message(btd_get_dbus_connection(), msg);
1388 static bool play(void *user_data)
1390 struct media_player *mp = user_data;
1394 if (!mp->play || !mp->control)
1397 return media_player_send(mp, "Play");
1400 static bool stop(void *user_data)
1402 struct media_player *mp = user_data;
1409 return media_player_send(mp, "Stop");
1412 static bool pause(void *user_data)
1414 struct media_player *mp = user_data;
1418 if (!mp->pause || !mp->control)
1421 return media_player_send(mp, "Pause");
1424 static bool next(void *user_data)
1426 struct media_player *mp = user_data;
1430 if (!mp->next || !mp->control)
1433 return media_player_send(mp, "Next");
1436 static bool previous(void *user_data)
1438 struct media_player *mp = user_data;
1442 if (!mp->previous || !mp->control)
1445 return media_player_send(mp, "Previous");
1448 static struct avrcp_player_cb player_cb = {
1449 .list_settings = list_settings,
1450 .get_setting = get_setting,
1451 .set_setting = set_setting,
1452 .list_metadata = list_metadata,
1454 .get_metadata = get_metadata,
1455 .get_position = get_position,
1456 .get_duration = get_duration,
1457 .get_status = get_status,
1458 .set_volume = set_volume,
1463 .previous = previous,
1466 static void media_player_exit(DBusConnection *connection, void *user_data)
1468 struct media_player *mp = user_data;
1471 media_player_remove(mp);
1474 static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
1477 #ifdef __TIZEN_PATCH__
1478 uint32_t playback_position;
1481 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
1484 dbus_message_iter_get_basic(iter, &value);
1485 DBG("Status=%s", value);
1487 if (g_strcmp0(mp->status, value) == 0)
1490 mp->position = get_position(mp);
1491 g_timer_start(mp->timer);
1494 mp->status = g_strdup(value);
1496 avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
1497 #ifdef __TIZEN_PATCH__
1498 if (strcasecmp(mp->status, "reverse-seek") != 0) {
1499 playback_position = get_position(mp);
1500 avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
1501 &playback_position);
1508 static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
1510 #ifdef __TIZEN_PATCH__
1516 #ifdef __TIZEN_PATCH__
1517 uint32_t playback_position;
1520 #ifndef __TIZEN_PATCH__
1521 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64)
1524 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
1527 dbus_message_iter_get_basic(iter, &value);
1529 #ifndef __TIZEN_PATCH__
1532 DBG("Value %d", value);
1533 #ifndef __TIZEN_PATCH__
1534 if (value > get_position(mp))
1535 status = "forward-seek";
1537 status = "reverse-seek";
1540 mp->position = value;
1541 g_timer_start(mp->timer);
1543 DBG("Position=%u", mp->position);
1545 if (!mp->position) {
1546 avrcp_player_event(mp->player,
1547 AVRCP_EVENT_TRACK_REACHED_START, NULL);
1548 #ifdef __TIZEN_PATCH__
1549 playback_position = get_position(mp);
1550 avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
1551 &playback_position);
1558 * If position is the maximum value allowed or greater than track's
1559 * duration, we send a track-reached-end event.
1561 if (mp->position == UINT32_MAX || mp->position >= mp->duration) {
1562 avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
1564 #ifdef __TIZEN_PATCH__
1565 playback_position = get_position(mp);
1566 avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
1567 &playback_position);
1572 #ifndef __TIZEN_PATCH__
1573 /* Send a status change to force resync the position */
1574 avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status);
1576 #ifdef __TIZEN_PATCH__
1577 playback_position = get_position(mp);
1578 avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
1579 &playback_position);
1585 static void set_metadata(struct media_player *mp, const char *key,
1588 #ifdef __TIZEN_PATCH__
1589 const char *current_value = NULL;
1591 current_value = g_hash_table_lookup(mp->track, key);
1593 if ((g_strcmp0(value, current_value) != 0) &&
1594 (send_track_changed_event == FALSE))
1595 send_track_changed_event = TRUE;
1597 DBG("%s=%s", key, value);
1598 g_hash_table_replace(mp->track, g_strdup(key), g_strdup(value));
1601 static gboolean parse_string_metadata(struct media_player *mp, const char *key,
1602 DBusMessageIter *iter)
1606 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
1609 dbus_message_iter_get_basic(iter, &value);
1611 set_metadata(mp, key, value);
1616 static gboolean parse_array_metadata(struct media_player *mp, const char *key,
1617 DBusMessageIter *iter)
1619 DBusMessageIter array;
1622 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
1625 dbus_message_iter_recurse(iter, &array);
1627 if (dbus_message_iter_get_arg_type(&array) == DBUS_TYPE_INVALID)
1630 if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRING)
1633 dbus_message_iter_get_basic(&array, &value);
1635 set_metadata(mp, key, value);
1640 static gboolean parse_int64_metadata(struct media_player *mp, const char *key,
1641 DBusMessageIter *iter)
1647 type = dbus_message_iter_get_arg_type(iter);
1648 if (type == DBUS_TYPE_UINT64)
1649 warn("expected DBUS_TYPE_INT64 got DBUS_TYPE_UINT64");
1650 else if (type != DBUS_TYPE_INT64)
1653 dbus_message_iter_get_basic(iter, &value);
1655 if (strcasecmp(key, "Duration") == 0) {
1656 #ifndef __TIZEN_PATCH__
1659 mp->duration = value;
1662 snprintf(valstr, 20, "%" PRIu64, value);
1664 set_metadata(mp, key, valstr);
1669 static gboolean parse_int32_metadata(struct media_player *mp, const char *key,
1670 DBusMessageIter *iter)
1676 type = dbus_message_iter_get_arg_type(iter);
1677 if (type == DBUS_TYPE_UINT32)
1678 warn("expected DBUS_TYPE_INT32 got DBUS_TYPE_UINT32");
1679 else if (type != DBUS_TYPE_INT32)
1682 dbus_message_iter_get_basic(iter, &value);
1684 snprintf(valstr, 20, "%u", value);
1686 set_metadata(mp, key, valstr);
1691 static gboolean parse_player_metadata(struct media_player *mp,
1692 DBusMessageIter *iter)
1694 DBusMessageIter dict;
1695 DBusMessageIter var;
1697 gboolean title = FALSE;
1699 #ifdef __TIZEN_PATCH__
1700 uint32_t playback_position;
1703 ctype = dbus_message_iter_get_arg_type(iter);
1704 if (ctype != DBUS_TYPE_ARRAY)
1707 dbus_message_iter_recurse(iter, &dict);
1709 #ifndef __TIZEN_PATCH__
1710 if (mp->track != NULL)
1711 g_hash_table_unref(mp->track);
1713 mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
1717 while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
1718 DBUS_TYPE_INVALID) {
1719 DBusMessageIter entry;
1722 if (ctype != DBUS_TYPE_DICT_ENTRY)
1725 dbus_message_iter_recurse(&dict, &entry);
1726 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
1729 dbus_message_iter_get_basic(&entry, &key);
1730 dbus_message_iter_next(&entry);
1732 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
1735 dbus_message_iter_recurse(&entry, &var);
1737 if (strcasecmp(key, "xesam:title") == 0) {
1738 if (!parse_string_metadata(mp, "Title", &var))
1741 } else if (strcasecmp(key, "xesam:artist") == 0) {
1742 if (!parse_array_metadata(mp, "Artist", &var))
1744 } else if (strcasecmp(key, "xesam:album") == 0) {
1745 if (!parse_string_metadata(mp, "Album", &var))
1747 } else if (strcasecmp(key, "xesam:genre") == 0) {
1748 if (!parse_array_metadata(mp, "Genre", &var))
1750 } else if (strcasecmp(key, "mpris:length") == 0) {
1751 if (!parse_int64_metadata(mp, "Duration", &var))
1753 #ifdef __TIZEN_PATCH__
1754 } else if (strcasecmp(key, "xesam:totalTracks") == 0) {
1755 if (!parse_int32_metadata(mp, "NumberOfTracks", &var))
1758 } else if (strcasecmp(key, "xesam:trackNumber") == 0) {
1759 if (!parse_int32_metadata(mp, "TrackNumber", &var))
1762 DBG("%s not supported, ignoring", key);
1764 dbus_message_iter_next(&dict);
1768 g_hash_table_insert(mp->track, g_strdup("Title"),
1771 #ifdef __TIZEN_PATCH__
1772 if (send_track_changed_event) {
1774 avrcp_player_event(mp->player,
1775 AVRCP_EVENT_TRACK_CHANGED, &uid);
1776 send_track_changed_event = FALSE;
1778 playback_position = get_position(mp);
1779 avrcp_player_event(mp->player,
1780 AVRCP_EVENT_PLAYBACK_POS_CHANGED, &playback_position);
1784 g_timer_start(mp->timer);
1787 avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid);
1788 avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL);
1793 static gboolean set_property(struct media_player *mp, const char *key,
1798 curval = g_hash_table_lookup(mp->settings, key);
1799 if (g_strcmp0(curval, value) == 0)
1802 DBG("%s=%s", key, value);
1804 g_hash_table_replace(mp->settings, g_strdup(key), g_strdup(value));
1806 avrcp_player_event(mp->player, AVRCP_EVENT_SETTINGS_CHANGED, key);
1811 static gboolean set_shuffle(struct media_player *mp, DBusMessageIter *iter)
1814 const char *strvalue;
1816 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
1819 dbus_message_iter_get_basic(iter, &value);
1821 strvalue = value ? "alltracks" : "off";
1823 return set_property(mp, "Shuffle", strvalue);
1826 static const char *loop_status_to_repeat(const char *value)
1828 if (strcasecmp(value, "None") == 0)
1830 else if (strcasecmp(value, "Track") == 0)
1831 return "singletrack";
1832 else if (strcasecmp(value, "Playlist") == 0)
1838 static gboolean set_repeat(struct media_player *mp, DBusMessageIter *iter)
1842 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
1845 dbus_message_iter_get_basic(iter, &value);
1847 value = loop_status_to_repeat(value);
1851 return set_property(mp, "Repeat", value);
1854 static gboolean set_flag(struct media_player *mp, DBusMessageIter *iter,
1859 if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN)
1862 dbus_message_iter_get_basic(iter, &value);
1869 static gboolean set_player_property(struct media_player *mp, const char *key,
1870 DBusMessageIter *entry)
1872 DBusMessageIter var;
1874 if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
1877 dbus_message_iter_recurse(entry, &var);
1879 if (strcasecmp(key, "PlaybackStatus") == 0)
1880 return set_status(mp, &var);
1882 if (strcasecmp(key, "Position") == 0)
1883 return set_position(mp, &var);
1885 if (strcasecmp(key, "Metadata") == 0)
1886 return parse_player_metadata(mp, &var);
1888 if (strcasecmp(key, "Shuffle") == 0)
1889 return set_shuffle(mp, &var);
1891 if (strcasecmp(key, "LoopStatus") == 0)
1892 return set_repeat(mp, &var);
1894 if (strcasecmp(key, "CanPlay") == 0)
1895 return set_flag(mp, &var, &mp->play);
1897 if (strcasecmp(key, "CanPause") == 0)
1898 return set_flag(mp, &var, &mp->pause);
1900 if (strcasecmp(key, "CanGoNext") == 0)
1901 return set_flag(mp, &var, &mp->next);
1903 if (strcasecmp(key, "CanGoPrevious") == 0)
1904 return set_flag(mp, &var, &mp->previous);
1906 if (strcasecmp(key, "CanControl") == 0)
1907 return set_flag(mp, &var, &mp->control);
1909 DBG("%s not supported, ignoring", key);
1914 static gboolean parse_player_properties(struct media_player *mp,
1915 DBusMessageIter *iter)
1917 DBusMessageIter dict;
1920 ctype = dbus_message_iter_get_arg_type(iter);
1921 if (ctype != DBUS_TYPE_ARRAY)
1924 dbus_message_iter_recurse(iter, &dict);
1926 while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
1927 DBUS_TYPE_INVALID) {
1928 DBusMessageIter entry;
1931 if (ctype != DBUS_TYPE_DICT_ENTRY)
1934 dbus_message_iter_recurse(&dict, &entry);
1935 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
1938 dbus_message_iter_get_basic(&entry, &key);
1939 dbus_message_iter_next(&entry);
1941 if (set_player_property(mp, key, &entry) == FALSE)
1944 dbus_message_iter_next(&dict);
1950 static gboolean properties_changed(DBusConnection *connection, DBusMessage *msg,
1953 struct media_player *mp = user_data;
1954 DBusMessageIter iter;
1956 DBG("sender=%s path=%s", mp->sender, mp->path);
1958 dbus_message_iter_init(msg, &iter);
1960 dbus_message_iter_next(&iter);
1962 parse_player_properties(mp, &iter);
1967 static gboolean position_changed(DBusConnection *connection, DBusMessage *msg,
1970 struct media_player *mp = user_data;
1971 DBusMessageIter iter;
1973 DBG("sender=%s path=%s", mp->sender, mp->path);
1975 dbus_message_iter_init(msg, &iter);
1977 set_position(mp, &iter);
1982 static struct media_player *media_player_create(struct media_adapter *adapter,
1987 DBusConnection *conn = btd_get_dbus_connection();
1988 struct media_player *mp;
1990 mp = g_new0(struct media_player, 1);
1991 mp->adapter = adapter;
1992 mp->sender = g_strdup(sender);
1993 mp->path = g_strdup(path);
1994 mp->timer = g_timer_new();
1996 mp->watch = g_dbus_add_disconnect_watch(conn, sender,
1997 media_player_exit, mp,
1999 mp->properties_watch = g_dbus_add_properties_watch(conn, sender,
2000 path, MEDIA_PLAYER_INTERFACE,
2003 mp->seek_watch = g_dbus_add_signal_watch(conn, sender,
2004 path, MEDIA_PLAYER_INTERFACE,
2005 "Seeked", position_changed,
2007 mp->player = avrcp_register_player(adapter->btd_adapter, &player_cb,
2008 mp, media_player_free);
2011 *err = -EPROTONOSUPPORT;
2012 media_player_destroy(mp);
2016 mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2018 #ifdef __TIZEN_PATCH__
2019 mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
2023 adapter->players = g_slist_append(adapter->players, mp);
2025 info("Player registered: sender=%s path=%s", sender, path);
2033 static DBusMessage *register_player(DBusConnection *conn, DBusMessage *msg,
2036 struct media_adapter *adapter = data;
2037 struct media_player *mp;
2038 DBusMessageIter args;
2039 const char *sender, *path;
2042 sender = dbus_message_get_sender(msg);
2044 dbus_message_iter_init(msg, &args);
2046 dbus_message_iter_get_basic(&args, &path);
2047 dbus_message_iter_next(&args);
2049 if (media_adapter_find_player(adapter, sender, path) != NULL)
2050 return btd_error_already_exists(msg);
2052 mp = media_player_create(adapter, sender, path, &err);
2054 if (err == -EPROTONOSUPPORT)
2055 return btd_error_not_supported(msg);
2057 return btd_error_invalid_args(msg);
2060 if (parse_player_properties(mp, &args) == FALSE) {
2061 media_player_destroy(mp);
2062 return btd_error_invalid_args(msg);
2065 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2068 static DBusMessage *unregister_player(DBusConnection *conn, DBusMessage *msg,
2071 struct media_adapter *adapter = data;
2072 struct media_player *player;
2073 const char *sender, *path;
2075 if (!dbus_message_get_args(msg, NULL,
2076 DBUS_TYPE_OBJECT_PATH, &path,
2078 return btd_error_invalid_args(msg);
2080 sender = dbus_message_get_sender(msg);
2082 player = media_adapter_find_player(adapter, sender, path);
2084 return btd_error_does_not_exist(msg);
2086 media_player_remove(player);
2088 return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
2091 static const GDBusMethodTable media_methods[] = {
2092 { GDBUS_METHOD("RegisterEndpoint",
2093 GDBUS_ARGS({ "endpoint", "o" }, { "properties", "a{sv}" }),
2094 NULL, register_endpoint) },
2095 { GDBUS_METHOD("UnregisterEndpoint",
2096 GDBUS_ARGS({ "endpoint", "o" }), NULL, unregister_endpoint) },
2097 #ifndef __TIZEN_PATCH__
2098 { GDBUS_EXPERIMENTAL_METHOD("RegisterPlayer",
2099 GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" }),
2100 NULL, register_player) },
2101 { GDBUS_EXPERIMENTAL_METHOD("UnregisterPlayer",
2102 GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
2104 { GDBUS_METHOD("RegisterPlayer",
2105 GDBUS_ARGS({ "player", "o" }, { "properties", "a{sv}" }),
2106 NULL, register_player) },
2107 { GDBUS_METHOD("UnregisterPlayer",
2108 GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
2109 #endif /* __TIZEN_PATCH__ */
2113 static void path_free(void *data)
2115 struct media_adapter *adapter = data;
2117 while (adapter->endpoints)
2118 release_endpoint(adapter->endpoints->data);
2120 while (adapter->players)
2121 media_player_destroy(adapter->players->data);
2123 adapters = g_slist_remove(adapters, adapter);
2125 btd_adapter_unref(adapter->btd_adapter);
2129 int media_register(struct btd_adapter *btd_adapter)
2131 struct media_adapter *adapter;
2133 adapter = g_new0(struct media_adapter, 1);
2134 adapter->btd_adapter = btd_adapter_ref(btd_adapter);
2136 if (!g_dbus_register_interface(btd_get_dbus_connection(),
2137 adapter_get_path(btd_adapter),
2139 media_methods, NULL, NULL,
2140 adapter, path_free)) {
2141 error("D-Bus failed to register %s path",
2142 adapter_get_path(btd_adapter));
2147 adapters = g_slist_append(adapters, adapter);
2152 void media_unregister(struct btd_adapter *btd_adapter)
2156 for (l = adapters; l; l = l->next) {
2157 struct media_adapter *adapter = l->data;
2159 if (adapter->btd_adapter == btd_adapter) {
2160 g_dbus_unregister_interface(btd_get_dbus_connection(),
2161 adapter_get_path(btd_adapter),
2168 struct a2dp_sep *media_endpoint_get_sep(struct media_endpoint *endpoint)
2170 return endpoint->sep;
2173 const char *media_endpoint_get_uuid(struct media_endpoint *endpoint)
2175 return endpoint->uuid;
2178 uint8_t media_endpoint_get_codec(struct media_endpoint *endpoint)
2180 return endpoint->codec;