blob: b66e127629d1829a62ddd18c595d3d6e023a743f [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_bt_player.h"
#include "cras_dbus.h"
#include "cras_dbus_control.h"
#include "cras_dbus_util.h"
#include "cras_hfp_ag_profile.h"
#include "cras_iodev_list.h"
#include "cras_main_thread_log.h"
#include "cras_observer.h"
#include "cras_system_state.h"
#include "cras_utf8.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=\"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_mute\" type=\"b\" direction=\"out\"/>\n" \
" <arg name=\"output_user_mute\" type=\"b\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetDefaultOutputBufferSize\">\n" \
" <arg name=\"buffer_size\" type=\"i\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetNodes\">\n" \
" <arg name=\"nodes\" type=\"a{sv}\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetSystemAecSupported\">\n" \
" <arg name=\"supported\" type=\"b\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetSystemAecGroupId\">\n" \
" <arg name=\"group_id\" type=\"i\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"GetDeprioritizeBtWbsMic\">\n" \
" <arg name=\"deprioritized\" type=\"b\" 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=\"SetFixA2dpPacketSize\">\n" \
" <arg name=\"toggle\" type=\"b\" 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=\"GetNumberOfInputStreamsWithPermission\">\n" \
" <arg name=\"num\" type=\"a{sv}\" 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" \
" <method name=\"IsAudioOutputActive\">\n" \
" <arg name=\"active\" type=\"b\" direction=\"out\"/>\n" \
" </method>\n" \
" <method name=\"SetWbsEnabled\">\n" \
" <arg name=\"enabled\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetNoiseCancellationEnabled\">\n" \
" <arg name=\"enabled\" type=\"b\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetPlayerPlaybackStatus\">\n" \
" <arg name=\"status\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetPlayerIdentity\">\n" \
" <arg name=\"identity\" type=\"s\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetPlayerPosition\">\n" \
" <arg name=\"position\" type=\"x\" direction=\"in\"/>\n" \
" </method>\n" \
" <method name=\"SetPlayerMetadata\">\n" \
" <arg name=\"metadata\" type=\"a{sv}\" 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;
}
static bool get_string_metadata(DBusMessageIter *iter, const char **dst)
{
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
return FALSE;
dbus_message_iter_get_basic(iter, dst);
return TRUE;
}
static bool get_int64_metadata(DBusMessageIter *iter, dbus_int64_t *dst)
{
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64)
return FALSE;
dbus_message_iter_get_basic(iter, dst);
return TRUE;
}
static bool get_metadata(DBusMessage *message, const char **title,
const char **artist, const char **album,
dbus_int64_t *length)
{
DBusError dbus_error;
DBusMessageIter iter, dict;
dbus_error_init(&dbus_error);
dbus_message_iter_init(message, &iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
return FALSE;
dbus_message_iter_recurse(&iter, &dict);
while (dbus_message_iter_get_arg_type(&dict) != DBUS_TYPE_INVALID) {
DBusMessageIter entry, var;
const char *key;
if (dbus_message_iter_get_arg_type(&dict) !=
DBUS_TYPE_DICT_ENTRY)
return FALSE;
dbus_message_iter_recurse(&dict, &entry);
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
return FALSE;
dbus_message_iter_get_basic(&entry, &key);
dbus_message_iter_next(&entry);
if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_VARIANT)
return FALSE;
dbus_message_iter_recurse(&entry, &var);
if (strcasecmp(key, "title") == 0) {
if (!get_string_metadata(&var, title))
return FALSE;
} else if (strcasecmp(key, "artist") == 0) {
if (!get_string_metadata(&var, artist))
return FALSE;
} else if (strcasecmp(key, "album") == 0) {
if (!get_string_metadata(&var, album))
return FALSE;
} else if (strcasecmp(key, "length") == 0) {
if (!get_int64_metadata(&var, length))
return FALSE;
} else
syslog(LOG_WARNING, "%s not supported, ignoring", key);
dbus_message_iter_next(&dict);
}
return TRUE;
}
/* 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);
MAINLOG(main_log, MAIN_THREAD_SET_OUTPUT_USER_MUTE, new_mute, 0, 0);
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_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_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_muted = cras_system_get_capture_mute();
dbus_message_append_args(reply, DBUS_TYPE_INT32, &volume,
DBUS_TYPE_BOOLEAN, &system_muted,
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;
}
static DBusHandlerResult
handle_get_default_output_buffer_size(DBusConnection *conn,
DBusMessage *message, void *arg)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
dbus_int32_t buffer_size;
reply = dbus_message_new_method_return(message);
buffer_size = cras_system_get_default_output_buffer_size();
dbus_message_append_args(reply, DBUS_TYPE_INT32, &buffer_size,
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;
const char *node_type = node->type;
const char *node_name = node->name;
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 dev_name is not utf8, libdbus may abort cras.
if (!is_utf8_string(dev_name)) {
syslog(LOG_ERR,
"Non-utf8 device name '%s' cannot be sent via dbus",
dev_name);
dev_name = "";
}
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 stable id migration is needed, use key 'StableDeviceIdNew'
* together with 'StableDeviceId'.
*/
if (!append_key_value(&dict, "StableDeviceId", DBUS_TYPE_UINT64,
DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id))
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, "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_get_system_aec_supported(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
dbus_bool_t system_aec_supported;
reply = dbus_message_new_method_return(message);
system_aec_supported = cras_system_get_aec_supported();
dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN,
&system_aec_supported, DBUS_TYPE_INVALID);
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_get_system_aec_group_id(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
dbus_int32_t system_aec_group_id;
reply = dbus_message_new_method_return(message);
system_aec_group_id = cras_system_get_aec_group_id();
dbus_message_append_args(reply, DBUS_TYPE_INT32, &system_aec_group_id,
DBUS_TYPE_INVALID);
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_get_deprioritize_bt_wbs_mic(DBusConnection *conn, DBusMessage *message,
void *arg)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
dbus_bool_t deprioritized;
reply = dbus_message_new_method_return(message);
deprioritized = cras_system_get_deprioritize_bt_wbs_mic();
dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &deprioritized,
DBUS_TYPE_INVALID);
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_set_fix_a2dp_packet_size(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
int rc;
dbus_bool_t enabled = FALSE;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &enabled);
if (rc)
return rc;
cras_system_set_bt_fix_a2dp_packet_size_enabled(enabled);
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 bool append_num_input_streams_with_permission(
DBusMessage *message, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
{
DBusMessageIter array;
DBusMessageIter dict;
unsigned type;
dbus_message_iter_init_append(message, &array);
for (type = 0; type < CRAS_NUM_CLIENT_TYPE; ++type) {
const char *client_type_str = cras_client_type_str(type);
if (!is_utf8_string(client_type_str)) {
syslog(LOG_ERR,
"Non-utf8 clinet_type_str '%s' cannot be sent "
"via dbus",
client_type_str);
client_type_str = "";
}
if (!dbus_message_iter_open_container(&array, DBUS_TYPE_ARRAY,
"{sv}", &dict))
return false;
if (!append_key_value(&dict, "ClientType", DBUS_TYPE_STRING,
DBUS_TYPE_STRING_AS_STRING,
&client_type_str))
return false;
if (!append_key_value(&dict, "NumStreamsWithPermission",
DBUS_TYPE_UINT32,
DBUS_TYPE_UINT32_AS_STRING,
&num_input_streams[type]))
return false;
if (!dbus_message_iter_close_container(&array, &dict))
return false;
}
return true;
}
static DBusHandlerResult
handle_get_num_input_streams_with_permission(DBusConnection *conn,
DBusMessage *message, void *arg)
{
DBusMessage *reply;
dbus_uint32_t serial = 0;
uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE] = {};
reply = dbus_message_new_method_return(message);
cras_system_state_get_input_streams_with_permission(num_input_streams);
if (!append_num_input_streams_with_permission(reply, num_input_streams))
goto error;
dbus_connection_send(conn, reply, &serial);
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_HANDLED;
error:
dbus_message_unref(reply);
return DBUS_HANDLER_RESULT_NEED_MEMORY;
}
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;
}
static DBusHandlerResult handle_is_audio_active(DBusConnection *conn,
DBusMessage *message, void *arg)
{
dbus_int32_t active = cras_system_state_get_non_empty_status();
send_int32_reply(conn, message, active);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_wbs_enabled(DBusConnection *conn,
DBusMessage *message, void *arg)
{
int rc;
dbus_bool_t enabled;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &enabled);
if (rc)
return rc;
cras_system_set_bt_wbs_enabled(enabled);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult
handle_set_noise_cancellation_enabled(DBusConnection *conn,
DBusMessage *message, void *arg)
{
int rc;
dbus_bool_t enabled;
rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &enabled);
if (rc)
return rc;
cras_system_set_noise_cancellation_enabled(enabled);
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_player_playback_status(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
char *status;
DBusError dbus_error;
int rc;
dbus_error_init(&dbus_error);
rc = get_single_arg(message, DBUS_TYPE_STRING, &status);
if (rc)
return rc;
rc = cras_bt_player_update_playback_status(conn, status);
if (rc) {
syslog(LOG_WARNING,
"CRAS failed to update BT Player Status: %d", rc);
}
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_player_identity(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
char *identity;
DBusError dbus_error;
int rc;
dbus_error_init(&dbus_error);
rc = get_single_arg(message, DBUS_TYPE_STRING, &identity);
if (rc)
return rc;
rc = cras_bt_player_update_identity(conn, identity);
if (rc) {
syslog(LOG_WARNING,
"CRAS failed to update BT Player Identity: %d", rc);
}
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_player_position(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
dbus_int64_t position;
DBusError dbus_error;
int rc;
dbus_error_init(&dbus_error);
rc = get_single_arg(message, DBUS_TYPE_INT64, &position);
if (rc)
return rc;
rc = cras_bt_player_update_position(conn, position);
if (rc) {
syslog(LOG_WARNING,
"CRAS failed to update BT Player Position: %d", rc);
}
send_empty_reply(conn, message);
return DBUS_HANDLER_RESULT_HANDLED;
}
static DBusHandlerResult handle_set_player_metadata(DBusConnection *conn,
DBusMessage *message,
void *arg)
{
DBusError dbus_error;
int rc;
dbus_error_init(&dbus_error);
const char *title = NULL, *artist = NULL, *album = NULL;
dbus_int64_t length = 0;
if (!get_metadata(message, &title, &artist, &album, &length))
return -EINVAL;
rc = cras_bt_player_update_metadata(conn, title, artist, album, length);
if (rc) {
syslog(LOG_WARNING, "CRAS failed to update BT Metadata: %d",
rc);
}
send_empty_reply(conn, message);
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,
"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,
"GetDefaultOutputBufferSize")) {
return handle_get_default_output_buffer_size(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,
"GetSystemAecSupported")) {
return handle_get_system_aec_supported(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"GetSystemAecGroupId")) {
return handle_get_system_aec_group_id(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"GetDeprioritizeBtWbsMic")) {
return handle_get_deprioritize_bt_wbs_mic(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,
"SetFixA2dpPacketSize")) {
return handle_set_fix_a2dp_packet_size(conn, message, arg);
} 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,
"GetNumberOfInputStreamsWithPermission")) {
return handle_get_num_input_streams_with_permission(
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);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"IsAudioOutputActive")) {
return handle_is_audio_active(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetWbsEnabled")) {
return handle_set_wbs_enabled(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetNoiseCancellationEnabled")) {
return handle_set_noise_cancellation_enabled(conn, message,
arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetPlayerPlaybackStatus")) {
return handle_set_player_playback_status(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetPlayerIdentity")) {
return handle_set_player_identity(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetPlayerPosition")) {
return handle_set_player_position(conn, message, arg);
} else if (dbus_message_is_method_call(message, CRAS_CONTROL_INTERFACE,
"SetPlayerMetadata")) {
return handle_set_player_metadata(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);
}
static void signal_num_input_streams_with_permission_changed(
void *context, uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("NumberOfInputStreamsWithPermissionChanged");
if (!msg)
return;
if (!append_num_input_streams_with_permission(msg, num_input_streams))
goto error;
dbus_connection_send(control->conn, msg, &serial);
error:
dbus_message_unref(msg);
}
static void signal_hotword_triggered(void *context, int64_t tv_sec,
int64_t tv_nsec)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("HotwordTriggered");
if (!msg)
return;
dbus_message_append_args(msg, DBUS_TYPE_INT64, &tv_sec, DBUS_TYPE_INT64,
&tv_nsec, DBUS_TYPE_INVALID);
dbus_connection_send(control->conn, msg, &serial);
dbus_message_unref(msg);
}
static void signal_non_empty_audio_state_changed(void *context, int non_empty)
{
struct cras_dbus_control *control = (struct cras_dbus_control *)context;
dbus_uint32_t serial = 0;
DBusMessage *msg;
msg = create_dbus_message("AudioOutputActiveStateChanged");
if (!msg)
return;
dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &non_empty,
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.num_input_streams_with_permission_changed =
signal_num_input_streams_with_permission_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;
observer_ops.hotword_triggered = signal_hotword_triggered;
observer_ops.non_empty_audio_state_changed =
signal_non_empty_audio_state_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;
}