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