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