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