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