blob: 21002249fc5338003b8c0cddd19d8e4514c8215c [file] [log] [blame]
/* Copyright (c) 2013 The Chromium OS 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 <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include "audio_thread.h"
#include "cras_dbus.h"
#include "cras_dbus_control.h"
#include "cras_dbus_util.h"
#include "cras_iodev_list.h"
#include "cras_observer.h"
#include "cras_system_state.h"
#include "cras_util.h"
#include "utlist.h"
#define CRAS_CONTROL_INTERFACE "org.chromium.cras.Control"
#define CRAS_ROOT_OBJECT_PATH "/org/chromium/cras"
#define CONTROL_INTROSPECT_XML \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
"<node>\n" \
" <interface name=\""CRAS_CONTROL_INTERFACE"\">\n" \
" <method name=\"SetOutputVolume\">\n" \
" <arg name=\"volume\" type=\"i\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetOutputNodeVolume\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" <arg name=\"volume\" type=\"i\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SwapLeftRight\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" <arg name=\"swap\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetOutputMute\">\n" \
" <arg name=\"mute_on\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetOutputUserMute\">\n" \
" <arg name=\"mute_on\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetSuspendAudio\">\n" \
" <arg name=\"suspend\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetInputGain\">\n" \
" <arg name=\"gain\" type=\"i\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetInputNodeGain\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" <arg name=\"gain\" type=\"i\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetInputMute\">\n" \
" <arg name=\"mute_on\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"GetVolumeState\">\n" \
" <arg name=\"output_volume\" type=\"i\" direction=\"out\"/>\n"\
" <arg name=\"output_mute\" type=\"b\" direction=\"out\"/>\n" \
" <arg name=\"input_gain\" type=\"i\" direction=\"out\"/>\n" \
" <arg name=\"input_mute\" type=\"b\" direction=\"out\"/>\n" \
" <arg name=\"output_user_mute\" type=\"b\" direction=\"out\"/>\n"\
" </method>\n" \
" <method name=\"GetNodes\">\n" \
" <arg name=\"nodes\" type=\"a{sv}\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"SetActiveOutputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetActiveInputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"AddActiveInputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"AddActiveOutputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"RemoveActiveInputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"RemoveActiveOutputNode\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"GetNumberOfActiveStreams\">\n" \
" <arg name=\"num\" type=\"i\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetNumberOfActiveOutputStreams\">\n" \
" <arg name=\"num\" type=\"i\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetNumberOfActiveInputStreams\">\n" \
" <arg name=\"num\" type=\"i\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"SetGlobalOutputChannelRemix\">\n" \
" <arg name=\"num_channels\" type=\"i\" direction=\"in\"/>\n" \
" <arg name=\"coefficient\" type=\"ad\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetHotwordModel\">\n" \
" <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n" \
" <arg name=\"model_name\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" </interface>\n" \
" <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n" \
" <method name=\"Introspect\">\n" \
" <arg name=\"data\" type=\"s\" direction=\"out\"/>\n" \
" </method>\n" \
" </interface>\n" \
"</node>\n"
struct cras_dbus_control {
DBusConnection *conn;
struct cras_observer_client *observer;
};
static struct cras_dbus_control dbus_control;
/* helper to extract a single argument from a DBus message. */
static int get_single_arg(DBusMessage *message, int dbus_type, void *arg)
{
DBusError dbus_error;
dbus_error_init(&dbus_error);
if (!dbus_message_get_args(message, &dbus_error,
dbus_type, arg,
DBUS_TYPE_INVALID)) {
syslog(LOG_WARNING,
"Bad method received: %s",
dbus_error.message);
dbus_error_free(&dbus_error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
return 0;
}
/* Helper to send an empty reply. */
static void send_empty_reply(DBusConnection *conn, DBusMessage *message)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
reply = dbus_message_new_method_return(message);
if (!reply)
return;
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
}
/* Helper to send an int32 reply. */
static void send_int32_reply(DBusConnection *conn,
DBusMessage *message,
dbus_int32_t value)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
reply = dbus_message_new_method_return(message);
if (!reply)
return;
dbus_message_append_args(reply,
DBUS_TYPE_INT32, &value,
DBUS_TYPE_INVALID);
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
}
/* Handlers for exported DBus method calls. */
static DBusHandlerResult handle_set_output_volume(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_int32_t new_vol;
rc = get_single_arg(message, DBUS_TYPE_INT32, &new_vol);
if (rc)
return rc;
cras_system_set_volume(new_vol);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_output_node_volume(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
dbus_int32_t new_vol;
cras_node_id_t id;
DBusError dbus_error;
dbus_error_init(&dbus_error);
if (!dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT64, &id,
DBUS_TYPE_INT32, &new_vol,
DBUS_TYPE_INVALID)) {
syslog(LOG_WARNING,
"Bad method received: %s",
dbus_error.message);
dbus_error_free(&dbus_error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
cras_iodev_list_set_node_attr(id, IONODE_ATTR_VOLUME, new_vol);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_swap_left_right(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
cras_node_id_t id;
dbus_bool_t swap;
DBusError dbus_error;
dbus_error_init(&dbus_error);
if (!dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT64, &id,
DBUS_TYPE_BOOLEAN, &swap,
DBUS_TYPE_INVALID)) {
syslog(LOG_WARNING,
"Bad method received: %s",
dbus_error.message);
dbus_error_free(&dbus_error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
cras_iodev_list_set_node_attr(id, IONODE_ATTR_SWAP_LEFT_RIGHT,
swap);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_output_mute(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_bool_t new_mute;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &new_mute);
if (rc)
return rc;
cras_system_set_mute(new_mute);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_output_user_mute(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_bool_t new_mute;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &new_mute);
if (rc)
return rc;
cras_system_set_user_mute(new_mute);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_suspend_audio(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_bool_t suspend;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &suspend);
if (rc)
return rc;
cras_system_set_suspended(suspend);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_input_gain(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_int32_t new_gain;
rc = get_single_arg(message, DBUS_TYPE_INT32, &new_gain);
if (rc)
return rc;
cras_system_set_capture_gain(new_gain);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_input_node_gain(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
dbus_int32_t new_gain;
cras_node_id_t id;
DBusError dbus_error;
dbus_error_init(&dbus_error);
if (!dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT64, &id,
DBUS_TYPE_INT32, &new_gain,
DBUS_TYPE_INVALID)) {
syslog(LOG_WARNING,
"Bad method received: %s",
dbus_error.message);
dbus_error_free(&dbus_error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
cras_iodev_list_set_node_attr(id, IONODE_ATTR_CAPTURE_GAIN, new_gain);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_input_mute(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_bool_t new_mute;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &new_mute);
if (rc)
return rc;
cras_system_set_capture_mute(new_mute);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_get_volume_state(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
dbus_int32_t volume;
dbus_bool_t system_muted;
dbus_bool_t user_muted;
dbus_int32_t capture_gain;
dbus_bool_t capture_muted;
reply = dbus_message_new_method_return(message);
volume = cras_system_get_volume();
system_muted = cras_system_get_system_mute();
user_muted = cras_system_get_user_mute();
capture_gain = cras_system_get_capture_gain();
capture_muted = cras_system_get_capture_mute();
dbus_message_append_args(reply,
DBUS_TYPE_INT32, &volume,
DBUS_TYPE_BOOLEAN, &system_muted,
DBUS_TYPE_INT32, &capture_gain,
DBUS_TYPE_BOOLEAN, &capture_muted,
DBUS_TYPE_BOOLEAN, &user_muted,
DBUS_TYPE_INVALID);
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
/* Appends the information about a node to the dbus message. Returns
* false if not enough memory. */
static dbus_bool_t append_node_dict(DBusMessageIter *iter,
const struct cras_iodev_info *dev,
const struct cras_ionode_info *node,
enum CRAS_STREAM_DIRECTION direction)
{
DBusMessageIter dict;
dbus_bool_t is_input;
dbus_uint64_t id;
const char *dev_name = dev->name;
dbus_uint64_t stable_dev_id = node->stable_id;
dbus_uint64_t stable_dev_id_new = node->stable_id_new;
const char *node_type = node->type;
const char *node_name = node->name;
const char *mic_positions = node->mic_positions;
dbus_bool_t active;
dbus_uint64_t plugged_time = node->plugged_time.tv_sec * 1000000ULL +
node->plugged_time.tv_usec;
dbus_uint64_t node_volume = node->volume;
dbus_int64_t node_capture_gain = node->capture_gain;
char *models, *empty_models = "";
is_input = (direction == CRAS_STREAM_INPUT);
id = node->iodev_idx;
id = (id << 32) | node->ionode_idx;
active = !!node->active;
if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}",
&dict))
return FALSE;
if (!append_key_value(&dict, "IsInput", DBUS_TYPE_BOOLEAN,
DBUS_TYPE_BOOLEAN_AS_STRING, &is_input))
return FALSE;
if (!append_key_value(&dict, "Id", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &id))
return FALSE;
if (!append_key_value(&dict, "DeviceName", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &dev_name))
return FALSE;
if (!append_key_value(&dict, "StableDeviceId", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id))
return FALSE;
if (!append_key_value(&dict, "StableDeviceIdNew", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id_new))
return FALSE;
if (!append_key_value(&dict, "Type", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &node_type))
return FALSE;
if (!append_key_value(&dict, "Name", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &node_name))
return FALSE;
if (!append_key_value(&dict, "MicPositions", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING, &mic_positions))
return FALSE;
if (!append_key_value(&dict, "Active", DBUS_TYPE_BOOLEAN,
DBUS_TYPE_BOOLEAN_AS_STRING, &active))
return FALSE;
if (!append_key_value(&dict, "PluggedTime", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &plugged_time))
return FALSE;
if (!append_key_value(&dict, "NodeVolume", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &node_volume))
return FALSE;
if (!append_key_value(&dict, "NodeCaptureGain", DBUS_TYPE_INT64,
DBUS_TYPE_INT64_AS_STRING, &node_capture_gain))
return FALSE;
models = cras_iodev_list_get_hotword_models(id);
if (!append_key_value(&dict, "HotwordModels", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING,
models ? &models : &empty_models)) {
free(models);
return FALSE;
}
free(models);
if (!dbus_message_iter_close_container(iter, &dict))
return FALSE;
return TRUE;
}
/* Appends the information about all nodes in a given direction. Returns false
* if not enough memory. */
static dbus_bool_t append_nodes(enum CRAS_STREAM_DIRECTION direction,
DBusMessageIter *array)
{
const struct cras_iodev_info *devs;
const struct cras_ionode_info *nodes;
int ndevs, nnodes;
int i, j;
if (direction == CRAS_STREAM_OUTPUT) {
ndevs = cras_system_state_get_output_devs(&devs);
nnodes = cras_system_state_get_output_nodes(&nodes);
} else {
ndevs = cras_system_state_get_input_devs(&devs);
nnodes = cras_system_state_get_input_nodes(&nodes);
}
for (i = 0; i < nnodes; i++) {
/* Don't reply unplugged nodes. */
if (!nodes[i].plugged)
continue;
/* Find the device for this node. */
for (j = 0; j < ndevs; j++)
if (devs[j].idx == nodes[i].iodev_idx)
break;
if (j == ndevs)
continue;
/* Send information about this node. */
if (!append_node_dict(array, &devs[j], &nodes[i], direction))
return FALSE;
}
return TRUE;
}
static DBusHandlerResult handle_get_nodes(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
DBusMessage *reply;
DBusMessageIter array;
dbus_uint32_t serial = 0;
reply = dbus_message_new_method_return(message);
dbus_message_iter_init_append(reply, &array);
if (!append_nodes(CRAS_STREAM_OUTPUT, &array))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!append_nodes(CRAS_STREAM_INPUT, &array))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_set_active_node(DBusConnection *conn,
DBusMessage *message,
void *arg,
enum CRAS_STREAM_DIRECTION direction)
{
int rc;
cras_node_id_t id;
rc = get_single_arg(message, DBUS_TYPE_UINT64, &id);
if (rc)
return rc;
cras_iodev_list_select_node(direction, id);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_add_active_node(DBusConnection *conn,
DBusMessage *message,
void *arg,
enum CRAS_STREAM_DIRECTION direction)
{
int rc;
cras_node_id_t id;
rc = get_single_arg(message, DBUS_TYPE_UINT64, &id);
if (rc)
return rc;
cras_iodev_list_add_active_node(direction, id);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_rm_active_node(DBusConnection *conn,
DBusMessage *message,
void *arg,
enum CRAS_STREAM_DIRECTION direction)
{
int rc;
cras_node_id_t id;
rc = get_single_arg(message, DBUS_TYPE_UINT64, &id);
if (rc)
return rc;
cras_iodev_list_rm_active_node(direction, id);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_get_num_active_streams(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
send_int32_reply(conn, message, cras_system_state_get_active_streams());
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_get_num_active_streams_use_input_hw(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
dbus_int32_t num = 0;
unsigned i;
for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
if (cras_stream_uses_input_hw(i))
num += cras_system_state_get_active_streams_by_direction(i);
}
send_int32_reply(conn, message, num);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_get_num_active_streams_use_output_hw(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
dbus_int32_t num = 0;
unsigned i;
for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
if (cras_stream_uses_output_hw(i))
num += cras_system_state_get_active_streams_by_direction(i);
}
send_int32_reply(conn, message, num);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_global_output_channel_remix(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
dbus_int32_t num_channels;
double *coeff_array;
dbus_int32_t count;
DBusError dbus_error;
float *coefficient;
int i;
dbus_error_init(&dbus_error);
if (!dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_INT32, &num_channels,
DBUS_TYPE_ARRAY,
DBUS_TYPE_DOUBLE, &coeff_array, &count,
DBUS_TYPE_INVALID)) {
syslog(LOG_WARNING, "Set global output channel remix error: %s",
dbus_error.message);
dbus_error_free(&dbus_error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
coefficient = (float *)calloc(count, sizeof(*coefficient));
if (!coefficient)
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
for (i = 0; i < count; i++)
coefficient[i] = coeff_array[i];
audio_thread_config_global_remix(
cras_iodev_list_get_audio_thread(),
num_channels,
coefficient);
send_empty_reply(conn, message);
free(coefficient);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_hotword_model(
DBusConnection *conn,
DBusMessage *message,
void *arg)
{
cras_node_id_t id;
const char *model_name;
DBusError dbus_error;
dbus_int32_t ret;
dbus_error_init(&dbus_error);
if (!dbus_message_get_args(message, &dbus_error,
DBUS_TYPE_UINT64, &id,
DBUS_TYPE_STRING, &model_name,
DBUS_TYPE_INVALID)) {
syslog(LOG_WARNING,
"Bad method received: %s",
dbus_error.message);
dbus_error_free(&dbus_error);
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
ret = cras_iodev_list_set_hotword_model(id, model_name);
send_int32_reply(conn, message, ret);
return DBUS_HANDLER_RESULT_HANDLED;
}
/* Handle incoming messages. */
static DBusHandlerResult handle_control_message(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
syslog(LOG_DEBUG, "Control message: %s %s %s",
dbus_message_get_path(message),
dbus_message_get_interface(message),
dbus_message_get_member(message));
if (dbus_message_is_method_call(message,
DBUS_INTERFACE_INTROSPECTABLE,
"Introspect")) {
DBusMessage *reply;
const char *xml = CONTROL_INTROSPECT_XML;
reply = dbus_message_new_method_return(message);
if (!reply)
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!dbus_message_append_args(reply,
DBUS_TYPE_STRING, &xml,
DBUS_TYPE_INVALID))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
if (!dbus_connection_send(conn, reply, NULL))
return DBUS_HANDLER_RESULT_NEED_MEMORY;
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetOutputVolume")) {
return handle_set_output_volume(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetOutputNodeVolume")) {
return handle_set_output_node_volume(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SwapLeftRight")) {
return handle_swap_left_right(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetOutputMute")) {
return handle_set_output_mute(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetOutputUserMute")) {
return handle_set_output_user_mute(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetSuspendAudio")) {
return handle_set_suspend_audio(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetInputGain")) {
return handle_set_input_gain(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetInputNodeGain")) {
return handle_set_input_node_gain(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetInputMute")) {
return handle_set_input_mute(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"GetVolumeState")) {
return handle_get_volume_state(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"GetNodes")) {
return handle_get_nodes(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetActiveOutputNode")) {
return handle_set_active_node(conn, message, arg,
CRAS_STREAM_OUTPUT);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetActiveInputNode")) {
return handle_set_active_node(conn, message, arg,
CRAS_STREAM_INPUT);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"AddActiveInputNode")) {
return handle_add_active_node(conn, message, arg,
CRAS_STREAM_INPUT);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"AddActiveOutputNode")) {
return handle_add_active_node(conn, message, arg,
CRAS_STREAM_OUTPUT);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"RemoveActiveInputNode")) {
return handle_rm_active_node(conn, message, arg,
CRAS_STREAM_INPUT);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"RemoveActiveOutputNode")) {
return handle_rm_active_node(conn, message, arg,
CRAS_STREAM_OUTPUT);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"GetNumberOfActiveStreams")) {
return handle_get_num_active_streams(conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"GetNumberOfActiveInputStreams")) {
return handle_get_num_active_streams_use_input_hw(
conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"GetNumberOfActiveOutputStreams")) {
return handle_get_num_active_streams_use_output_hw(
conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetGlobalOutputChannelRemix")) {
return handle_set_global_output_channel_remix(
conn, message, arg);
} else if (dbus_message_is_method_call(message,
CRAS_CONTROL_INTERFACE,
"SetHotwordModel")) {
return handle_set_hotword_model(conn, message, arg);
}
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
/* Creates a new DBus message, must be freed with dbus_message_unref. */
static DBusMessage *create_dbus_message(const char *name)
{
DBusMessage *msg;
msg = dbus_message_new_signal(CRAS_ROOT_OBJECT_PATH,
CRAS_CONTROL_INTERFACE,
name);
if (!msg)
syslog(LOG_ERR, "Failed to create signal");
return msg;
}
/* Handlers for system updates that generate DBus signals. */
static void signal_output_volume(void *context, int32_t volume)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("OutputVolumeChanged");
if (!msg)
return;
volume = cras_system_get_volume();
dbus_message_append_args(msg,
DBUS_TYPE_INT32, &volume,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_output_mute(void *context, int muted, int user_muted,
int mute_locked)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("OutputMuteChanged");
if (!msg)
return;
muted = cras_system_get_system_mute();
user_muted = cras_system_get_user_mute();
dbus_message_append_args(msg,
DBUS_TYPE_BOOLEAN, &muted,
DBUS_TYPE_BOOLEAN, &user_muted,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_capture_gain(void *context, int32_t gain)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("InputGainChanged");
if (!msg)
return;
dbus_message_append_args(msg,
DBUS_TYPE_INT32, &gain,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_capture_mute(void *context, int muted, int mute_locked)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("InputMuteChanged");
if (!msg)
return;
dbus_message_append_args(msg,
DBUS_TYPE_BOOLEAN, &muted,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_nodes_changed(void *context)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("NodesChanged");
if (!msg)
return;
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_active_node_changed(void *context,
enum CRAS_STREAM_DIRECTION dir,
cras_node_id_t node_id)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
DBusMessage *msg;
dbus_uint32_t serial = 0;
msg = create_dbus_message((dir == CRAS_STREAM_OUTPUT)
? "ActiveOutputNodeChanged"
: "ActiveInputNodeChanged");
if (!msg)
return;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
/* Called by iodev_list when a node volume changes. */
static void signal_node_volume_changed(void *context,
cras_node_id_t node_id,
int32_t volume)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("OutputNodeVolumeChanged");
if (!msg)
return;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
DBUS_TYPE_INT32, &volume,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_node_capture_gain_changed(void *context,
cras_node_id_t node_id,
int capture_gain)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("InputNodeGainChanged");
if (!msg)
return;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
DBUS_TYPE_INT32, &capture_gain,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_node_left_right_swapped_changed(void *context,
cras_node_id_t node_id,
int swapped)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("NodeLeftRightSwappedChanged");
if (!msg)
return;
dbus_message_append_args(msg,
DBUS_TYPE_UINT64, &node_id,
DBUS_TYPE_BOOLEAN, &swapped,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_num_active_streams_changed(void *context,
enum CRAS_STREAM_DIRECTION dir,
uint32_t num_active_streams)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
dbus_int32_t num;
msg = create_dbus_message("NumberOfActiveStreamsChanged");
if (!msg)
return;
num = cras_system_state_get_active_streams();
dbus_message_append_args(msg,
DBUS_TYPE_INT32, &num,
DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
/* Exported Interface */
void cras_dbus_control_start(DBusConnection *conn)
{
static const DBusObjectPathVTable control_vtable = {
.message_function = handle_control_message,
};
DBusError dbus_error;
struct cras_observer_ops observer_ops;
dbus_control.conn = conn;
dbus_connection_ref(dbus_control.conn);
if (!dbus_connection_register_object_path(conn,
CRAS_ROOT_OBJECT_PATH,
&control_vtable,
&dbus_error)) {
syslog(LOG_WARNING,
"Couldn't register CRAS control: %s: %s",
CRAS_ROOT_OBJECT_PATH, dbus_error.message);
dbus_error_free(&dbus_error);
return;
}
memset(&observer_ops, 0, sizeof(observer_ops));
observer_ops.output_volume_changed = signal_output_volume;
observer_ops.output_mute_changed = signal_output_mute;
observer_ops.capture_gain_changed = signal_capture_gain;
observer_ops.capture_mute_changed = signal_capture_mute;
observer_ops.num_active_streams_changed =
signal_num_active_streams_changed;
observer_ops.nodes_changed = signal_nodes_changed;
observer_ops.active_node_changed = signal_active_node_changed;
observer_ops.input_node_gain_changed = signal_node_capture_gain_changed;
observer_ops.output_node_volume_changed = signal_node_volume_changed;
observer_ops.node_left_right_swapped_changed =
signal_node_left_right_swapped_changed;
dbus_control.observer = cras_observer_add(&observer_ops, &dbus_control);
}
void cras_dbus_control_stop()
{
if (!dbus_control.conn)
return;
dbus_connection_unregister_object_path(dbus_control.conn,
CRAS_ROOT_OBJECT_PATH);
dbus_connection_unref(dbus_control.conn);
dbus_control.conn = NULL;
cras_observer_remove(dbus_control.observer);
dbus_control.observer = NULL;
}