/* Copyright (c) 2016 The Chromium Authors. All rights reserved. * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include "cras_bt_constants.h" #include "cras_bt_adapter.h" #include "cras_bt_player.h" #include "cras_dbus_util.h" #include "utlist.h" #define CRAS_DEFAULT_PLAYER "/org/chromium/Cras/Bluetooth/DefaultPlayer" static void cras_bt_on_player_registered(DBusPendingCall *pending_call, void *data) { DBusMessage *reply; reply = dbus_pending_call_steal_reply(pending_call); dbus_pending_call_unref(pending_call); if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) { syslog(LOG_ERR, "RegisterPlayer returned error: %s", dbus_message_get_error_name(reply)); dbus_message_unref(reply); return; } dbus_message_unref(reply); } static int cras_bt_add_player(DBusConnection *conn, const struct cras_bt_adapter *adapter, struct cras_bt_player *player) { const char *adapter_path; DBusMessage *method_call; DBusMessageIter message_iter, dict; DBusPendingCall *pending_call; adapter_path = cras_bt_adapter_object_path(adapter); method_call = dbus_message_new_method_call(BLUEZ_SERVICE, adapter_path, BLUEZ_INTERFACE_MEDIA, "RegisterPlayer"); if (!method_call) return -ENOMEM; dbus_message_iter_init_append(method_call, &message_iter); dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH, &player->object_path); dbus_message_iter_open_container(&message_iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); append_key_value(&dict, "PlaybackStatus", DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &player->playback_status); append_key_value(&dict, "Identity", DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &player->identity); append_key_value(&dict, "LoopStatus", DBUS_TYPE_STRING, DBUS_TYPE_STRING_AS_STRING, &player->loop_status); append_key_value(&dict, "Position", DBUS_TYPE_INT64, DBUS_TYPE_INT64_AS_STRING, &player->position); append_key_value(&dict, "Shuffle", DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &player->shuffle); append_key_value(&dict, "CanGoNext", DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_go_next); append_key_value(&dict, "CanGoPrevious", DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_go_prev); append_key_value(&dict, "CanPlay", DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_play); append_key_value(&dict, "CanPause", DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_pause); append_key_value(&dict, "CanControl", DBUS_TYPE_BOOLEAN, DBUS_TYPE_BOOLEAN_AS_STRING, &player->can_control); dbus_message_iter_close_container(&message_iter, &dict); if (!dbus_connection_send_with_reply(conn, method_call, &pending_call, DBUS_TIMEOUT_USE_DEFAULT)) { dbus_message_unref(method_call); return -ENOMEM; } dbus_message_unref(method_call); if (!pending_call) return -EIO; if (!dbus_pending_call_set_notify(pending_call, cras_bt_on_player_registered, player, NULL)) { dbus_pending_call_cancel(pending_call); dbus_pending_call_unref(pending_call); return -ENOMEM; } return 0; } /* Note that player properties will be used mostly for AVRCP qualification and * not for normal use cases. The corresponding media events won't be routed by * CRAS until we have a plan to provide general system API to handle media * control. */ static struct cras_bt_player player = { .object_path = CRAS_DEFAULT_PLAYER, .playback_status = "playing", .identity = "DefaultPlayer", .loop_status = "None", .shuffle = 0, .position = 0, .can_go_next = 0, .can_go_prev = 0, .can_play = 0, .can_pause = 0, .can_control = 0, .message_cb = NULL, }; static DBusHandlerResult cras_bt_player_handle_message(DBusConnection *conn, DBusMessage *message, void *arg) { const char *msg = dbus_message_get_member(message); if (player.message_cb) player.message_cb(msg); return DBUS_HANDLER_RESULT_HANDLED; } int cras_bt_player_create(DBusConnection *conn) { static const DBusObjectPathVTable player_vtable = { .message_function = cras_bt_player_handle_message }; DBusError dbus_error; struct cras_bt_adapter **adapters; size_t num_adapters, i; dbus_error_init(&dbus_error); if (!dbus_connection_register_object_path(conn, player.object_path, &player_vtable, &dbus_error)) { syslog(LOG_ERR, "Cannot register player %s", player.object_path); dbus_error_free(&dbus_error); return -ENOMEM; } num_adapters = cras_bt_adapter_get_list(&adapters); for (i = 0; i < num_adapters; ++i) cras_bt_add_player(conn, adapters[i], &player); free(adapters); return 0; } int cras_bt_register_player(DBusConnection *conn, const struct cras_bt_adapter *adapter) { return cras_bt_add_player(conn, adapter, &player); }