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