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