4 * Copyright (c) 2000 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
6 * Contact: Sangchul Lee <sc11.lee@samsung.com>
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
25 #include "include/mm_sound_mgr_focus.h"
26 #include "../include/mm_sound_common.h"
32 #include "include/mm_sound_mgr_focus_dbus.h"
33 #include "../include/mm_sound_utils.h"
36 #define CALLBACK_TIMEOUT 2500 /* millisecond */
38 static GList *g_focus_node_list = NULL;
39 static pthread_mutex_t g_focus_node_list_mutex = PTHREAD_MUTEX_INITIALIZER;
40 stream_list_t g_stream_list;
42 static const char* focus_status_str[] = {
54 char stream_type[MAX_STREAM_TYPE_LEN];
55 char ext_info[MM_SOUND_NAME_NUM];
59 #define CLEAR_DEAD_NODE_LIST(x) do { \
60 debug_warning("list = %p, node = %p, pid=[%d]", x, node, (node) ? node->pid : -1); \
61 if (x && node && (mm_sound_util_is_process_alive(node->pid) == FALSE)) { \
62 debug_warning("PID:%d does not exist now! remove from device cb list", node->pid); \
63 __clear_focus_pipe(node); \
64 x = g_list_remove(x, node); \
69 #define UPDATE_FOCUS_TAKEN_INFO(x_postfix, x_node, x_pid, x_hid) do { \
70 debug_msg("updating node[%p], taken_"#x_postfix"[%d] : pid = [%d], handle_id = [%d]", x_node, i, x_pid, x_hid); \
71 x_node->taken_##x_postfix[i].pid = x_pid; \
72 x_node->taken_##x_postfix[i].handle_id = x_hid; \
75 #define CONTINUE_IF_LIST_DATA_IS_NULL(x_node, x_list) \
76 if (!((x_node) = (focus_node_t *)(x_list)->data)) \
79 #define CONTINUE_IF_NOT_MY_FOCUS_NODE(x_node, x_param) \
80 if ((x_node)->is_for_watch || ((x_node)->pid != (x_param)->pid) || ((x_node)->handle_id != (x_param)->handle_id)) \
83 static char* __get_focus_pipe_path(int instance_id, int handle, const char *postfix, bool is_watch)
89 path = g_strdup_printf("/tmp/FOCUS.%d.%d.wch", instance_id, handle);
91 path = g_strdup_printf("/tmp/FOCUS.%d.%d", instance_id, handle);
94 path2 = g_strconcat(path, postfix, NULL);
103 static void __clear_focus_pipe(focus_node_t *node)
105 char *filename = NULL;
106 char *filename2 = NULL;
110 if (!node->is_for_watch) {
111 filename = __get_focus_pipe_path(node->pid, node->handle_id, NULL, false);
112 filename2 = __get_focus_pipe_path(node->pid, node->handle_id, "r", false);
114 filename = __get_focus_pipe_path(node->pid, node->handle_id, NULL, true);
115 filename2 = __get_focus_pipe_path(node->pid, node->handle_id, "r", true);
118 if (remove(filename))
119 debug_error("remove() failure, filename(%s), errno(%d)", filename, errno);
121 debug_log("removed file(%s)", filename);
125 if (remove(filename2))
126 debug_error("remove() failure, filename2(%s), errno(%d)", filename2, errno);
128 debug_log("removed file(%s)", filename2);
135 static void _clear_focus_node_list_func(focus_node_t *node, gpointer user_data)
137 CLEAR_DEAD_NODE_LIST(g_focus_node_list);
140 static int _mm_sound_mgr_focus_get_priority_from_stream_type(int *priority, const char *stream_type)
142 int ret = MM_ERROR_NONE;
147 if (priority == NULL || stream_type == NULL) {
148 ret = MM_ERROR_INVALID_ARGUMENT;
149 debug_error("invalid argument, priority[%p], stream_type[%s], ret[0x%x]", priority, stream_type, ret);
151 for (i = 0; i < AVAIL_STREAMS_MAX; i++) {
152 if (g_stream_list.stream_types[i] &&
153 !strncmp(g_stream_list.stream_types[i], stream_type, strlen(stream_type))) {
154 *priority = g_stream_list.priorities[i];
158 if (i == AVAIL_STREAMS_MAX) {
159 ret = MM_ERROR_NOT_SUPPORT_API;
160 debug_error("not supported stream_type[%s], ret[0x%x]", stream_type, ret);
162 debug_log("[%s] has priority of [%d]", stream_type, *priority);
170 static void _invoke_watch_callback(focus_node_t *node, const char *stream_type, focus_type_e focus_type, focus_command_e command, const _mm_sound_mgr_focus_param_t *param)
175 char *filename = NULL;
176 char *filename2 = NULL;
182 focus_cb_data cb_data;
184 if (!node || !stream_type) {
185 debug_error("[CB] invalid argument, node[%p], stream_type[%s]", node, stream_type);
189 memset(&cb_data, 0, sizeof(focus_cb_data));
190 cb_data.pid = node->pid;
191 cb_data.handle = node->handle_id;
192 cb_data.type = focus_type & node->status;
193 cb_data.state = (command == FOCUS_COMMAND_ACQUIRE) ? !FOCUS_STATUS_DEACTIVATED : FOCUS_STATUS_DEACTIVATED;
194 MMSOUND_STRNCPY(cb_data.stream_type, stream_type, MAX_STREAM_TYPE_LEN);
195 MMSOUND_STRNCPY(cb_data.ext_info, param->ext_info, MM_SOUND_NAME_NUM);
196 node->during_cb = true;
199 gettimeofday(&time, NULL);
200 starttime = time.tv_sec * 1000000 + time.tv_usec;
202 /**************************************
204 * Open callback cmd pipe
206 **************************************/
207 filename = __get_focus_pipe_path(cb_data.pid, cb_data.handle, NULL, true);
208 if (filename == NULL) {
209 debug_error("[CB] failed to get watch pipe");
212 if ((fd_FOCUS = open(filename, O_WRONLY|O_NONBLOCK)) == -1) {
214 strerror_r(errno, str_error, sizeof(str_error));
215 debug_error("[CB] failed to open watch pipe (%s, err:%s)", filename, str_error);
219 /******************************************
221 * Open callback result pipe
222 * before writing callback cmd to pipe
224 ******************************************/
225 filename2 = __get_focus_pipe_path(cb_data.pid, cb_data.handle, "r", true);
226 if (filename2 == NULL) {
227 debug_error("[RETCB] failed to get watch return pipe");
230 if ((fd_FOCUS_R = open(filename2, O_RDONLY | O_NONBLOCK)) == -1) {
232 strerror_r(errno, str_error, sizeof(str_error));
233 debug_error("[RETCB] failed to open watch return pipe (%s, err:%s)", filename2, str_error);
237 /*******************************************
239 *******************************************/
240 if (write(fd_FOCUS, &cb_data, sizeof(cb_data)) == -1) {
242 strerror_r(errno, str_error, sizeof(str_error));
243 debug_error("[CB] failed to write (err:%s)", str_error);
247 /*********************************************
249 * Wait callback result msg
251 ********************************************/
255 debug_msg("[RETCB] wait WATCH CALLBACK (client pid=%d, cmd=%d, timeout=%d(ms))", cb_data.pid, command, CALLBACK_TIMEOUT);
256 pret = poll(&pfd, 1, CALLBACK_TIMEOUT);
258 debug_error("[RETCB] poll failed (%d)", pret);
261 if (pfd.revents & POLLIN) {
262 if (read(fd_FOCUS_R, &ret, sizeof(ret)) == -1) {
264 strerror_r(errno, str_error, sizeof(str_error));
265 debug_error("[RETCB] failed to read (err:%s)", str_error);
270 /* Calculate endtime and display*/
271 gettimeofday(&time, NULL);
272 endtime = time.tv_sec * 1000000 + time.tv_usec;
273 debug_msg("[RETCB] WATCH CALLBACK returned (cbtimelab=%d(ms), client pid=%d, return handle=%d)", ((endtime-starttime)/1000), cb_data.pid, ret);
275 /**************************************
277 * Close callback result pipe
279 **************************************/
281 node->during_cb = false;
289 if (fd_FOCUS != -1) {
293 if (fd_FOCUS_R != -1) {
299 static int _mm_sound_mgr_focus_do_watch_callback(focus_type_e focus_type, focus_command_e command, focus_node_t *my_node, const _mm_sound_mgr_focus_param_t *param)
302 focus_node_t *node = NULL;
307 debug_error("[CB] my_node is null");
308 return MM_ERROR_INVALID_ARGUMENT;
311 for (list = g_focus_node_list; list != NULL; list = list->next) {
312 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
315 if (!node->is_for_watch || !(node->status & focus_type))
317 if (node->during_cb) {
318 debug_msg("it is about to invoke watch callback again during processing it, skip it");
322 _invoke_watch_callback(node, my_node->stream_type, focus_type, command, param);
327 return MM_ERROR_NONE;
330 int _mm_sound_mgr_focus_do_callback(focus_command_e command, focus_node_t *victim_node, const _mm_sound_mgr_focus_param_t *assaulter_param)
332 int res = MM_ERROR_NONE;
333 char *filename = NULL;
334 char *filename2 = NULL;
344 int flag_for_focus_type = 0;
345 int flag_for_taken_index = 0;
349 bool reacquisition_changed = false;
351 focus_cb_data cb_data;
353 debug_msg("for pid(%d) handle(%d)", victim_node->pid, victim_node->handle_id);
355 memset(&cb_data, 0, sizeof(focus_cb_data));
356 cb_data.pid = victim_node->pid;
357 cb_data.handle = victim_node->handle_id;
358 if (command == FOCUS_COMMAND_RELEASE) {
359 /* client will lost the acquired focus */
360 cb_data.type = assaulter_param->request_type & victim_node->status;
361 cb_data.state = FOCUS_STATUS_DEACTIVATED;
362 /* remove ext info. */
363 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
364 if (cb_data.type & (i+1)) {
365 memset(victim_node->ext_info[i], 0x0, MM_SOUND_NAME_NUM);
366 victim_node->option[i] = 0;
370 /* client will gain the lost focus */
371 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
372 if ((victim_node->taken_by_id[i].pid == assaulter_param->pid) &&
373 ((victim_node->taken_by_id[i].handle_id == assaulter_param->handle_id))) {
374 flag_for_focus_type |= i + 1; /* playback:1, capture:2 */
377 cb_data.type = flag_for_focus_type & assaulter_param->request_type;
378 cb_data.state = !FOCUS_STATUS_DEACTIVATED;
380 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
381 if (cb_data.type & (i+1)) {
382 MMSOUND_STRNCPY(victim_node->ext_info[i], assaulter_param->ext_info, MM_SOUND_NAME_NUM);
383 victim_node->option[i] = assaulter_param->option;
387 MMSOUND_STRNCPY(cb_data.stream_type, assaulter_param->stream_type, MAX_STREAM_TYPE_LEN);
388 MMSOUND_STRNCPY(cb_data.ext_info, assaulter_param->ext_info, MM_SOUND_NAME_NUM);
389 cb_data.option = assaulter_param->option;
392 gettimeofday(&time, NULL);
393 starttime = time.tv_sec * 1000000 + time.tv_usec;
395 /**************************************
397 * Open callback cmd pipe
399 **************************************/
400 filename = __get_focus_pipe_path(cb_data.pid, cb_data.handle, NULL, false);
401 if (filename == NULL) {
402 debug_error("[CB] failed to get pipe");
406 if ((fd_FOCUS = open(filename, O_WRONLY|O_NONBLOCK)) == -1) {
408 strerror_r(errno, str_error, sizeof(str_error));
409 debug_error("[CB] failed to open pipe (%s, err:%s)", filename, str_error);
414 /******************************************
416 * Open callback result pipe
417 * before writing callback cmd to pipe
419 ******************************************/
420 filename2 = __get_focus_pipe_path(cb_data.pid, cb_data.handle, "r", false);
421 if (filename2 == NULL) {
422 debug_error("[RETCB] failed to get return pipe");
426 if ((fd_FOCUS_R = open(filename2, O_RDONLY | O_NONBLOCK)) == -1) {
428 strerror_r(errno, str_error, sizeof(str_error));
429 debug_error("[RETCB] failed to open return pipe (%s, err:%s)", filename2, str_error);
434 /*******************************************
436 *******************************************/
437 if (write(fd_FOCUS, &cb_data, sizeof(cb_data)) == -1) {
439 strerror_r(errno, str_error, sizeof(str_error));
440 debug_error("[CB] failed to write (err:%s)", str_error);
445 /*********************************************
447 * Wait callback result msg
449 ********************************************/
453 debug_msg("[RETCB] wait CALLBACK (client pid=%d, handle=%d, cmd=%d, timeout=%d(ms))",
454 cb_data.pid, cb_data.handle, command, CALLBACK_TIMEOUT);
455 pret = poll(&pfd, 1, CALLBACK_TIMEOUT);
457 debug_error("[RETCB] poll failed (%d)", pret);
461 if (pfd.revents & POLLIN) {
462 if (read(fd_FOCUS_R, &ret, sizeof(ret)) == -1) {
464 strerror_r(errno, str_error, sizeof(str_error));
465 debug_error("[RETCB] failed to read (err:%s)", str_error);
469 /* ret contains data as below,
470 * |<--12bits--><--4bits (reacquisition)--><--16bits (handle)-->| */
471 ret_handle = (int)(ret & 0x0000ffff);
472 if (victim_node->reacquisition != (bool)((ret >> 16) & 0xf)) {
473 reacquisition_changed = true;
474 victim_node->reacquisition = (bool)((ret >> 16) & 0xf);
475 debug_msg("[RETCB] victim's reacquisition is changed to (%d)", victim_node->reacquisition);
479 /* Calculate endtime and display*/
480 gettimeofday(&time, NULL);
481 endtime = time.tv_sec * 1000000 + time.tv_usec;
482 debug_msg("[RETCB] CALLBACK returned (cbtimelab=%d(ms), client pid=%d, returned handle=%d)", ((endtime-starttime)/1000), cb_data.pid, ret_handle);
484 /* update victim node */
485 if (command == FOCUS_COMMAND_RELEASE) {
486 taken_pid = assaulter_param->pid;
487 taken_hid = assaulter_param->handle_id;
488 flag_for_taken_index = assaulter_param->request_type & victim_node->status;
492 flag_for_taken_index = assaulter_param->request_type;
495 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
496 if (flag_for_taken_index & (i+1)) {
498 focus_node_t *node = NULL;
500 if (command == FOCUS_COMMAND_ACQUIRE && (victim_node->taken_by_id[i].pid != assaulter_param->pid || (victim_node->taken_by_id[i].handle_id != assaulter_param->handle_id))) {
502 debug_error("skip updating victim node");
505 if (reacquisition_changed) {
506 if (!victim_node->reacquisition) {
507 for (list = g_focus_node_list; list != NULL; list = list->next) {
508 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
509 if (node->taken_by_id[i].pid == victim_node->pid) {
510 UPDATE_FOCUS_TAKEN_INFO(backup, node, node->taken_by_id[i].pid, node->taken_by_id[i].handle_id);
511 UPDATE_FOCUS_TAKEN_INFO(by_id, node, taken_pid, taken_hid);
512 } else if (!list->next) {
513 UPDATE_FOCUS_TAKEN_INFO(backup, victim_node, taken_pid, taken_hid);
514 UPDATE_FOCUS_TAKEN_INFO(by_id, victim_node, 0, 0);
518 UPDATE_FOCUS_TAKEN_INFO(by_id, victim_node, taken_pid, taken_hid);
519 UPDATE_FOCUS_TAKEN_INFO(backup, victim_node, 0, 0);
522 if (victim_node->reacquisition)
523 UPDATE_FOCUS_TAKEN_INFO(by_id, victim_node, taken_pid, taken_hid);
525 UPDATE_FOCUS_TAKEN_INFO(backup, victim_node, taken_pid, taken_hid);
530 if (command == FOCUS_COMMAND_RELEASE)
531 victim_node->status = victim_node->status & ~(cb_data.type);
532 else if (command == FOCUS_COMMAND_ACQUIRE)
533 victim_node->status = victim_node->status | cb_data.type;
535 if (strncmp(assaulter_param->stream_type, victim_node->stream_type, MAX_STREAM_TYPE_LEN))
536 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)assaulter_param->request_type, command, victim_node, assaulter_param);
545 if (fd_FOCUS != -1) {
549 if (fd_FOCUS_R != -1) {
557 static int _mm_sound_mgr_focus_list_dump()
559 int ret = MM_ERROR_NONE;
561 focus_node_t *node = NULL;
563 debug_msg("================================================ focus node list : start ===================================================");
564 for (list = g_focus_node_list; list != NULL; list = list->next) {
565 if ((node = (focus_node_t *)list->data) && !node->is_for_watch) {
566 debug_msg("*** pid[%5d]/handle_id[%2d]/[%14s]:priority[%2d],status[%s],taken_by[P(%5d/%2d)C(%5d/%2d)],option[0x%x/0x%x],ext_info[%s/%s]",
567 node->pid, node->handle_id, node->stream_type, node->priority, focus_status_str[node->status],
568 node->taken_by_id[0].pid, node->taken_by_id[0].handle_id,
569 node->taken_by_id[1].pid, node->taken_by_id[1].handle_id,
570 node->option[0], node->option[1],
571 node->ext_info[0], node->ext_info[1]);
574 debug_msg("================================================ focus node list : end =====================================================");
579 static int _mm_sound_mgr_focus_watch_list_dump()
581 int ret = MM_ERROR_NONE;
583 focus_node_t *node = NULL;
585 debug_msg("============================================= focus watch node list : start =================================================");
586 for (list = g_focus_node_list; list != NULL; list = list->next) {
587 if ((node = (focus_node_t *)list->data) && node->is_for_watch)
588 debug_msg("*** pid[%5d]/handle_id[%d]/watch on focus status[%s]",
589 node->pid, node->handle_id, focus_status_str[node->status]);
591 debug_msg("============================================= focus watch node list : end ===================================================");
596 static void _mm_sound_mgr_focus_fill_info_from_msg(focus_node_t *node, const _mm_sound_mgr_focus_param_t *msg)
599 node->pid = msg->pid;
600 node->handle_id = msg->handle_id;
601 node->callback = msg->callback;
602 node->cbdata = msg->cbdata;
608 int mm_sound_mgr_focus_create_node(const _mm_sound_mgr_focus_param_t *param)
610 int ret = MM_ERROR_NONE;
612 focus_node_t *node = NULL;
617 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
619 /* Update list for dead process */
620 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
622 for (list = g_focus_node_list; list != NULL; list = list->next) {
623 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
624 CONTINUE_IF_NOT_MY_FOCUS_NODE(node, param);
626 debug_error("the node of pid[%d]/handle_id[%d] is already created", param->pid, param->handle_id);
627 ret = MM_ERROR_INVALID_ARGUMENT;
631 /* get priority from stream type */
632 ret = _mm_sound_mgr_focus_get_priority_from_stream_type(&priority, param->stream_type);
636 node = g_malloc0(sizeof(focus_node_t));
638 /* fill up information to the node */
639 _mm_sound_mgr_focus_fill_info_from_msg(node, param);
640 node->priority = priority;
641 node->status = FOCUS_STATUS_DEACTIVATED;
642 node->reacquisition = true;
643 MMSOUND_STRNCPY(node->stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
645 g_focus_node_list = g_list_append(g_focus_node_list, node);
646 if (g_focus_node_list) {
647 debug_log("new focus node is added");
649 debug_error("g_list_append failed");
650 ret = MM_ERROR_SOUND_INTERNAL;
654 _mm_sound_mgr_focus_list_dump();
656 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
662 int mm_sound_mgr_focus_destroy_node(const _mm_sound_mgr_focus_param_t *param)
664 int ret = MM_ERROR_NONE;
666 focus_node_t *node = NULL;
667 focus_node_t *my_node = NULL;
668 bool need_to_trigger = true;
673 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
675 /* Update list for dead process */
676 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
678 for (list = g_focus_node_list; list != NULL; list = list->next) {
679 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
680 CONTINUE_IF_NOT_MY_FOCUS_NODE(node, param);
682 debug_log("found the node of pid[%d]/handle_id[%d]", param->pid, param->handle_id);
686 if (my_node == NULL) {
687 debug_error("could not find any node of pid[%d]/handle_id[%d]", param->pid, param->handle_id);
688 ret = MM_ERROR_INVALID_ARGUMENT;
692 if (need_to_trigger) {
693 bool need_to_trigger_watch_cb = true;
694 _mm_sound_mgr_focus_param_t *new_param = NULL;
696 if (!(new_param = g_malloc0(sizeof(_mm_sound_mgr_focus_param_t)))) {
697 debug_error("Fail to g_malloc0 for new_param");
700 new_param->pid = param->pid;
701 new_param->handle_id = param->handle_id;
702 new_param->request_type = my_node->status;
703 new_param->option = my_node->option[i];
704 MMSOUND_STRNCPY(new_param->stream_type, my_node->stream_type, MAX_STREAM_TYPE_LEN);
705 MMSOUND_STRNCPY(new_param->ext_info, my_node->ext_info[i], MM_SOUND_NAME_NUM);
707 for (list = g_focus_node_list; list != NULL; list = list->next) {
708 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
709 if (my_node == node || node->is_for_watch)
711 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
712 if (node->taken_by_id[i].pid != param->pid)
715 if (my_node->taken_by_id[i].pid) {
716 /* If exists update the taken focus info to my victim node */
717 if (node->taken_by_id[i].handle_id == param->handle_id)
718 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id);
719 } else if (my_node->status & (i+1)) {
720 if (node->taken_by_id[i].handle_id == new_param->handle_id) {
721 /* do callback for resumption */
722 if ((ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_ACQUIRE, node, new_param)))
723 debug_error("Fail to _focus_do_callback for COMMAND ACQUIRE to node[%p], ret[0x%x]", node, ret);
724 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN)) {
725 need_to_trigger_watch_cb = false;
726 my_node->status &= ~(new_param->request_type);
732 if (need_to_trigger_watch_cb)
733 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)new_param->request_type, FOCUS_COMMAND_RELEASE, my_node, new_param);
739 /* Destroy my node */
740 __clear_focus_pipe(my_node);
741 g_focus_node_list = g_list_remove(g_focus_node_list, my_node);
744 _mm_sound_mgr_focus_list_dump();
745 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
751 int mm_sound_mgr_focus_set_reacquisition(const _mm_sound_mgr_focus_param_t *param)
753 int ret = MM_ERROR_NONE;
755 focus_node_t *node = NULL;
756 focus_node_t *my_node = NULL;
761 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
763 /* Update list for dead process */
764 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
766 /* Find node to set reacquisition */
767 for (list = g_focus_node_list; list != NULL; list = list->next) {
768 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
769 CONTINUE_IF_NOT_MY_FOCUS_NODE(node, param);
771 if (node->reacquisition == param->reacquisition) {
772 debug_msg("it is already set as same value of reacquisition(%d)", param->reacquisition);
775 node->reacquisition = param->reacquisition;
776 debug_msg("found a node(pid[%d]/handle_id[%d]) to set reacquisition to (%d)", node->pid, node->handle_id, param->reacquisition);
780 if (my_node == NULL) {
781 debug_error("could not find any node of pid[%d]/handle_id[%d]", param->pid, param->handle_id);
782 ret = MM_ERROR_INVALID_ARGUMENT;
786 if (!param->reacquisition) {
787 for (list = g_focus_node_list; list != NULL; list = list->next) {
788 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
789 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
790 if (node->taken_by_id[i].pid == param->pid) {
791 /* victim node : append my node's taken info to my victim node */
792 if (my_node->taken_by_id[i].pid != 0) {
793 UPDATE_FOCUS_TAKEN_INFO(backup, node, node->taken_by_id[i].pid, node->taken_by_id[i].handle_id);
794 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id);
796 } else if (!list->next) {
797 /* my node : backup and reset */
798 UPDATE_FOCUS_TAKEN_INFO(backup, my_node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id);
799 UPDATE_FOCUS_TAKEN_INFO(by_id, my_node, 0, 0);
804 for (list = g_focus_node_list; list != NULL; list = list->next) {
805 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
806 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
807 /* rollback and reset backup info. */
808 if (node->taken_by_id[i].pid && (node->taken_by_id[i].pid == my_node->taken_backup[i].pid)) {
809 UPDATE_FOCUS_TAKEN_INFO(by_id, node, node->taken_backup[i].pid, node->taken_backup[i].handle_id);
810 UPDATE_FOCUS_TAKEN_INFO(backup, node, 0, 0);
811 } else if (!list->next) {
812 UPDATE_FOCUS_TAKEN_INFO(by_id, my_node, my_node->taken_backup[i].pid, my_node->taken_backup[i].handle_id);
813 UPDATE_FOCUS_TAKEN_INFO(backup, my_node, 0, 0);
820 _mm_sound_mgr_focus_list_dump();
821 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
827 int mm_sound_mgr_focus_get_stream_type_of_acquired_focus(focus_type_e focus_type, char **stream_type, int *option, char **ext_info)
829 int ret = MM_ERROR_SOUND_NO_DATA;
831 focus_node_t *node = NULL;
835 if (!stream_type || !option)
836 return MM_ERROR_INVALID_ARGUMENT;
838 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
840 /* Update list for dead process */
841 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
843 /* Find node to set reacquisition */
844 for (list = g_focus_node_list; list != NULL; list = list->next) {
845 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
846 if (!node->is_for_watch && (node->status & focus_type)) {
847 int index = focus_type - 1;
849 debug_msg("found a node : request_focus_type(%d), stream_type(%s)/ext info(%s) of acquired focus",
850 focus_type, node->stream_type, node->ext_info[index]);
852 *stream_type = node->stream_type;
853 *option = node->option[index];
855 *ext_info = node->ext_info[index];
861 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
868 static void update_reacquisition_with_released_state(focus_node_t *node, int direction)
871 debug_error("node is null");
874 if (direction >= NUM_OF_STREAM_IO_TYPE) {
875 debug_error("invalid direction(%d)", direction);
880 int mm_sound_mgr_focus_request_acquire(const _mm_sound_mgr_focus_param_t *param)
882 int ret = MM_ERROR_NONE;
884 focus_node_t *node = NULL;
885 focus_node_t *my_node = NULL;
886 bool need_to_trigger_cb = false;
887 bool need_to_trigger_watch_cb = true;
892 if (!param->is_in_thread)
893 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
895 /* Update list for dead process */
896 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
898 for (list = g_focus_node_list; list != NULL; list = list->next) {
899 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
900 CONTINUE_IF_NOT_MY_FOCUS_NODE(node, param);
903 if ((my_node->status > FOCUS_STATUS_DEACTIVATED) && (my_node->status & param->request_type)) {
904 debug_error("focus status is already activated");
905 ret = MM_ERROR_SOUND_INVALID_STATE;
910 if (my_node == NULL) {
911 debug_error("node is null");
912 ret = MM_ERROR_INVALID_ARGUMENT;
916 /* check if the priority of any node is higher than its based on io direction */
917 for (list = g_focus_node_list; list != NULL; list = list->next) {
918 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
919 if (my_node == node || node->is_for_watch)
921 if (param->request_type == FOCUS_TYPE_BOTH || node->status == FOCUS_STATUS_ACTIVATED_BOTH ||
922 (node->status & param->request_type)) {
923 if (node->status <= FOCUS_STATUS_DEACTIVATED)
926 if ((my_node->priority < node->priority)) {
927 ret = MM_ERROR_POLICY_BLOCKED;
928 need_to_trigger_cb = false;
931 need_to_trigger_cb = true;
936 if (need_to_trigger_cb) {
937 _mm_sound_mgr_focus_param_t *param_s = (_mm_sound_mgr_focus_param_t *)param;
938 MMSOUND_STRNCPY(param_s->stream_type, my_node->stream_type, MAX_STREAM_TYPE_LEN);
939 for (list = g_focus_node_list; list != NULL; list = list->next) {
940 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
941 if (node == my_node || node->is_for_watch)
943 if (param_s->request_type == FOCUS_TYPE_BOTH || node->status == FOCUS_STATUS_ACTIVATED_BOTH ||
944 (node->status & param_s->request_type)) {
945 if (node->status <= FOCUS_STATUS_DEACTIVATED)
947 if (my_node->priority >= node->priority) {
948 /* do callback for interruption */
949 if ((ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_RELEASE, node, param_s))) {
950 debug_error("Fail to _focus_do_callback for COMMAND RELEASE to node[%p], ret[0x%x]", node, ret);
951 /* but, keep going */
954 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN))
955 need_to_trigger_watch_cb = false;
961 if (ret != MM_ERROR_POLICY_BLOCKED) {
963 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
964 if (param->request_type & (i+1)) {
965 MMSOUND_STRNCPY(my_node->ext_info[i], param->ext_info, MM_SOUND_NAME_NUM);
966 my_node->option[i] = param->option;
967 update_reacquisition_with_released_state(my_node, i);
971 my_node->status |= param->request_type;
972 /* do watch callback due to the status of mine */
973 if (need_to_trigger_watch_cb)
974 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)param->request_type, FOCUS_COMMAND_ACQUIRE, my_node, param);
976 /* update taken information */
977 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
978 if (param->request_type & (i+1)) {
979 UPDATE_FOCUS_TAKEN_INFO(by_id, my_node, 0, 0);
980 UPDATE_FOCUS_TAKEN_INFO(backup, my_node, 0, 0);
985 _mm_sound_mgr_focus_list_dump();
986 _mm_sound_mgr_focus_watch_list_dump();
988 if (!param->is_in_thread)
989 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
995 int mm_sound_mgr_focus_request_release(const _mm_sound_mgr_focus_param_t *param)
997 int ret = MM_ERROR_NONE;
999 focus_node_t *node = NULL;
1000 focus_node_t *my_node = NULL;
1001 bool need_to_trigger_watch_cb = true;
1002 bool need_to_trigger_cb = true;
1007 if (!param->is_in_thread)
1008 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1010 /* Update list for dead process */
1011 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1013 for (list = g_focus_node_list; list != NULL; list = list->next) {
1014 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
1015 CONTINUE_IF_NOT_MY_FOCUS_NODE(node, param);
1018 if (my_node->status == FOCUS_STATUS_DEACTIVATED) {
1019 debug_error("focus status is already deactivated");
1020 ret = MM_ERROR_SOUND_INVALID_STATE;
1022 } else if ((my_node->status != FOCUS_STATUS_ACTIVATED_BOTH) && (my_node->status != (focus_status_e)param->request_type)) {
1023 debug_error("request type is not matched with current focus type");
1024 ret = MM_ERROR_SOUND_INVALID_STATE;
1030 if (my_node == NULL) {
1031 debug_error("node is null");
1032 ret = MM_ERROR_INVALID_ARGUMENT;
1036 if (need_to_trigger_cb) {
1037 _mm_sound_mgr_focus_param_t *param_s = (_mm_sound_mgr_focus_param_t *)param;
1038 MMSOUND_STRNCPY(param_s->stream_type, my_node->stream_type, MAX_STREAM_TYPE_LEN);
1039 for (list = g_focus_node_list; list != NULL; list = list->next) {
1040 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
1041 if (node == my_node || node->is_for_watch)
1043 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1044 if (param_s->request_type & (i+1)) {
1045 if (node->taken_by_id[i].pid == param_s->pid &&
1046 node->taken_by_id[i].handle_id == param_s->handle_id) {
1047 /* do callback for resumption */
1048 if ((ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_ACQUIRE, node, param_s)))
1049 debug_error("Fail to _focus_do_callback for COMMAND ACQUIRE to node[%p], ret[0x%x]", node, ret);
1050 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN))
1051 need_to_trigger_watch_cb = false;
1058 my_node->status &= ~(param->request_type);
1059 /* remove ext info. */
1060 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1061 if (!(my_node->status & (i+1))) {
1062 memset(my_node->ext_info[i], 0x0, MM_SOUND_NAME_NUM);
1063 my_node->option[i] = 0;
1066 /* do watch callback due to the status of mine */
1067 if (need_to_trigger_watch_cb)
1068 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)param->request_type, FOCUS_COMMAND_RELEASE, my_node, param);
1070 _mm_sound_mgr_focus_list_dump();
1071 _mm_sound_mgr_focus_watch_list_dump();
1073 if (!param->is_in_thread)
1074 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1080 int mm_sound_mgr_focus_set_watch_cb(const _mm_sound_mgr_focus_param_t *param)
1082 int ret = MM_ERROR_NONE;
1084 focus_node_t *node = NULL;
1088 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1090 /* Update list for dead process */
1091 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1093 for (list = g_focus_node_list; list != NULL; list = list->next) {
1094 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
1095 if ((node->pid == param->pid) && (node->handle_id == param->handle_id) && node->is_for_watch) {
1096 debug_error("the node of pid[%d]/handle_id[%d] for watch focus is already created", param->pid, param->handle_id);
1097 ret = MM_ERROR_INVALID_ARGUMENT;
1102 node = g_malloc0(sizeof(focus_node_t));
1104 /* fill up information to the node */
1105 _mm_sound_mgr_focus_fill_info_from_msg(node, param);
1106 node->is_for_watch = true;
1107 node->status = param->request_type;
1109 g_focus_node_list = g_list_append(g_focus_node_list, node);
1110 if (g_focus_node_list) {
1111 debug_log("new focus node is added");
1113 debug_error("g_list_append failed");
1114 ret = MM_ERROR_SOUND_INTERNAL;
1118 _mm_sound_mgr_focus_watch_list_dump();
1120 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1126 int mm_sound_mgr_focus_unset_watch_cb(const _mm_sound_mgr_focus_param_t *param)
1128 int ret = MM_ERROR_SOUND_INTERNAL;
1130 focus_node_t *node = NULL;
1134 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1136 /* Update list for dead process */
1137 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1139 for (list = g_focus_node_list; list != NULL; list = list->next) {
1140 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
1141 if ((node->pid == param->pid) && (node->handle_id == param->handle_id) && node->is_for_watch) {
1142 debug_log("found the node of pid[%d]/handle_id[%d] for watch focus", param->pid, param->handle_id);
1143 __clear_focus_pipe(node);
1144 g_focus_node_list = g_list_remove(g_focus_node_list, node);
1146 ret = MM_ERROR_NONE;
1151 debug_error("could not find any node of pid[%d]/handle_id[%d] for watch focus", param->pid, param->handle_id);
1152 ret = MM_ERROR_INVALID_ARGUMENT;
1156 _mm_sound_mgr_focus_watch_list_dump();
1158 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1164 int mm_sound_mgr_focus_deliver(const _mm_sound_mgr_focus_param_t *param)
1166 int ret = MM_ERROR_NONE;
1168 focus_node_t *src_node = NULL;
1169 focus_node_t *dst_node = NULL;
1174 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1176 /* Update list for dead process */
1177 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1179 for (list = g_focus_node_list; list != NULL; list = list->next) {
1180 CONTINUE_IF_LIST_DATA_IS_NULL(src_node, list);
1181 if ((src_node->pid == param->pid) && (src_node->handle_id == param->handle_id)) {
1182 debug_log("SRC: found the node of pid[%d]/handle_id[%d] for watch focus",
1183 param->pid, param->handle_id);
1187 if (src_node == NULL) {
1188 debug_error("could not find the source node of param, pid[%d]/handle_id[%d]",
1189 param->pid, param->handle_id);
1190 ret = MM_ERROR_INVALID_ARGUMENT;
1194 if (!(src_node->status & param->request_type)) {
1195 debug_error("invalid request type(0x%x), src_node->status(0x%x)",
1196 param->request_type, src_node->status);
1197 ret = MM_ERROR_SOUND_INVALID_STATE;
1201 for (list = g_focus_node_list; list != NULL; list = list->next) {
1202 CONTINUE_IF_LIST_DATA_IS_NULL(dst_node, list);
1203 if ((dst_node->pid == param->pid) && (dst_node->handle_id == param->handle_id_dst)) {
1204 debug_log("DST: found the destination node of param, pid[%d]/handle_id[%d]",
1205 param->pid, param->handle_id_dst);
1209 if (dst_node == NULL) {
1210 debug_error("could not find the destination node of param, pid[%d]/handle_id[%d]",
1211 param->pid, param->handle_id_dst);
1212 ret = MM_ERROR_INVALID_ARGUMENT;
1216 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1217 focus_node_t *node = NULL;
1218 if (!(param->request_type & (i+1)))
1221 for (list = g_focus_node_list; list != NULL; list = list->next) {
1222 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
1223 if ((node->taken_by_id[i].pid == src_node->pid) &&
1224 (node->taken_by_id[i].handle_id == src_node->handle_id)) {
1225 debug_log("found the node pid[%d]/handle_id[%d] that focus was taken by this src node, update it",
1226 node->pid, node->handle_id);
1227 UPDATE_FOCUS_TAKEN_INFO(by_id, node, dst_node->pid, dst_node->handle_id);
1229 if ((node->taken_backup[i].pid == src_node->pid) &&
1230 (node->taken_backup[i].handle_id == src_node->handle_id)) {
1231 debug_log("found the node pid[%d]/handle_id[%d] that focus was taken by this src node and set reacquisition to false, update it",
1232 node->pid, node->handle_id);
1233 UPDATE_FOCUS_TAKEN_INFO(backup, node, dst_node->pid, dst_node->handle_id);
1237 dst_node->status |= (i+1);
1238 UPDATE_FOCUS_TAKEN_INFO(by_id, dst_node, 0, 0);
1239 UPDATE_FOCUS_TAKEN_INFO(backup, dst_node, 0, 0);
1240 MMSOUND_STRNCPY(dst_node->ext_info[i], src_node->ext_info[i], MM_SOUND_NAME_NUM);
1241 dst_node->option[i] = src_node->option[i];
1243 src_node->status &= ~(i+1);
1244 memset(src_node->ext_info[i], 0x0, MM_SOUND_NAME_NUM);
1245 src_node->option[i] = 0;
1248 _mm_sound_mgr_focus_list_dump();
1251 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1257 int mm_sound_mgr_focus_emergent_exit(const _mm_sound_mgr_focus_param_t *param)
1259 int ret = MM_ERROR_NONE;
1261 GList *list_s = NULL;
1262 focus_node_t *node = NULL;
1263 focus_node_t *my_node = NULL;
1268 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1270 /* Update list for dead process */
1271 g_list_foreach(g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1273 list = g_focus_node_list;
1275 CONTINUE_IF_LIST_DATA_IS_NULL(node, list);
1276 if (node->pid != param->pid) {
1278 debug_log("node not found, next list = %p", list);
1281 if (node->is_for_watch) {
1282 debug_log("clearing watch cb of pid(%d) handle(%d)", node->pid, node->handle_id);
1283 __clear_focus_pipe(node);
1284 g_focus_node_list = g_list_remove(g_focus_node_list, node);
1285 list = g_focus_node_list;
1289 if (node->status == FOCUS_STATUS_DEACTIVATED) {
1290 debug_log("clearing deactivated focus node of pid(%d) hande(%d)", node->pid, node->handle_id);
1292 /* update info of nodes that are lost their focus by the process exited */
1293 for (list_s = g_focus_node_list; list_s != NULL; list_s = list_s->next) {
1294 CONTINUE_IF_LIST_DATA_IS_NULL(node, list_s);
1295 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1296 if (node->taken_by_id[i].pid == param->pid) {
1297 if (my_node->taken_by_id[i].pid)
1298 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id);
1300 UPDATE_FOCUS_TAKEN_INFO(by_id, node, 0, 0);
1304 __clear_focus_pipe(my_node);
1305 g_focus_node_list = g_list_remove(g_focus_node_list, my_node);
1306 list = g_focus_node_list;
1308 } else { /* node that acquired focus */
1309 bool need_to_trigger_watch_cb = true;
1310 _mm_sound_mgr_focus_param_t param_s;
1311 debug_log("clearing activated focus node of pid(%d) handle(%d)", node->pid, node->handle_id);
1314 memset(¶m_s, 0x00, sizeof(_mm_sound_mgr_focus_param_t));
1315 param_s.pid = my_node->pid;
1316 param_s.handle_id = my_node->handle_id;
1317 param_s.request_type = my_node->status;
1318 MMSOUND_STRNCPY(param_s.stream_type, my_node->stream_type, MAX_STREAM_TYPE_LEN);
1319 for (list_s = g_focus_node_list; list_s != NULL; list_s = list_s->next) {
1320 CONTINUE_IF_LIST_DATA_IS_NULL(node, list_s);
1321 if (my_node->pid == node->pid || node->is_for_watch)
1323 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1324 if (!(my_node->status & (i+1)))
1326 if (node->taken_by_id[i].pid == param_s.pid && node->taken_by_id[i].handle_id == param_s.handle_id) {
1327 /* do callback for resumption */
1328 if ((ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_ACQUIRE, node, ¶m_s)))
1329 debug_error("Fail to _focus_do_callback for COMMAND ACQUIRE to node[%p], ret[0x%x]", node, ret);
1330 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN))
1331 need_to_trigger_watch_cb = false;
1335 if (need_to_trigger_watch_cb) {
1336 if ((ret = _mm_sound_mgr_focus_do_watch_callback((focus_type_e)param_s.request_type, FOCUS_COMMAND_RELEASE, my_node, ¶m_s)))
1337 debug_error("Fail to _focus_do_watch_callback, ret[0x%x]", ret);
1340 __clear_focus_pipe(my_node);
1341 g_focus_node_list = g_list_remove(g_focus_node_list, my_node);
1342 list = g_focus_node_list;
1346 _mm_sound_mgr_focus_list_dump();
1347 _mm_sound_mgr_focus_watch_list_dump();
1349 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1356 int MMSoundMgrFocusInit(void)
1358 int ret = MM_ERROR_NONE;
1361 ret = __mm_sound_mgr_focus_dbus_get_stream_list(&g_stream_list);
1363 debug_error("failed to __mm_sound_mgr_focus_dbus_get_stream_list()");
1369 int MMSoundMgrFocusFini(void)
1374 for (i = 0; i < AVAIL_STREAMS_MAX; i++) {
1375 if (g_stream_list.stream_types[i])
1376 free(g_stream_list.stream_types[i]);
1380 return MM_ERROR_NONE;