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
cras_bt_on_player_registered(DBusPendingCall * pending_call,void * data)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
cras_bt_add_player(DBusConnection * conn,const struct cras_bt_adapter * adapter,struct cras_bt_player * player)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
cras_bt_player_handle_message(DBusConnection * conn,DBusMessage * message,void * arg)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
cras_bt_player_create(DBusConnection * conn)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
cras_bt_register_player(DBusConnection * conn,const struct cras_bt_adapter * adapter)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