• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright 2016 The Chromium OS Authors. All rights reserved.
2  * Use of this source code is governed by a BSD-style license that can be
3  * found in the LICENSE file.
4  */
5 #include <dbus/dbus.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <syslog.h>
9 
10 #include "cras_bt_constants.h"
11 #include "cras_bt_adapter.h"
12 #include "cras_bt_player.h"
13 #include "cras_dbus_util.h"
14 #include "utlist.h"
15 
16 #define CRAS_DEFAULT_PLAYER "/org/chromium/Cras/Bluetooth/DefaultPlayer"
17 
cras_bt_on_player_registered(DBusPendingCall * pending_call,void * data)18 static void cras_bt_on_player_registered(DBusPendingCall *pending_call,
19 					 void *data)
20 {
21 	DBusMessage *reply;
22 
23 	reply = dbus_pending_call_steal_reply(pending_call);
24 	dbus_pending_call_unref(pending_call);
25 
26 	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
27 		syslog(LOG_ERR, "RegisterPlayer returned error: %s",
28 		       dbus_message_get_error_name(reply));
29 		dbus_message_unref(reply);
30 		return;
31 	}
32 
33 	dbus_message_unref(reply);
34 }
35 
cras_bt_add_player(DBusConnection * conn,const struct cras_bt_adapter * adapter,struct cras_bt_player * player)36 static int cras_bt_add_player(DBusConnection *conn,
37 			      const struct cras_bt_adapter *adapter,
38 			      struct cras_bt_player *player)
39 {
40 	const char *adapter_path;
41 	DBusMessage *method_call;
42 	DBusMessageIter message_iter, dict;
43 	DBusPendingCall *pending_call;
44 
45 	adapter_path = cras_bt_adapter_object_path(adapter);
46 	method_call = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path,
47 						   BLUEZ_INTERFACE_MEDIA,
48 						   "RegisterPlayer");
49 	if (!method_call)
50 		return -ENOMEM;
51 
52 	dbus_message_iter_init_append(method_call, &message_iter);
53 	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
54 				       &player->object_path);
55 
56 	dbus_message_iter_open_container(
57 		&message_iter, DBUS_TYPE_ARRAY,
58 		DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
59 			DBUS_TYPE_VARIANT_AS_STRING
60 				DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
61 		&dict);
62 
63 	append_key_value(&dict, "PlaybackStatus", DBUS_TYPE_STRING,
64 			 DBUS_TYPE_STRING_AS_STRING, &player->playback_status);
65 	append_key_value(&dict, "Identity", DBUS_TYPE_STRING,
66 			 DBUS_TYPE_STRING_AS_STRING, &player->identity);
67 	append_key_value(&dict, "LoopStatus", DBUS_TYPE_STRING,
68 			 DBUS_TYPE_STRING_AS_STRING, &player->loop_status);
69 	append_key_value(&dict, "Position", DBUS_TYPE_INT64,
70 			 DBUS_TYPE_INT64_AS_STRING, &player->position);
71 	append_key_value(&dict, "Shuffle", DBUS_TYPE_BOOLEAN,
72 			 DBUS_TYPE_BOOLEAN_AS_STRING, &player->shuffle);
73 	append_key_value(&dict, "CanGoNext", DBUS_TYPE_BOOLEAN,
74 			 DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_go_next);
75 	append_key_value(&dict, "CanGoPrevious", DBUS_TYPE_BOOLEAN,
76 			 DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_go_prev);
77 	append_key_value(&dict, "CanPlay", DBUS_TYPE_BOOLEAN,
78 			 DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_play);
79 	append_key_value(&dict, "CanPause", DBUS_TYPE_BOOLEAN,
80 			 DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_pause);
81 	append_key_value(&dict, "CanControl", DBUS_TYPE_BOOLEAN,
82 			 DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_control);
83 
84 	dbus_message_iter_close_container(&message_iter, &dict);
85 
86 	if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
87 					     DBUS_TIMEOUT_USE_DEFAULT)) {
88 		dbus_message_unref(method_call);
89 		return -ENOMEM;
90 	}
91 
92 	dbus_message_unref(method_call);
93 	if (!pending_call)
94 		return -EIO;
95 
96 	if (!dbus_pending_call_set_notify(
97 		    pending_call, cras_bt_on_player_registered, player, NULL)) {
98 		dbus_pending_call_cancel(pending_call);
99 		dbus_pending_call_unref(pending_call);
100 		return -ENOMEM;
101 	}
102 	return 0;
103 }
104 
105 /* Note that player properties will be used mostly for AVRCP qualification and
106  * not for normal use cases. The corresponding media events won't be routed by
107  * CRAS until we have a plan to provide general system API to handle media
108  * control.
109  */
110 static struct cras_bt_player player = {
111 	.object_path = CRAS_DEFAULT_PLAYER,
112 	.playback_status = "playing",
113 	.identity = "DefaultPlayer",
114 	.loop_status = "None",
115 	.shuffle = 0,
116 	.position = 0,
117 	.can_go_next = 0,
118 	.can_go_prev = 0,
119 	.can_play = 0,
120 	.can_pause = 0,
121 	.can_control = 0,
122 	.message_cb = NULL,
123 };
124 
cras_bt_player_handle_message(DBusConnection * conn,DBusMessage * message,void * arg)125 static DBusHandlerResult cras_bt_player_handle_message(DBusConnection *conn,
126 						       DBusMessage *message,
127 						       void *arg)
128 {
129 	const char *msg = dbus_message_get_member(message);
130 
131 	if (player.message_cb)
132 		player.message_cb(msg);
133 
134 	return DBUS_HANDLER_RESULT_HANDLED;
135 }
136 
cras_bt_player_create(DBusConnection * conn)137 int cras_bt_player_create(DBusConnection *conn)
138 {
139 	static const DBusObjectPathVTable player_vtable = {
140 		.message_function = cras_bt_player_handle_message
141 	};
142 
143 	DBusError dbus_error;
144 	struct cras_bt_adapter **adapters;
145 	size_t num_adapters, i;
146 
147 	dbus_error_init(&dbus_error);
148 
149 	if (!dbus_connection_register_object_path(
150 		    conn, player.object_path, &player_vtable, &dbus_error)) {
151 		syslog(LOG_ERR, "Cannot register player %s",
152 		       player.object_path);
153 		dbus_error_free(&dbus_error);
154 		return -ENOMEM;
155 	}
156 
157 	num_adapters = cras_bt_adapter_get_list(&adapters);
158 	for (i = 0; i < num_adapters; ++i)
159 		cras_bt_add_player(conn, adapters[i], &player);
160 	free(adapters);
161 	return 0;
162 }
163 
cras_bt_register_player(DBusConnection * conn,const struct cras_bt_adapter * adapter)164 int cras_bt_register_player(DBusConnection *conn,
165 			    const struct cras_bt_adapter *adapter)
166 {
167 	return cras_bt_add_player(conn, adapter, &player);
168 }
169