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