/* 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 <dbus/dbus.h>
#include <errno.h>
#include <stdlib.h>
#include <syslog.h>
#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);
}