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