1 /* Copyright 2016 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 "cras_observer.h"
7 
8 #include "cras_alert.h"
9 #include "cras_iodev_list.h"
10 #include "cras_types.h"
11 #include "utlist.h"
12 
13 struct cras_observer_client {
14 	struct cras_observer_ops ops;
15 	void *context;
16 	struct cras_observer_client *next, *prev;
17 };
18 
19 struct cras_observer_alerts {
20 	struct cras_alert *output_volume;
21 	struct cras_alert *output_mute;
22 	struct cras_alert *capture_gain;
23 	struct cras_alert *capture_mute;
24 	struct cras_alert *nodes;
25 	struct cras_alert *active_node;
26 	struct cras_alert *output_node_volume;
27 	struct cras_alert *node_left_right_swapped;
28 	struct cras_alert *input_node_gain;
29 	struct cras_alert *suspend_changed;
30 	struct cras_alert *hotword_triggered;
31 	/* If all events for active streams went through a single alert then
32          * we might miss some because the alert code does not send every
33          * alert message. To ensure that the event sent contains the correct
34          * number of active streams per direction, make the alerts
35          * per-direciton. */
36 	struct cras_alert *num_active_streams[CRAS_NUM_DIRECTIONS];
37 	struct cras_alert *non_empty_audio_state_changed;
38 	struct cras_alert *bt_battery_changed;
39 	struct cras_alert *num_input_streams_with_permission;
40 };
41 
42 struct cras_observer_server {
43 	struct cras_observer_alerts alerts;
44 	struct cras_observer_client *clients;
45 };
46 
47 struct cras_observer_alert_data_volume {
48 	int32_t volume;
49 };
50 
51 struct cras_observer_alert_data_mute {
52 	int muted;
53 	int user_muted;
54 	int mute_locked;
55 };
56 
57 struct cras_observer_alert_data_active_node {
58 	enum CRAS_STREAM_DIRECTION direction;
59 	cras_node_id_t node_id;
60 };
61 
62 struct cras_observer_alert_data_node_volume {
63 	cras_node_id_t node_id;
64 	int32_t volume;
65 };
66 
67 struct cras_observer_alert_data_node_lr_swapped {
68 	cras_node_id_t node_id;
69 	int swapped;
70 };
71 
72 struct cras_observer_alert_data_suspend {
73 	int suspended;
74 };
75 
76 struct cras_observer_alert_data_streams {
77 	enum CRAS_STREAM_DIRECTION direction;
78 	uint32_t num_active_streams;
79 };
80 
81 struct cras_observer_alert_data_input_streams {
82 	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE];
83 };
84 
85 struct cras_observer_alert_data_hotword_triggered {
86 	int64_t tv_sec;
87 	int64_t tv_nsec;
88 };
89 
90 struct cras_observer_non_empty_audio_state {
91 	int non_empty;
92 };
93 
94 struct cras_observer_alert_data_bt_battery_changed {
95 	const char *address;
96 	uint32_t level;
97 };
98 
99 /* Global observer instance. */
100 static struct cras_observer_server *g_observer;
101 
102 /* Empty observer ops. */
103 static struct cras_observer_ops g_empty_ops;
104 
105 /*
106  * Alert handlers for delayed callbacks.
107  */
108 
output_volume_alert(void * arg,void * data)109 static void output_volume_alert(void *arg, void *data)
110 {
111 	struct cras_observer_client *client;
112 	struct cras_observer_alert_data_volume *volume_data =
113 		(struct cras_observer_alert_data_volume *)data;
114 
115 	DL_FOREACH (g_observer->clients, client) {
116 		if (client->ops.output_volume_changed)
117 			client->ops.output_volume_changed(client->context,
118 							  volume_data->volume);
119 	}
120 }
121 
output_mute_alert(void * arg,void * data)122 static void output_mute_alert(void *arg, void *data)
123 {
124 	struct cras_observer_client *client;
125 	struct cras_observer_alert_data_mute *mute_data =
126 		(struct cras_observer_alert_data_mute *)data;
127 
128 	DL_FOREACH (g_observer->clients, client) {
129 		if (client->ops.output_mute_changed)
130 			client->ops.output_mute_changed(client->context,
131 							mute_data->muted,
132 							mute_data->user_muted,
133 							mute_data->mute_locked);
134 	}
135 }
136 
capture_gain_alert(void * arg,void * data)137 static void capture_gain_alert(void *arg, void *data)
138 {
139 	struct cras_observer_client *client;
140 	struct cras_observer_alert_data_volume *volume_data =
141 		(struct cras_observer_alert_data_volume *)data;
142 
143 	DL_FOREACH (g_observer->clients, client) {
144 		if (client->ops.capture_gain_changed)
145 			client->ops.capture_gain_changed(client->context,
146 							 volume_data->volume);
147 	}
148 }
149 
capture_mute_alert(void * arg,void * data)150 static void capture_mute_alert(void *arg, void *data)
151 {
152 	struct cras_observer_client *client;
153 	struct cras_observer_alert_data_mute *mute_data =
154 		(struct cras_observer_alert_data_mute *)data;
155 
156 	DL_FOREACH (g_observer->clients, client) {
157 		if (client->ops.capture_mute_changed)
158 			client->ops.capture_mute_changed(
159 				client->context, mute_data->muted,
160 				mute_data->mute_locked);
161 	}
162 }
163 
nodes_prepare(struct cras_alert * alert)164 static void nodes_prepare(struct cras_alert *alert)
165 {
166 	cras_iodev_list_update_device_list();
167 }
168 
nodes_alert(void * arg,void * data)169 static void nodes_alert(void *arg, void *data)
170 {
171 	struct cras_observer_client *client;
172 
173 	DL_FOREACH (g_observer->clients, client) {
174 		if (client->ops.nodes_changed)
175 			client->ops.nodes_changed(client->context);
176 	}
177 }
178 
active_node_alert(void * arg,void * data)179 static void active_node_alert(void *arg, void *data)
180 {
181 	struct cras_observer_client *client;
182 	struct cras_observer_alert_data_active_node *node_data =
183 		(struct cras_observer_alert_data_active_node *)data;
184 
185 	DL_FOREACH (g_observer->clients, client) {
186 		if (client->ops.active_node_changed)
187 			client->ops.active_node_changed(client->context,
188 							node_data->direction,
189 							node_data->node_id);
190 	}
191 }
192 
output_node_volume_alert(void * arg,void * data)193 static void output_node_volume_alert(void *arg, void *data)
194 {
195 	struct cras_observer_client *client;
196 	struct cras_observer_alert_data_node_volume *node_data =
197 		(struct cras_observer_alert_data_node_volume *)data;
198 
199 	DL_FOREACH (g_observer->clients, client) {
200 		if (client->ops.output_node_volume_changed)
201 			client->ops.output_node_volume_changed(
202 				client->context, node_data->node_id,
203 				node_data->volume);
204 	}
205 }
206 
node_left_right_swapped_alert(void * arg,void * data)207 static void node_left_right_swapped_alert(void *arg, void *data)
208 {
209 	struct cras_observer_client *client;
210 	struct cras_observer_alert_data_node_lr_swapped *node_data =
211 		(struct cras_observer_alert_data_node_lr_swapped *)data;
212 
213 	DL_FOREACH (g_observer->clients, client) {
214 		if (client->ops.node_left_right_swapped_changed)
215 			client->ops.node_left_right_swapped_changed(
216 				client->context, node_data->node_id,
217 				node_data->swapped);
218 	}
219 }
220 
input_node_gain_alert(void * arg,void * data)221 static void input_node_gain_alert(void *arg, void *data)
222 {
223 	struct cras_observer_client *client;
224 	struct cras_observer_alert_data_node_volume *node_data =
225 		(struct cras_observer_alert_data_node_volume *)data;
226 
227 	DL_FOREACH (g_observer->clients, client) {
228 		if (client->ops.input_node_gain_changed)
229 			client->ops.input_node_gain_changed(client->context,
230 							    node_data->node_id,
231 							    node_data->volume);
232 	}
233 }
234 
suspend_changed_alert(void * arg,void * data)235 static void suspend_changed_alert(void *arg, void *data)
236 {
237 	struct cras_observer_client *client;
238 	struct cras_observer_alert_data_suspend *suspend_data =
239 		(struct cras_observer_alert_data_suspend *)data;
240 
241 	DL_FOREACH (g_observer->clients, client) {
242 		if (client->ops.suspend_changed)
243 			client->ops.suspend_changed(client->context,
244 						    suspend_data->suspended);
245 	}
246 }
247 
num_active_streams_alert(void * arg,void * data)248 static void num_active_streams_alert(void *arg, void *data)
249 {
250 	struct cras_observer_client *client;
251 	struct cras_observer_alert_data_streams *streams_data =
252 		(struct cras_observer_alert_data_streams *)data;
253 
254 	DL_FOREACH (g_observer->clients, client) {
255 		if (client->ops.num_active_streams_changed)
256 			client->ops.num_active_streams_changed(
257 				client->context, streams_data->direction,
258 				streams_data->num_active_streams);
259 	}
260 }
261 
num_input_streams_with_permission_alert(void * arg,void * data)262 static void num_input_streams_with_permission_alert(void *arg, void *data)
263 {
264 	struct cras_observer_client *client;
265 	struct cras_observer_alert_data_input_streams *input_streams_data =
266 		(struct cras_observer_alert_data_input_streams *)data;
267 
268 	DL_FOREACH (g_observer->clients, client) {
269 		if (client->ops.num_input_streams_with_permission_changed)
270 			client->ops.num_input_streams_with_permission_changed(
271 				client->context,
272 				input_streams_data->num_input_streams);
273 	}
274 }
275 
hotword_triggered_alert(void * arg,void * data)276 static void hotword_triggered_alert(void *arg, void *data)
277 {
278 	struct cras_observer_client *client;
279 	struct cras_observer_alert_data_hotword_triggered *triggered_data =
280 		(struct cras_observer_alert_data_hotword_triggered *)data;
281 
282 	DL_FOREACH (g_observer->clients, client) {
283 		if (client->ops.hotword_triggered)
284 			client->ops.hotword_triggered(client->context,
285 						      triggered_data->tv_sec,
286 						      triggered_data->tv_nsec);
287 	}
288 }
289 
non_empty_audio_state_changed_alert(void * arg,void * data)290 static void non_empty_audio_state_changed_alert(void *arg, void *data)
291 {
292 	struct cras_observer_client *client;
293 	struct cras_observer_non_empty_audio_state *non_empty_audio_data =
294 		(struct cras_observer_non_empty_audio_state *)data;
295 
296 	DL_FOREACH (g_observer->clients, client) {
297 		if (client->ops.non_empty_audio_state_changed) {
298 			client->ops.non_empty_audio_state_changed(
299 				client->context,
300 				non_empty_audio_data->non_empty);
301 		}
302 	}
303 }
304 
bt_battery_changed_alert(void * arg,void * data)305 static void bt_battery_changed_alert(void *arg, void *data)
306 {
307 	struct cras_observer_client *client;
308 	struct cras_observer_alert_data_bt_battery_changed *triggered_data =
309 		(struct cras_observer_alert_data_bt_battery_changed *)data;
310 
311 	DL_FOREACH (g_observer->clients, client) {
312 		if (client->ops.bt_battery_changed)
313 			client->ops.bt_battery_changed(client->context,
314 						       triggered_data->address,
315 						       triggered_data->level);
316 	}
317 }
318 
cras_observer_server_set_alert(struct cras_alert ** alert,cras_alert_cb cb,cras_alert_prepare prepare,unsigned int flags)319 static int cras_observer_server_set_alert(struct cras_alert **alert,
320 					  cras_alert_cb cb,
321 					  cras_alert_prepare prepare,
322 					  unsigned int flags)
323 {
324 	*alert = cras_alert_create(prepare, flags);
325 	if (!*alert)
326 		return -ENOMEM;
327 	return cras_alert_add_callback(*alert, cb, NULL);
328 }
329 
330 #define CRAS_OBSERVER_SET_ALERT(alert, prepare, flags)                         \
331 	do {                                                                   \
332 		rc = cras_observer_server_set_alert(&g_observer->alerts.alert, \
333 						    alert##_alert, prepare,    \
334 						    flags);                    \
335 		if (rc)                                                        \
336 			goto error;                                            \
337 	} while (0)
338 
339 #define CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(alert, direction)               \
340 	do {                                                                   \
341 		rc = cras_observer_server_set_alert(                           \
342 			&g_observer->alerts.alert[direction], alert##_alert,   \
343 			NULL, 0);                                              \
344 		if (rc)                                                        \
345 			goto error;                                            \
346 	} while (0)
347 
348 /*
349  * Public interface
350  */
351 
cras_observer_server_init()352 int cras_observer_server_init()
353 {
354 	int rc;
355 
356 	memset(&g_empty_ops, 0, sizeof(g_empty_ops));
357 	g_observer = (struct cras_observer_server *)calloc(
358 		1, sizeof(struct cras_observer_server));
359 	if (!g_observer)
360 		return -ENOMEM;
361 
362 	CRAS_OBSERVER_SET_ALERT(output_volume, NULL, 0);
363 	CRAS_OBSERVER_SET_ALERT(output_mute, NULL, 0);
364 	CRAS_OBSERVER_SET_ALERT(capture_gain, NULL, 0);
365 	CRAS_OBSERVER_SET_ALERT(capture_mute, NULL, 0);
366 	CRAS_OBSERVER_SET_ALERT(nodes, nodes_prepare, 0);
367 	CRAS_OBSERVER_SET_ALERT(active_node, nodes_prepare,
368 				CRAS_ALERT_FLAG_KEEP_ALL_DATA);
369 	CRAS_OBSERVER_SET_ALERT(output_node_volume, NULL, 0);
370 	CRAS_OBSERVER_SET_ALERT(node_left_right_swapped, NULL, 0);
371 	CRAS_OBSERVER_SET_ALERT(input_node_gain, NULL, 0);
372 	CRAS_OBSERVER_SET_ALERT(suspend_changed, NULL, 0);
373 	CRAS_OBSERVER_SET_ALERT(hotword_triggered, NULL, 0);
374 	CRAS_OBSERVER_SET_ALERT(non_empty_audio_state_changed, NULL, 0);
375 	CRAS_OBSERVER_SET_ALERT(bt_battery_changed, NULL, 0);
376 	CRAS_OBSERVER_SET_ALERT(num_input_streams_with_permission, NULL, 0);
377 
378 	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
379 					       CRAS_STREAM_OUTPUT);
380 	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
381 					       CRAS_STREAM_INPUT);
382 	CRAS_OBSERVER_SET_ALERT_WITH_DIRECTION(num_active_streams,
383 					       CRAS_STREAM_POST_MIX_PRE_DSP);
384 
385 	return 0;
386 
387 error:
388 	cras_observer_server_free();
389 	return rc;
390 }
391 
cras_observer_server_free()392 void cras_observer_server_free()
393 {
394 	if (!g_observer)
395 		return;
396 	cras_alert_destroy(g_observer->alerts.output_volume);
397 	cras_alert_destroy(g_observer->alerts.output_mute);
398 	cras_alert_destroy(g_observer->alerts.capture_gain);
399 	cras_alert_destroy(g_observer->alerts.capture_mute);
400 	cras_alert_destroy(g_observer->alerts.nodes);
401 	cras_alert_destroy(g_observer->alerts.active_node);
402 	cras_alert_destroy(g_observer->alerts.output_node_volume);
403 	cras_alert_destroy(g_observer->alerts.node_left_right_swapped);
404 	cras_alert_destroy(g_observer->alerts.input_node_gain);
405 	cras_alert_destroy(g_observer->alerts.suspend_changed);
406 	cras_alert_destroy(g_observer->alerts.hotword_triggered);
407 	cras_alert_destroy(g_observer->alerts.non_empty_audio_state_changed);
408 	cras_alert_destroy(g_observer->alerts.bt_battery_changed);
409 	cras_alert_destroy(
410 		g_observer->alerts.num_input_streams_with_permission);
411 	cras_alert_destroy(
412 		g_observer->alerts.num_active_streams[CRAS_STREAM_OUTPUT]);
413 	cras_alert_destroy(
414 		g_observer->alerts.num_active_streams[CRAS_STREAM_INPUT]);
415 	cras_alert_destroy(
416 		g_observer->alerts
417 			.num_active_streams[CRAS_STREAM_POST_MIX_PRE_DSP]);
418 	free(g_observer);
419 	g_observer = NULL;
420 }
421 
cras_observer_ops_are_empty(const struct cras_observer_ops * ops)422 int cras_observer_ops_are_empty(const struct cras_observer_ops *ops)
423 {
424 	return memcmp(ops, &g_empty_ops, sizeof(*ops)) == 0;
425 }
426 
cras_observer_get_ops(const struct cras_observer_client * client,struct cras_observer_ops * ops)427 void cras_observer_get_ops(const struct cras_observer_client *client,
428 			   struct cras_observer_ops *ops)
429 {
430 	if (!ops)
431 		return;
432 	if (!client)
433 		memset(ops, 0, sizeof(*ops));
434 	else
435 		memcpy(ops, &client->ops, sizeof(*ops));
436 }
437 
cras_observer_set_ops(struct cras_observer_client * client,const struct cras_observer_ops * ops)438 void cras_observer_set_ops(struct cras_observer_client *client,
439 			   const struct cras_observer_ops *ops)
440 {
441 	if (!client)
442 		return;
443 	if (!ops)
444 		memset(&client->ops, 0, sizeof(client->ops));
445 	else
446 		memcpy(&client->ops, ops, sizeof(client->ops));
447 }
448 
449 struct cras_observer_client *
cras_observer_add(const struct cras_observer_ops * ops,void * context)450 cras_observer_add(const struct cras_observer_ops *ops, void *context)
451 {
452 	struct cras_observer_client *client;
453 
454 	client = (struct cras_observer_client *)calloc(1, sizeof(*client));
455 	if (!client)
456 		return NULL;
457 	client->context = context;
458 	DL_APPEND(g_observer->clients, client);
459 	cras_observer_set_ops(client, ops);
460 	return client;
461 }
462 
cras_observer_remove(struct cras_observer_client * client)463 void cras_observer_remove(struct cras_observer_client *client)
464 {
465 	if (!client)
466 		return;
467 	DL_DELETE(g_observer->clients, client);
468 	free(client);
469 }
470 
471 /*
472  * Public interface for notifiers.
473  */
474 
cras_observer_notify_output_volume(int32_t volume)475 void cras_observer_notify_output_volume(int32_t volume)
476 {
477 	struct cras_observer_alert_data_volume data;
478 
479 	data.volume = volume;
480 	cras_alert_pending_data(g_observer->alerts.output_volume, &data,
481 				sizeof(data));
482 }
483 
cras_observer_notify_output_mute(int muted,int user_muted,int mute_locked)484 void cras_observer_notify_output_mute(int muted, int user_muted,
485 				      int mute_locked)
486 {
487 	struct cras_observer_alert_data_mute data;
488 
489 	data.muted = muted;
490 	data.user_muted = user_muted;
491 	data.mute_locked = mute_locked;
492 	cras_alert_pending_data(g_observer->alerts.output_mute, &data,
493 				sizeof(data));
494 }
495 
cras_observer_notify_capture_gain(int32_t gain)496 void cras_observer_notify_capture_gain(int32_t gain)
497 {
498 	struct cras_observer_alert_data_volume data;
499 
500 	data.volume = gain;
501 	cras_alert_pending_data(g_observer->alerts.capture_gain, &data,
502 				sizeof(data));
503 }
504 
cras_observer_notify_capture_mute(int muted,int mute_locked)505 void cras_observer_notify_capture_mute(int muted, int mute_locked)
506 {
507 	struct cras_observer_alert_data_mute data;
508 
509 	data.muted = muted;
510 	data.user_muted = 0;
511 	data.mute_locked = mute_locked;
512 	cras_alert_pending_data(g_observer->alerts.capture_mute, &data,
513 				sizeof(data));
514 }
515 
cras_observer_notify_nodes(void)516 void cras_observer_notify_nodes(void)
517 {
518 	cras_alert_pending(g_observer->alerts.nodes);
519 }
520 
cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION dir,cras_node_id_t node_id)521 void cras_observer_notify_active_node(enum CRAS_STREAM_DIRECTION dir,
522 				      cras_node_id_t node_id)
523 {
524 	struct cras_observer_alert_data_active_node data;
525 
526 	data.direction = dir;
527 	data.node_id = node_id;
528 	cras_alert_pending_data(g_observer->alerts.active_node, &data,
529 				sizeof(data));
530 }
531 
cras_observer_notify_output_node_volume(cras_node_id_t node_id,int32_t volume)532 void cras_observer_notify_output_node_volume(cras_node_id_t node_id,
533 					     int32_t volume)
534 {
535 	struct cras_observer_alert_data_node_volume data;
536 
537 	data.node_id = node_id;
538 	data.volume = volume;
539 	cras_alert_pending_data(g_observer->alerts.output_node_volume, &data,
540 				sizeof(data));
541 }
542 
cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id,int swapped)543 void cras_observer_notify_node_left_right_swapped(cras_node_id_t node_id,
544 						  int swapped)
545 {
546 	struct cras_observer_alert_data_node_lr_swapped data;
547 
548 	data.node_id = node_id;
549 	data.swapped = swapped;
550 	cras_alert_pending_data(g_observer->alerts.node_left_right_swapped,
551 				&data, sizeof(data));
552 }
553 
cras_observer_notify_input_node_gain(cras_node_id_t node_id,int32_t gain)554 void cras_observer_notify_input_node_gain(cras_node_id_t node_id, int32_t gain)
555 {
556 	struct cras_observer_alert_data_node_volume data;
557 
558 	data.node_id = node_id;
559 	data.volume = gain;
560 	cras_alert_pending_data(g_observer->alerts.input_node_gain, &data,
561 				sizeof(data));
562 }
563 
cras_observer_notify_suspend_changed(int suspended)564 void cras_observer_notify_suspend_changed(int suspended)
565 {
566 	struct cras_observer_alert_data_suspend data;
567 
568 	data.suspended = suspended;
569 	cras_alert_pending_data(g_observer->alerts.suspend_changed, &data,
570 				sizeof(data));
571 }
572 
cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,uint32_t num_active_streams)573 void cras_observer_notify_num_active_streams(enum CRAS_STREAM_DIRECTION dir,
574 					     uint32_t num_active_streams)
575 {
576 	struct cras_observer_alert_data_streams data;
577 	struct cras_alert *alert;
578 
579 	data.direction = dir;
580 	data.num_active_streams = num_active_streams;
581 	alert = g_observer->alerts.num_active_streams[dir];
582 	if (!alert)
583 		return;
584 
585 	cras_alert_pending_data(alert, &data, sizeof(data));
586 }
587 
cras_observer_notify_input_streams_with_permission(uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])588 void cras_observer_notify_input_streams_with_permission(
589 	uint32_t num_input_streams[CRAS_NUM_CLIENT_TYPE])
590 {
591 	struct cras_observer_alert_data_input_streams data;
592 	struct cras_alert *alert;
593 
594 	memcpy(&data.num_input_streams, num_input_streams,
595 	       sizeof(*num_input_streams) * CRAS_NUM_CLIENT_TYPE);
596 	alert = g_observer->alerts.num_input_streams_with_permission;
597 	if (!alert)
598 		return;
599 
600 	cras_alert_pending_data(alert, &data, sizeof(data));
601 }
602 
cras_observer_notify_hotword_triggered(int64_t tv_sec,int64_t tv_nsec)603 void cras_observer_notify_hotword_triggered(int64_t tv_sec, int64_t tv_nsec)
604 {
605 	struct cras_observer_alert_data_hotword_triggered data;
606 
607 	data.tv_sec = tv_sec;
608 	data.tv_nsec = tv_nsec;
609 	cras_alert_pending_data(g_observer->alerts.hotword_triggered, &data,
610 				sizeof(data));
611 }
612 
cras_observer_notify_non_empty_audio_state_changed(int non_empty)613 void cras_observer_notify_non_empty_audio_state_changed(int non_empty)
614 {
615 	struct cras_observer_non_empty_audio_state data;
616 
617 	data.non_empty = non_empty;
618 
619 	cras_alert_pending_data(
620 		g_observer->alerts.non_empty_audio_state_changed, &data,
621 		sizeof(data));
622 }
623 
cras_observer_notify_bt_battery_changed(const char * address,uint32_t level)624 void cras_observer_notify_bt_battery_changed(const char *address,
625 					     uint32_t level)
626 {
627 	struct cras_observer_alert_data_bt_battery_changed data;
628 
629 	data.address = address;
630 	data.level = level;
631 
632 	cras_alert_pending_data(g_observer->alerts.bt_battery_changed, &data,
633 				sizeof(data));
634 }
635