• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* Copyright (c) 2013 The Chromium OS 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 
6 #include <dbus/dbus.h>
7 #include <errno.h>
8 #include <stdint.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <syslog.h>
12 
13 #include "audio_thread.h"
14 #include "cras_dbus.h"
15 #include "cras_dbus_control.h"
16 #include "cras_dbus_util.h"
17 #include "cras_iodev_list.h"
18 #include "cras_observer.h"
19 #include "cras_system_state.h"
20 #include "cras_util.h"
21 #include "utlist.h"
22 
23 #define CRAS_CONTROL_INTERFACE "org.chromium.cras.Control"
24 #define CRAS_ROOT_OBJECT_PATH "/org/chromium/cras"
25 #define CONTROL_INTROSPECT_XML                                          \
26     DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE                           \
27     "<node>\n"                                                          \
28     "  <interface name=\""CRAS_CONTROL_INTERFACE"\">\n"                 \
29     "    <method name=\"SetOutputVolume\">\n"                           \
30     "      <arg name=\"volume\" type=\"i\" direction=\"in\"/>\n"        \
31     "    </method>\n"                                                   \
32     "    <method name=\"SetOutputNodeVolume\">\n"                       \
33     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
34     "      <arg name=\"volume\" type=\"i\" direction=\"in\"/>\n"        \
35     "    </method>\n"                                                   \
36     "    <method name=\"SwapLeftRight\">\n"                             \
37     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
38     "      <arg name=\"swap\" type=\"b\" direction=\"in\"/>\n"          \
39     "    </method>\n"                                                   \
40     "    <method name=\"SetOutputMute\">\n"                             \
41     "      <arg name=\"mute_on\" type=\"b\" direction=\"in\"/>\n"       \
42     "    </method>\n"                                                   \
43     "    <method name=\"SetOutputUserMute\">\n"                         \
44     "      <arg name=\"mute_on\" type=\"b\" direction=\"in\"/>\n"       \
45     "    </method>\n"                                                   \
46     "    <method name=\"SetSuspendAudio\">\n"                           \
47     "      <arg name=\"suspend\" type=\"b\" direction=\"in\"/>\n"       \
48     "    </method>\n"                                                   \
49     "    <method name=\"SetInputGain\">\n"                              \
50     "      <arg name=\"gain\" type=\"i\" direction=\"in\"/>\n"          \
51     "    </method>\n"                                                   \
52     "    <method name=\"SetInputNodeGain\">\n"                          \
53     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
54     "      <arg name=\"gain\" type=\"i\" direction=\"in\"/>\n"          \
55     "    </method>\n"                                                   \
56     "    <method name=\"SetInputMute\">\n"                              \
57     "      <arg name=\"mute_on\" type=\"b\" direction=\"in\"/>\n"       \
58     "    </method>\n"                                                   \
59     "    <method name=\"GetVolumeState\">\n"                            \
60     "      <arg name=\"output_volume\" type=\"i\" direction=\"out\"/>\n"\
61     "      <arg name=\"output_mute\" type=\"b\" direction=\"out\"/>\n"  \
62     "      <arg name=\"input_gain\" type=\"i\" direction=\"out\"/>\n"   \
63     "      <arg name=\"input_mute\" type=\"b\" direction=\"out\"/>\n"   \
64     "      <arg name=\"output_user_mute\" type=\"b\" direction=\"out\"/>\n"\
65     "    </method>\n"                                                   \
66     "    <method name=\"GetNodes\">\n"                                  \
67     "      <arg name=\"nodes\" type=\"a{sv}\" direction=\"out\"/>\n"    \
68     "    </method>\n"                                                   \
69     "    <method name=\"SetActiveOutputNode\">\n"                       \
70     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
71     "    </method>\n"                                                   \
72     "    <method name=\"SetActiveInputNode\">\n"                        \
73     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
74     "    </method>\n"                                                   \
75     "    <method name=\"AddActiveInputNode\">\n"                        \
76     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
77     "    </method>\n"                                                   \
78     "    <method name=\"AddActiveOutputNode\">\n"                       \
79     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
80     "    </method>\n"                                                   \
81     "    <method name=\"RemoveActiveInputNode\">\n"                     \
82     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
83     "    </method>\n"                                                   \
84     "    <method name=\"RemoveActiveOutputNode\">\n"                    \
85     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
86     "    </method>\n"                                                   \
87     "    <method name=\"GetNumberOfActiveStreams\">\n"                  \
88     "      <arg name=\"num\" type=\"i\" direction=\"out\"/>\n"          \
89     "    </method>\n"                                                   \
90     "    <method name=\"GetNumberOfActiveOutputStreams\">\n"            \
91     "      <arg name=\"num\" type=\"i\" direction=\"out\"/>\n"          \
92     "    </method>\n"                                                   \
93     "    <method name=\"GetNumberOfActiveInputStreams\">\n"             \
94     "      <arg name=\"num\" type=\"i\" direction=\"out\"/>\n"          \
95     "    </method>\n"                                                   \
96     "    <method name=\"SetGlobalOutputChannelRemix\">\n"               \
97     "      <arg name=\"num_channels\" type=\"i\" direction=\"in\"/>\n"  \
98     "      <arg name=\"coefficient\" type=\"ad\" direction=\"in\"/>\n"  \
99     "    </method>\n"                                                   \
100     "    <method name=\"SetHotwordModel\">\n"                           \
101     "      <arg name=\"node_id\" type=\"t\" direction=\"in\"/>\n"       \
102     "      <arg name=\"model_name\" type=\"s\" direction=\"in\"/>\n"    \
103     "    </method>\n"                                                   \
104     "  </interface>\n"                                                  \
105     "  <interface name=\"" DBUS_INTERFACE_INTROSPECTABLE "\">\n"        \
106     "    <method name=\"Introspect\">\n"                                \
107     "      <arg name=\"data\" type=\"s\" direction=\"out\"/>\n"         \
108     "    </method>\n"                                                   \
109     "  </interface>\n"                                                  \
110     "</node>\n"
111 
112 struct cras_dbus_control {
113 	DBusConnection *conn;
114 	struct cras_observer_client *observer;
115 };
116 static struct cras_dbus_control dbus_control;
117 
118 /* helper to extract a single argument from a DBus message. */
get_single_arg(DBusMessage * message,int dbus_type,void * arg)119 static int get_single_arg(DBusMessage *message, int dbus_type, void *arg)
120 {
121 	DBusError dbus_error;
122 
123 	dbus_error_init(&dbus_error);
124 
125 	if (!dbus_message_get_args(message, &dbus_error,
126 				   dbus_type, arg,
127 				   DBUS_TYPE_INVALID)) {
128 		syslog(LOG_WARNING,
129 		       "Bad method received: %s",
130 		       dbus_error.message);
131 		dbus_error_free(&dbus_error);
132 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
133 	}
134 
135 	return 0;
136 }
137 
138 /* Helper to send an empty reply. */
send_empty_reply(DBusConnection * conn,DBusMessage * message)139 static void send_empty_reply(DBusConnection *conn, DBusMessage *message)
140 {
141 	DBusMessage *reply;
142 	dbus_uint32_t serial = 0;
143 
144 	reply = dbus_message_new_method_return(message);
145 	if (!reply)
146 		return;
147 
148 	dbus_connection_send(conn, reply, &serial);
149 
150 	dbus_message_unref(reply);
151 }
152 
153 /* Helper to send an int32 reply. */
send_int32_reply(DBusConnection * conn,DBusMessage * message,dbus_int32_t value)154 static void send_int32_reply(DBusConnection *conn,
155 			     DBusMessage *message,
156 			     dbus_int32_t value)
157 {
158 	DBusMessage *reply;
159 	dbus_uint32_t serial = 0;
160 
161 	reply = dbus_message_new_method_return(message);
162 	if (!reply)
163 		return;
164 
165 	dbus_message_append_args(reply,
166 				 DBUS_TYPE_INT32, &value,
167 				 DBUS_TYPE_INVALID);
168 	dbus_connection_send(conn, reply, &serial);
169 
170 	dbus_message_unref(reply);
171 }
172 
173 /* Handlers for exported DBus method calls. */
handle_set_output_volume(DBusConnection * conn,DBusMessage * message,void * arg)174 static DBusHandlerResult handle_set_output_volume(
175 	DBusConnection *conn,
176 	DBusMessage *message,
177 	void *arg)
178 {
179 	int rc;
180 	dbus_int32_t new_vol;
181 
182 	rc = get_single_arg(message, DBUS_TYPE_INT32, &new_vol);
183 	if (rc)
184 		return rc;
185 
186 	cras_system_set_volume(new_vol);
187 
188 	send_empty_reply(conn, message);
189 
190 	return DBUS_HANDLER_RESULT_HANDLED;
191 }
192 
handle_set_output_node_volume(DBusConnection * conn,DBusMessage * message,void * arg)193 static DBusHandlerResult handle_set_output_node_volume(
194 	DBusConnection *conn,
195 	DBusMessage *message,
196 	void *arg)
197 {
198 	dbus_int32_t new_vol;
199 	cras_node_id_t id;
200 	DBusError dbus_error;
201 
202 	dbus_error_init(&dbus_error);
203 
204 	if (!dbus_message_get_args(message, &dbus_error,
205 				   DBUS_TYPE_UINT64, &id,
206 				   DBUS_TYPE_INT32, &new_vol,
207 				   DBUS_TYPE_INVALID)) {
208 		syslog(LOG_WARNING,
209 		       "Bad method received: %s",
210 		       dbus_error.message);
211 		dbus_error_free(&dbus_error);
212 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
213 	}
214 
215 	cras_iodev_list_set_node_attr(id, IONODE_ATTR_VOLUME, new_vol);
216 
217 	send_empty_reply(conn, message);
218 
219 	return DBUS_HANDLER_RESULT_HANDLED;
220 }
221 
handle_swap_left_right(DBusConnection * conn,DBusMessage * message,void * arg)222 static DBusHandlerResult handle_swap_left_right(
223 	DBusConnection *conn,
224 	DBusMessage *message,
225 	void *arg)
226 {
227 	cras_node_id_t id;
228 	dbus_bool_t swap;
229 	DBusError dbus_error;
230 
231 	dbus_error_init(&dbus_error);
232 
233 	if (!dbus_message_get_args(message, &dbus_error,
234 				   DBUS_TYPE_UINT64, &id,
235 				   DBUS_TYPE_BOOLEAN, &swap,
236 				   DBUS_TYPE_INVALID)) {
237 		syslog(LOG_WARNING,
238 		       "Bad method received: %s",
239 		       dbus_error.message);
240 		dbus_error_free(&dbus_error);
241 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
242 	}
243 
244 	cras_iodev_list_set_node_attr(id, IONODE_ATTR_SWAP_LEFT_RIGHT,
245 				      swap);
246 
247 	send_empty_reply(conn, message);
248 
249 	return DBUS_HANDLER_RESULT_HANDLED;
250 }
251 
handle_set_output_mute(DBusConnection * conn,DBusMessage * message,void * arg)252 static DBusHandlerResult handle_set_output_mute(
253 	DBusConnection *conn,
254 	DBusMessage *message,
255 	void *arg)
256 {
257 	int rc;
258 	dbus_bool_t new_mute;
259 
260 	rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &new_mute);
261 	if (rc)
262 		return rc;
263 
264 	cras_system_set_mute(new_mute);
265 
266 	send_empty_reply(conn, message);
267 
268 	return DBUS_HANDLER_RESULT_HANDLED;
269 }
270 
handle_set_output_user_mute(DBusConnection * conn,DBusMessage * message,void * arg)271 static DBusHandlerResult handle_set_output_user_mute(
272 	DBusConnection *conn,
273 	DBusMessage *message,
274 	void *arg)
275 {
276 	int rc;
277 	dbus_bool_t new_mute;
278 
279 	rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &new_mute);
280 	if (rc)
281 		return rc;
282 
283 	cras_system_set_user_mute(new_mute);
284 
285 	send_empty_reply(conn, message);
286 
287 	return DBUS_HANDLER_RESULT_HANDLED;
288 }
289 
handle_set_suspend_audio(DBusConnection * conn,DBusMessage * message,void * arg)290 static DBusHandlerResult handle_set_suspend_audio(
291 	DBusConnection *conn,
292 	DBusMessage *message,
293 	void *arg)
294 {
295 	int rc;
296 	dbus_bool_t suspend;
297 	rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &suspend);
298 	if (rc)
299 		return rc;
300 
301 	cras_system_set_suspended(suspend);
302 
303 	send_empty_reply(conn, message);
304 
305 	return DBUS_HANDLER_RESULT_HANDLED;
306 }
307 
handle_set_input_gain(DBusConnection * conn,DBusMessage * message,void * arg)308 static DBusHandlerResult handle_set_input_gain(
309 	DBusConnection *conn,
310 	DBusMessage *message,
311 	void *arg)
312 {
313 	int rc;
314 	dbus_int32_t new_gain;
315 
316 	rc = get_single_arg(message, DBUS_TYPE_INT32, &new_gain);
317 	if (rc)
318 		return rc;
319 
320 	cras_system_set_capture_gain(new_gain);
321 
322 	send_empty_reply(conn, message);
323 
324 	return DBUS_HANDLER_RESULT_HANDLED;
325 }
326 
handle_set_input_node_gain(DBusConnection * conn,DBusMessage * message,void * arg)327 static DBusHandlerResult handle_set_input_node_gain(
328 	DBusConnection *conn,
329 	DBusMessage *message,
330 	void *arg)
331 {
332 	dbus_int32_t new_gain;
333 	cras_node_id_t id;
334 	DBusError dbus_error;
335 
336 	dbus_error_init(&dbus_error);
337 
338 	if (!dbus_message_get_args(message, &dbus_error,
339 				   DBUS_TYPE_UINT64, &id,
340 				   DBUS_TYPE_INT32, &new_gain,
341 				   DBUS_TYPE_INVALID)) {
342 		syslog(LOG_WARNING,
343 		       "Bad method received: %s",
344 		       dbus_error.message);
345 		dbus_error_free(&dbus_error);
346 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
347 	}
348 
349 	cras_iodev_list_set_node_attr(id, IONODE_ATTR_CAPTURE_GAIN, new_gain);
350 
351 	send_empty_reply(conn, message);
352 
353 	return DBUS_HANDLER_RESULT_HANDLED;
354 }
355 
handle_set_input_mute(DBusConnection * conn,DBusMessage * message,void * arg)356 static DBusHandlerResult handle_set_input_mute(
357 	DBusConnection *conn,
358 	DBusMessage *message,
359 	void *arg)
360 {
361 	int rc;
362 	dbus_bool_t new_mute;
363 
364 	rc = get_single_arg(message, DBUS_TYPE_BOOLEAN, &new_mute);
365 	if (rc)
366 		return rc;
367 
368 	cras_system_set_capture_mute(new_mute);
369 
370 	send_empty_reply(conn, message);
371 
372 	return DBUS_HANDLER_RESULT_HANDLED;
373 }
374 
handle_get_volume_state(DBusConnection * conn,DBusMessage * message,void * arg)375 static DBusHandlerResult handle_get_volume_state(
376 	DBusConnection *conn,
377 	DBusMessage *message,
378 	void *arg)
379 {
380 	DBusMessage *reply;
381 	dbus_uint32_t serial = 0;
382 	dbus_int32_t volume;
383 	dbus_bool_t system_muted;
384 	dbus_bool_t user_muted;
385 	dbus_int32_t capture_gain;
386 	dbus_bool_t capture_muted;
387 
388 	reply = dbus_message_new_method_return(message);
389 
390 	volume = cras_system_get_volume();
391 	system_muted = cras_system_get_system_mute();
392 	user_muted = cras_system_get_user_mute();
393 	capture_gain = cras_system_get_capture_gain();
394 	capture_muted = cras_system_get_capture_mute();
395 
396 	dbus_message_append_args(reply,
397 				 DBUS_TYPE_INT32, &volume,
398 				 DBUS_TYPE_BOOLEAN, &system_muted,
399 				 DBUS_TYPE_INT32, &capture_gain,
400 				 DBUS_TYPE_BOOLEAN, &capture_muted,
401 				 DBUS_TYPE_BOOLEAN, &user_muted,
402 				 DBUS_TYPE_INVALID);
403 
404 	dbus_connection_send(conn, reply, &serial);
405 
406 	dbus_message_unref(reply);
407 
408 	return DBUS_HANDLER_RESULT_HANDLED;
409 }
410 
411 /* Appends the information about a node to the dbus message. Returns
412  * false if not enough memory. */
append_node_dict(DBusMessageIter * iter,const struct cras_iodev_info * dev,const struct cras_ionode_info * node,enum CRAS_STREAM_DIRECTION direction)413 static dbus_bool_t append_node_dict(DBusMessageIter *iter,
414 				    const struct cras_iodev_info *dev,
415 				    const struct cras_ionode_info *node,
416 				    enum CRAS_STREAM_DIRECTION direction)
417 {
418 	DBusMessageIter dict;
419 	dbus_bool_t is_input;
420 	dbus_uint64_t id;
421 	const char *dev_name = dev->name;
422 	dbus_uint64_t stable_dev_id = node->stable_id;
423 	dbus_uint64_t stable_dev_id_new = node->stable_id_new;
424 	const char *node_type = node->type;
425 	const char *node_name = node->name;
426 	const char *mic_positions = node->mic_positions;
427 	dbus_bool_t active;
428 	dbus_uint64_t plugged_time = node->plugged_time.tv_sec * 1000000ULL +
429 		node->plugged_time.tv_usec;
430 	dbus_uint64_t node_volume = node->volume;
431 	dbus_int64_t node_capture_gain = node->capture_gain;
432 	char *models, *empty_models = "";
433 
434 	is_input = (direction == CRAS_STREAM_INPUT);
435 	id = node->iodev_idx;
436 	id = (id << 32) | node->ionode_idx;
437 	active = !!node->active;
438 
439 	if (!dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}",
440 					      &dict))
441 		return FALSE;
442 	if (!append_key_value(&dict, "IsInput", DBUS_TYPE_BOOLEAN,
443 			      DBUS_TYPE_BOOLEAN_AS_STRING, &is_input))
444 		return FALSE;
445 	if (!append_key_value(&dict, "Id", DBUS_TYPE_UINT64,
446 			      DBUS_TYPE_UINT64_AS_STRING, &id))
447 		return FALSE;
448 	if (!append_key_value(&dict, "DeviceName", DBUS_TYPE_STRING,
449 			      DBUS_TYPE_STRING_AS_STRING, &dev_name))
450 		return FALSE;
451 	if (!append_key_value(&dict, "StableDeviceId", DBUS_TYPE_UINT64,
452 			      DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id))
453 		return FALSE;
454 	if (!append_key_value(&dict, "StableDeviceIdNew", DBUS_TYPE_UINT64,
455 			      DBUS_TYPE_UINT64_AS_STRING, &stable_dev_id_new))
456 		return FALSE;
457 	if (!append_key_value(&dict, "Type", DBUS_TYPE_STRING,
458 			      DBUS_TYPE_STRING_AS_STRING, &node_type))
459 		return FALSE;
460 	if (!append_key_value(&dict, "Name", DBUS_TYPE_STRING,
461 			      DBUS_TYPE_STRING_AS_STRING, &node_name))
462 		return FALSE;
463 	if (!append_key_value(&dict, "MicPositions", DBUS_TYPE_STRING,
464 			      DBUS_TYPE_STRING_AS_STRING, &mic_positions))
465 		return FALSE;
466 	if (!append_key_value(&dict, "Active", DBUS_TYPE_BOOLEAN,
467 			      DBUS_TYPE_BOOLEAN_AS_STRING, &active))
468 		return FALSE;
469 	if (!append_key_value(&dict, "PluggedTime", DBUS_TYPE_UINT64,
470 			      DBUS_TYPE_UINT64_AS_STRING, &plugged_time))
471 		return FALSE;
472 	if (!append_key_value(&dict, "NodeVolume", DBUS_TYPE_UINT64,
473 			      DBUS_TYPE_UINT64_AS_STRING, &node_volume))
474 		return FALSE;
475 	if (!append_key_value(&dict, "NodeCaptureGain", DBUS_TYPE_INT64,
476 			      DBUS_TYPE_INT64_AS_STRING, &node_capture_gain))
477 		return FALSE;
478 
479 	models = cras_iodev_list_get_hotword_models(id);
480 	if (!append_key_value(&dict, "HotwordModels", DBUS_TYPE_STRING,
481 			      DBUS_TYPE_STRING_AS_STRING,
482 			      models ? &models : &empty_models)) {
483 		free(models);
484 		return FALSE;
485 	}
486 	free(models);
487 
488 	if (!dbus_message_iter_close_container(iter, &dict))
489 		return FALSE;
490 
491 	return TRUE;
492 }
493 
494 /* Appends the information about all nodes in a given direction. Returns false
495  * if not enough memory. */
append_nodes(enum CRAS_STREAM_DIRECTION direction,DBusMessageIter * array)496 static dbus_bool_t append_nodes(enum CRAS_STREAM_DIRECTION direction,
497 				DBusMessageIter *array)
498 {
499 	const struct cras_iodev_info *devs;
500 	const struct cras_ionode_info *nodes;
501 	int ndevs, nnodes;
502 	int i, j;
503 
504 	if (direction == CRAS_STREAM_OUTPUT) {
505 		ndevs = cras_system_state_get_output_devs(&devs);
506 		nnodes = cras_system_state_get_output_nodes(&nodes);
507 	} else {
508 		ndevs = cras_system_state_get_input_devs(&devs);
509 		nnodes = cras_system_state_get_input_nodes(&nodes);
510 	}
511 
512 	for (i = 0; i < nnodes; i++) {
513 		/* Don't reply unplugged nodes. */
514 		if (!nodes[i].plugged)
515 			continue;
516 		/* Find the device for this node. */
517 		for (j = 0; j < ndevs; j++)
518 			if (devs[j].idx == nodes[i].iodev_idx)
519 				break;
520 		if (j == ndevs)
521 			continue;
522 		/* Send information about this node. */
523 		if (!append_node_dict(array, &devs[j], &nodes[i], direction))
524 			return FALSE;
525 	}
526 
527 	return TRUE;
528 }
529 
handle_get_nodes(DBusConnection * conn,DBusMessage * message,void * arg)530 static DBusHandlerResult handle_get_nodes(DBusConnection *conn,
531 					  DBusMessage *message,
532 					  void *arg)
533 {
534 	DBusMessage *reply;
535 	DBusMessageIter array;
536 	dbus_uint32_t serial = 0;
537 
538 	reply = dbus_message_new_method_return(message);
539 	dbus_message_iter_init_append(reply, &array);
540 	if (!append_nodes(CRAS_STREAM_OUTPUT, &array))
541 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
542 	if (!append_nodes(CRAS_STREAM_INPUT, &array))
543 		return DBUS_HANDLER_RESULT_NEED_MEMORY;
544 	dbus_connection_send(conn, reply, &serial);
545 	dbus_message_unref(reply);
546 
547 	return DBUS_HANDLER_RESULT_HANDLED;
548 }
549 
550 static DBusHandlerResult
handle_set_active_node(DBusConnection * conn,DBusMessage * message,void * arg,enum CRAS_STREAM_DIRECTION direction)551 handle_set_active_node(DBusConnection *conn,
552 		       DBusMessage *message,
553 		       void *arg,
554 		       enum CRAS_STREAM_DIRECTION direction)
555 {
556 	int rc;
557 	cras_node_id_t id;
558 
559 	rc = get_single_arg(message, DBUS_TYPE_UINT64, &id);
560 	if (rc)
561 		return rc;
562 
563 	cras_iodev_list_select_node(direction, id);
564 
565 	send_empty_reply(conn, message);
566 
567 	return DBUS_HANDLER_RESULT_HANDLED;
568 }
569 
570 static DBusHandlerResult
handle_add_active_node(DBusConnection * conn,DBusMessage * message,void * arg,enum CRAS_STREAM_DIRECTION direction)571 handle_add_active_node(DBusConnection *conn,
572 		       DBusMessage *message,
573 		       void *arg,
574                        enum CRAS_STREAM_DIRECTION direction)
575 {
576 	int rc;
577 	cras_node_id_t id;
578 
579 	rc = get_single_arg(message, DBUS_TYPE_UINT64, &id);
580 	if (rc)
581 		return rc;
582 
583 	cras_iodev_list_add_active_node(direction, id);
584 
585 	send_empty_reply(conn, message);
586 
587 	return DBUS_HANDLER_RESULT_HANDLED;
588 }
589 
590 static DBusHandlerResult
handle_rm_active_node(DBusConnection * conn,DBusMessage * message,void * arg,enum CRAS_STREAM_DIRECTION direction)591 handle_rm_active_node(DBusConnection *conn,
592                       DBusMessage *message,
593                       void *arg,
594                       enum CRAS_STREAM_DIRECTION direction)
595 {
596         int rc;
597         cras_node_id_t id;
598 
599         rc = get_single_arg(message, DBUS_TYPE_UINT64, &id);
600         if (rc)
601                 return rc;
602 
603         cras_iodev_list_rm_active_node(direction, id);
604 
605         send_empty_reply(conn, message);
606 
607         return DBUS_HANDLER_RESULT_HANDLED;
608 }
609 
handle_get_num_active_streams(DBusConnection * conn,DBusMessage * message,void * arg)610 static DBusHandlerResult handle_get_num_active_streams(
611 	DBusConnection *conn,
612 	DBusMessage *message,
613 	void *arg)
614 {
615 	send_int32_reply(conn, message, cras_system_state_get_active_streams());
616 	return DBUS_HANDLER_RESULT_HANDLED;
617 }
618 
handle_get_num_active_streams_use_input_hw(DBusConnection * conn,DBusMessage * message,void * arg)619 static DBusHandlerResult handle_get_num_active_streams_use_input_hw(
620 	DBusConnection *conn,
621 	DBusMessage *message,
622 	void *arg)
623 {
624 	dbus_int32_t num = 0;
625 	unsigned i;
626 
627 	for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
628 		if (cras_stream_uses_input_hw(i))
629 			num += cras_system_state_get_active_streams_by_direction(i);
630 	}
631 	send_int32_reply(conn, message, num);
632 
633 	return DBUS_HANDLER_RESULT_HANDLED;
634 }
635 
handle_get_num_active_streams_use_output_hw(DBusConnection * conn,DBusMessage * message,void * arg)636 static DBusHandlerResult handle_get_num_active_streams_use_output_hw(
637 	DBusConnection *conn,
638 	DBusMessage *message,
639 	void *arg)
640 {
641 	dbus_int32_t num = 0;
642 	unsigned i;
643 
644 	for (i = 0; i < CRAS_NUM_DIRECTIONS; i++) {
645 		if (cras_stream_uses_output_hw(i))
646 			num += cras_system_state_get_active_streams_by_direction(i);
647 	}
648 	send_int32_reply(conn, message, num);
649 
650 	return DBUS_HANDLER_RESULT_HANDLED;
651 }
652 
handle_set_global_output_channel_remix(DBusConnection * conn,DBusMessage * message,void * arg)653 static DBusHandlerResult handle_set_global_output_channel_remix(
654 	DBusConnection *conn,
655 	DBusMessage *message,
656 	void *arg)
657 {
658 	dbus_int32_t num_channels;
659 	double *coeff_array;
660 	dbus_int32_t count;
661 	DBusError dbus_error;
662 	float *coefficient;
663 	int i;
664 
665 	dbus_error_init(&dbus_error);
666 
667 	if (!dbus_message_get_args(message, &dbus_error,
668 			DBUS_TYPE_INT32, &num_channels,
669 			DBUS_TYPE_ARRAY,
670 			      DBUS_TYPE_DOUBLE, &coeff_array, &count,
671 			      DBUS_TYPE_INVALID)) {
672 		syslog(LOG_WARNING, "Set global output channel remix error: %s",
673 			dbus_error.message);
674 		dbus_error_free(&dbus_error);
675 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
676 	}
677 
678 	coefficient = (float *)calloc(count, sizeof(*coefficient));
679 	if (!coefficient)
680 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
681 
682 	for (i = 0; i < count; i++)
683 		coefficient[i] = coeff_array[i];
684 
685 	audio_thread_config_global_remix(
686 			cras_iodev_list_get_audio_thread(),
687 			num_channels,
688 			coefficient);
689 
690 	send_empty_reply(conn, message);
691 	free(coefficient);
692 	return DBUS_HANDLER_RESULT_HANDLED;
693 }
694 
handle_set_hotword_model(DBusConnection * conn,DBusMessage * message,void * arg)695 static DBusHandlerResult handle_set_hotword_model(
696 	DBusConnection *conn,
697 	DBusMessage *message,
698 	void *arg)
699 {
700 	cras_node_id_t id;
701 	const char *model_name;
702 	DBusError dbus_error;
703 	dbus_int32_t ret;
704 
705 	dbus_error_init(&dbus_error);
706 
707 	if (!dbus_message_get_args(message, &dbus_error,
708 				   DBUS_TYPE_UINT64, &id,
709 				   DBUS_TYPE_STRING, &model_name,
710 				   DBUS_TYPE_INVALID)) {
711 		syslog(LOG_WARNING,
712 		       "Bad method received: %s",
713 		       dbus_error.message);
714 		dbus_error_free(&dbus_error);
715 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
716 	}
717 
718 	ret = cras_iodev_list_set_hotword_model(id, model_name);
719 	send_int32_reply(conn, message, ret);
720 
721 	return DBUS_HANDLER_RESULT_HANDLED;
722 }
723 
724 /* Handle incoming messages. */
handle_control_message(DBusConnection * conn,DBusMessage * message,void * arg)725 static DBusHandlerResult handle_control_message(DBusConnection *conn,
726 						DBusMessage *message,
727 						void *arg)
728 {
729 	syslog(LOG_DEBUG, "Control message: %s %s %s",
730 	       dbus_message_get_path(message),
731 	       dbus_message_get_interface(message),
732 	       dbus_message_get_member(message));
733 
734 	if (dbus_message_is_method_call(message,
735 					DBUS_INTERFACE_INTROSPECTABLE,
736 					"Introspect")) {
737 		DBusMessage *reply;
738 		const char *xml = CONTROL_INTROSPECT_XML;
739 
740 		reply = dbus_message_new_method_return(message);
741 		if (!reply)
742 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
743 		if (!dbus_message_append_args(reply,
744 					      DBUS_TYPE_STRING, &xml,
745 					      DBUS_TYPE_INVALID))
746 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
747 		if (!dbus_connection_send(conn, reply, NULL))
748 			return DBUS_HANDLER_RESULT_NEED_MEMORY;
749 
750 		dbus_message_unref(reply);
751 		return DBUS_HANDLER_RESULT_HANDLED;
752 
753 	} else if (dbus_message_is_method_call(message,
754 					       CRAS_CONTROL_INTERFACE,
755 					       "SetOutputVolume")) {
756 		return handle_set_output_volume(conn, message, arg);
757 	} else if (dbus_message_is_method_call(message,
758 					       CRAS_CONTROL_INTERFACE,
759 					       "SetOutputNodeVolume")) {
760 		return handle_set_output_node_volume(conn, message, arg);
761 	} else if (dbus_message_is_method_call(message,
762 					       CRAS_CONTROL_INTERFACE,
763 					       "SwapLeftRight")) {
764 		return handle_swap_left_right(conn, message, arg);
765 	} else if (dbus_message_is_method_call(message,
766 					       CRAS_CONTROL_INTERFACE,
767 					       "SetOutputMute")) {
768 		return handle_set_output_mute(conn, message, arg);
769 	} else if (dbus_message_is_method_call(message,
770 					       CRAS_CONTROL_INTERFACE,
771 					       "SetOutputUserMute")) {
772 		return handle_set_output_user_mute(conn, message, arg);
773 	} else if (dbus_message_is_method_call(message,
774 					       CRAS_CONTROL_INTERFACE,
775 					       "SetSuspendAudio")) {
776 		return handle_set_suspend_audio(conn, message, arg);
777 	} else if (dbus_message_is_method_call(message,
778 					       CRAS_CONTROL_INTERFACE,
779 					       "SetInputGain")) {
780 		return handle_set_input_gain(conn, message, arg);
781 	} else if (dbus_message_is_method_call(message,
782 					       CRAS_CONTROL_INTERFACE,
783 					       "SetInputNodeGain")) {
784 		return handle_set_input_node_gain(conn, message, arg);
785 	} else if (dbus_message_is_method_call(message,
786 					       CRAS_CONTROL_INTERFACE,
787 					       "SetInputMute")) {
788 		return handle_set_input_mute(conn, message, arg);
789 	} else if (dbus_message_is_method_call(message,
790 					       CRAS_CONTROL_INTERFACE,
791 					       "GetVolumeState")) {
792 		return handle_get_volume_state(conn, message, arg);
793 	} else if (dbus_message_is_method_call(message,
794 					       CRAS_CONTROL_INTERFACE,
795 					       "GetNodes")) {
796 		return handle_get_nodes(conn, message, arg);
797 	} else if (dbus_message_is_method_call(message,
798 					       CRAS_CONTROL_INTERFACE,
799 					       "SetActiveOutputNode")) {
800 		return handle_set_active_node(conn, message, arg,
801 					      CRAS_STREAM_OUTPUT);
802 	} else if (dbus_message_is_method_call(message,
803 					       CRAS_CONTROL_INTERFACE,
804 					       "SetActiveInputNode")) {
805 		return handle_set_active_node(conn, message, arg,
806 					      CRAS_STREAM_INPUT);
807 	} else if (dbus_message_is_method_call(message,
808 					       CRAS_CONTROL_INTERFACE,
809 					       "AddActiveInputNode")) {
810 		return handle_add_active_node(conn, message, arg,
811                                               CRAS_STREAM_INPUT);
812         } else if (dbus_message_is_method_call(message,
813                                                CRAS_CONTROL_INTERFACE,
814                                                "AddActiveOutputNode")) {
815                 return handle_add_active_node(conn, message, arg,
816                                               CRAS_STREAM_OUTPUT);
817         } else if (dbus_message_is_method_call(message,
818                                                CRAS_CONTROL_INTERFACE,
819                                                "RemoveActiveInputNode")) {
820                 return handle_rm_active_node(conn, message, arg,
821                                              CRAS_STREAM_INPUT);
822         } else if (dbus_message_is_method_call(message,
823                                                CRAS_CONTROL_INTERFACE,
824                                                "RemoveActiveOutputNode")) {
825                 return handle_rm_active_node(conn, message, arg,
826                                              CRAS_STREAM_OUTPUT);
827 	} else if (dbus_message_is_method_call(message,
828 					       CRAS_CONTROL_INTERFACE,
829 					       "GetNumberOfActiveStreams")) {
830 		return handle_get_num_active_streams(conn, message, arg);
831 	} else if (dbus_message_is_method_call(message,
832 						   CRAS_CONTROL_INTERFACE,
833 						   "GetNumberOfActiveInputStreams")) {
834 		return handle_get_num_active_streams_use_input_hw(
835 				conn, message, arg);
836 	} else if (dbus_message_is_method_call(message,
837 						   CRAS_CONTROL_INTERFACE,
838 						   "GetNumberOfActiveOutputStreams")) {
839 		return handle_get_num_active_streams_use_output_hw(
840 				conn, message, arg);
841 	} else if (dbus_message_is_method_call(message,
842 					       CRAS_CONTROL_INTERFACE,
843 					       "SetGlobalOutputChannelRemix")) {
844 		return handle_set_global_output_channel_remix(
845 				conn, message, arg);
846 	} else if (dbus_message_is_method_call(message,
847 					       CRAS_CONTROL_INTERFACE,
848 					       "SetHotwordModel")) {
849 		return handle_set_hotword_model(conn, message, arg);
850 	}
851 
852 
853 	return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
854 }
855 
856 /* Creates a new DBus message, must be freed with dbus_message_unref. */
create_dbus_message(const char * name)857 static DBusMessage *create_dbus_message(const char *name)
858 {
859 	DBusMessage *msg;
860 	msg = dbus_message_new_signal(CRAS_ROOT_OBJECT_PATH,
861 				      CRAS_CONTROL_INTERFACE,
862 				      name);
863 	if (!msg)
864 		syslog(LOG_ERR, "Failed to create signal");
865 
866 	return msg;
867 }
868 
869 /* Handlers for system updates that generate DBus signals. */
870 
signal_output_volume(void * context,int32_t volume)871 static void signal_output_volume(void *context, int32_t volume)
872 {
873 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
874 	dbus_uint32_t serial = 0;
875 	DBusMessage *msg;
876 
877 	msg = create_dbus_message("OutputVolumeChanged");
878 	if (!msg)
879 		return;
880 
881 	volume = cras_system_get_volume();
882 	dbus_message_append_args(msg,
883 				 DBUS_TYPE_INT32, &volume,
884 				 DBUS_TYPE_INVALID);
885 	dbus_connection_send(control->conn, msg, &serial);
886 	dbus_message_unref(msg);
887 }
888 
signal_output_mute(void * context,int muted,int user_muted,int mute_locked)889 static void signal_output_mute(void *context, int muted, int user_muted,
890 			       int mute_locked)
891 {
892 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
893 	dbus_uint32_t serial = 0;
894 	DBusMessage *msg;
895 
896 	msg = create_dbus_message("OutputMuteChanged");
897 	if (!msg)
898 		return;
899 
900 	muted = cras_system_get_system_mute();
901 	user_muted = cras_system_get_user_mute();
902 	dbus_message_append_args(msg,
903 				 DBUS_TYPE_BOOLEAN, &muted,
904 				 DBUS_TYPE_BOOLEAN, &user_muted,
905 				 DBUS_TYPE_INVALID);
906 	dbus_connection_send(control->conn, msg, &serial);
907 	dbus_message_unref(msg);
908 }
909 
signal_capture_gain(void * context,int32_t gain)910 static void signal_capture_gain(void *context, int32_t gain)
911 {
912 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
913 	dbus_uint32_t serial = 0;
914 	DBusMessage *msg;
915 
916 	msg = create_dbus_message("InputGainChanged");
917 	if (!msg)
918 		return;
919 
920 	dbus_message_append_args(msg,
921 				 DBUS_TYPE_INT32, &gain,
922 				 DBUS_TYPE_INVALID);
923 	dbus_connection_send(control->conn, msg, &serial);
924 	dbus_message_unref(msg);
925 }
926 
signal_capture_mute(void * context,int muted,int mute_locked)927 static void signal_capture_mute(void *context, int muted, int mute_locked)
928 {
929 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
930 	dbus_uint32_t serial = 0;
931 	DBusMessage *msg;
932 
933 	msg = create_dbus_message("InputMuteChanged");
934 	if (!msg)
935 		return;
936 
937 	dbus_message_append_args(msg,
938 				 DBUS_TYPE_BOOLEAN, &muted,
939 				 DBUS_TYPE_INVALID);
940 	dbus_connection_send(control->conn, msg, &serial);
941 	dbus_message_unref(msg);
942 }
943 
signal_nodes_changed(void * context)944 static void signal_nodes_changed(void *context)
945 {
946 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
947 	dbus_uint32_t serial = 0;
948 	DBusMessage *msg;
949 
950 	msg = create_dbus_message("NodesChanged");
951 	if (!msg)
952 		return;
953 
954 	dbus_connection_send(control->conn, msg, &serial);
955 	dbus_message_unref(msg);
956 }
957 
signal_active_node_changed(void * context,enum CRAS_STREAM_DIRECTION dir,cras_node_id_t node_id)958 static void signal_active_node_changed(void *context,
959 				       enum CRAS_STREAM_DIRECTION dir,
960 				       cras_node_id_t node_id)
961 {
962 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
963 	DBusMessage *msg;
964 	dbus_uint32_t serial = 0;
965 
966 	msg = create_dbus_message((dir == CRAS_STREAM_OUTPUT)
967 			? "ActiveOutputNodeChanged"
968 			: "ActiveInputNodeChanged");
969 	if (!msg)
970 		return;
971 	dbus_message_append_args(msg,
972 				 DBUS_TYPE_UINT64, &node_id,
973 				 DBUS_TYPE_INVALID);
974 	dbus_connection_send(control->conn, msg, &serial);
975 	dbus_message_unref(msg);
976 }
977 
978 /* Called by iodev_list when a node volume changes. */
signal_node_volume_changed(void * context,cras_node_id_t node_id,int32_t volume)979 static void signal_node_volume_changed(void *context,
980 				       cras_node_id_t node_id,
981 				       int32_t volume)
982 {
983 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
984 	dbus_uint32_t serial = 0;
985 	DBusMessage *msg;
986 
987 	msg = create_dbus_message("OutputNodeVolumeChanged");
988 	if (!msg)
989 		return;
990 
991 	dbus_message_append_args(msg,
992 				 DBUS_TYPE_UINT64, &node_id,
993 				 DBUS_TYPE_INT32, &volume,
994 				 DBUS_TYPE_INVALID);
995 	dbus_connection_send(control->conn, msg, &serial);
996 	dbus_message_unref(msg);
997 }
998 
signal_node_capture_gain_changed(void * context,cras_node_id_t node_id,int capture_gain)999 static void signal_node_capture_gain_changed(void *context,
1000 					     cras_node_id_t node_id,
1001 					     int capture_gain)
1002 {
1003 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
1004 	dbus_uint32_t serial = 0;
1005 	DBusMessage *msg;
1006 
1007 	msg = create_dbus_message("InputNodeGainChanged");
1008 	if (!msg)
1009 		return;
1010 
1011 	dbus_message_append_args(msg,
1012 				 DBUS_TYPE_UINT64, &node_id,
1013 				 DBUS_TYPE_INT32, &capture_gain,
1014 				 DBUS_TYPE_INVALID);
1015 	dbus_connection_send(control->conn, msg, &serial);
1016 	dbus_message_unref(msg);
1017 }
1018 
signal_node_left_right_swapped_changed(void * context,cras_node_id_t node_id,int swapped)1019 static void signal_node_left_right_swapped_changed(void *context,
1020 						   cras_node_id_t node_id,
1021 						   int swapped)
1022 {
1023 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
1024 	dbus_uint32_t serial = 0;
1025 	DBusMessage *msg;
1026 
1027 	msg = create_dbus_message("NodeLeftRightSwappedChanged");
1028 	if (!msg)
1029 		return;
1030 
1031 	dbus_message_append_args(msg,
1032 				 DBUS_TYPE_UINT64, &node_id,
1033 				 DBUS_TYPE_BOOLEAN, &swapped,
1034 				 DBUS_TYPE_INVALID);
1035 	dbus_connection_send(control->conn, msg, &serial);
1036 	dbus_message_unref(msg);
1037 }
1038 
signal_num_active_streams_changed(void * context,enum CRAS_STREAM_DIRECTION dir,uint32_t num_active_streams)1039 static void signal_num_active_streams_changed(void *context,
1040 					      enum CRAS_STREAM_DIRECTION dir,
1041 					      uint32_t num_active_streams)
1042 {
1043 	struct cras_dbus_control *control = (struct cras_dbus_control *)context;
1044 	dbus_uint32_t serial = 0;
1045 	DBusMessage *msg;
1046 	dbus_int32_t num;
1047 
1048 	msg = create_dbus_message("NumberOfActiveStreamsChanged");
1049 	if (!msg)
1050 		return;
1051 
1052 	num = cras_system_state_get_active_streams();
1053 	dbus_message_append_args(msg,
1054 				 DBUS_TYPE_INT32, &num,
1055 				 DBUS_TYPE_INVALID);
1056 	dbus_connection_send(control->conn, msg, &serial);
1057 	dbus_message_unref(msg);
1058 }
1059 
1060 /* Exported Interface */
1061 
cras_dbus_control_start(DBusConnection * conn)1062 void cras_dbus_control_start(DBusConnection *conn)
1063 {
1064 	static const DBusObjectPathVTable control_vtable = {
1065 		.message_function = handle_control_message,
1066 	};
1067 
1068 	DBusError dbus_error;
1069 	struct cras_observer_ops observer_ops;
1070 
1071 	dbus_control.conn = conn;
1072 	dbus_connection_ref(dbus_control.conn);
1073 
1074 	if (!dbus_connection_register_object_path(conn,
1075 						  CRAS_ROOT_OBJECT_PATH,
1076 						  &control_vtable,
1077 						  &dbus_error)) {
1078 		syslog(LOG_WARNING,
1079 		       "Couldn't register CRAS control: %s: %s",
1080 		       CRAS_ROOT_OBJECT_PATH, dbus_error.message);
1081 		dbus_error_free(&dbus_error);
1082 		return;
1083 	}
1084 
1085 	memset(&observer_ops, 0, sizeof(observer_ops));
1086 	observer_ops.output_volume_changed = signal_output_volume;
1087 	observer_ops.output_mute_changed = signal_output_mute;
1088 	observer_ops.capture_gain_changed = signal_capture_gain;
1089 	observer_ops.capture_mute_changed = signal_capture_mute;
1090 	observer_ops.num_active_streams_changed =
1091 			signal_num_active_streams_changed;
1092 	observer_ops.nodes_changed = signal_nodes_changed;
1093 	observer_ops.active_node_changed = signal_active_node_changed;
1094 	observer_ops.input_node_gain_changed = signal_node_capture_gain_changed;
1095 	observer_ops.output_node_volume_changed = signal_node_volume_changed;
1096 	observer_ops.node_left_right_swapped_changed =
1097 			signal_node_left_right_swapped_changed;
1098 
1099 	dbus_control.observer = cras_observer_add(&observer_ops, &dbus_control);
1100 }
1101 
cras_dbus_control_stop()1102 void cras_dbus_control_stop()
1103 {
1104 	if (!dbus_control.conn)
1105 		return;
1106 
1107 	dbus_connection_unregister_object_path(dbus_control.conn,
1108 					       CRAS_ROOT_OBJECT_PATH);
1109 
1110 	dbus_connection_unref(dbus_control.conn);
1111 	dbus_control.conn = NULL;
1112 	cras_observer_remove(dbus_control.observer);
1113 	dbus_control.observer = NULL;
1114 }
1115