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[] =
55 char stream_type[MAX_STREAM_TYPE_LEN];
56 char ext_info[MM_SOUND_NAME_NUM];
60 #define CLEAR_DEAD_NODE_LIST(x) do { \
61 debug_warning("list = %p, node = %p, pid=[%d]", x, node, (node)? node->pid : -1); \
62 if (x && node && (mm_sound_util_is_process_alive(node->pid) == FALSE)) { \
63 debug_warning("PID:%d does not exist now! remove from device cb list\n", node->pid); \
64 __clear_focus_pipe(node); \
65 x = g_list_remove (x, node); \
70 #define UPDATE_FOCUS_TAKEN_INFO(x_postfix, x_node, x_pid, x_hid, x_by_session) do { \
71 debug_msg("updating node[%p], taken_"#x_postfix"[%d] : pid = [%d], handle_id = [%d], is_for_session = [%d]", x_node, i, x_pid, x_hid, x_by_session); \
72 x_node->taken_##x_postfix[i].pid = x_pid; \
73 x_node->taken_##x_postfix[i].handle_id = x_hid; \
74 x_node->taken_##x_postfix[i].by_session = x_by_session; \
77 #define CHECK_MY_NODE(x_node, x_param) \
78 (x_node && !x_node->is_for_watch && (x_node->pid == x_param->pid) && (x_node->handle_id == x_param->handle_id) && (x_node->is_for_session == x_param->is_for_session))
80 static char* __get_focus_pipe_path(int instance_id, int handle, const char* postfix, bool is_watch)
86 path = g_strdup_printf("/tmp/FOCUS.%d.%d.wch", instance_id, handle);
88 path = g_strdup_printf("/tmp/FOCUS.%d.%d", instance_id, handle);
92 path2 = g_strconcat(path, postfix, NULL);
101 static void __clear_focus_pipe(focus_node_t *node)
103 char *filename = NULL;
104 char *filename2 = NULL;
108 if (!node->is_for_watch) {
109 filename = __get_focus_pipe_path(node->pid, node->handle_id, NULL, false);
110 filename2 = __get_focus_pipe_path(node->pid, node->handle_id, "r", false);
112 filename = __get_focus_pipe_path(node->pid, node->handle_id, NULL, true);
113 filename2 = __get_focus_pipe_path(node->pid, node->handle_id, "r", true);
116 if (remove(filename))
117 debug_error("remove() failure, filename(%s), errno(%d)", filename, errno);
119 debug_log("removed file(%s)", filename);
123 if (remove(filename2))
124 debug_error("remove() failure, filename2(%s), errno(%d)", filename2, errno);
126 debug_log("removed file(%s)", filename2);
133 static void _clear_focus_node_list_func(focus_node_t *node, gpointer user_data)
135 CLEAR_DEAD_NODE_LIST(g_focus_node_list);
138 static int _mm_sound_mgr_focus_get_priority_from_stream_type(int *priority, const char *stream_type)
140 int ret = MM_ERROR_NONE;
145 if (priority == NULL || stream_type == NULL) {
146 ret = MM_ERROR_INVALID_ARGUMENT;
147 debug_error("invalid argument, priority[0x%x], stream_type[%s], ret[0x%x]\n", priority, stream_type, ret);
149 for (i = 0; i < AVAIL_STREAMS_MAX; i++) {
150 if (g_stream_list.stream_types[i] &&
151 !strncmp(g_stream_list.stream_types[i], stream_type, strlen(stream_type))) {
152 *priority = g_stream_list.priorities[i];
156 if (i == AVAIL_STREAMS_MAX) {
157 ret = MM_ERROR_NOT_SUPPORT_API;
158 debug_error("not supported stream_type[%s], ret[0x%x]\n", stream_type, ret);
160 debug_log("[%s] has priority of [%d]\n", stream_type, *priority);
168 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)
170 char *filename = NULL;
171 char *filename2 = NULL;
182 focus_node_t *node = NULL;
183 focus_cb_data cb_data;
187 for (list = g_focus_node_list; list != NULL; list = list->next) {
188 node = (focus_node_t *)list->data;
189 if (node == my_node || (node->pid == my_node->pid && node->is_for_session && my_node->is_for_session)) {
192 if (node->is_for_watch && (node->status & focus_type)) {
193 memset(&cb_data, 0, sizeof(focus_cb_data));
194 cb_data.pid = node->pid;
195 cb_data.handle = node->handle_id;
196 cb_data.type = focus_type & node->status;
197 cb_data.state = (command == FOCUS_COMMAND_ACQUIRE) ? !FOCUS_STATUS_DEACTIVATED : FOCUS_STATUS_DEACTIVATED;
198 MMSOUND_STRNCPY(cb_data.stream_type, my_node->stream_type, MAX_STREAM_TYPE_LEN);
199 MMSOUND_STRNCPY(cb_data.ext_info, param->ext_info, MM_SOUND_NAME_NUM);
202 gettimeofday(&time, NULL);
203 starttime = time.tv_sec * 1000000 + time.tv_usec;
205 /**************************************
207 * Open callback cmd pipe
209 **************************************/
210 filename = __get_focus_pipe_path(cb_data.pid, cb_data.handle, NULL, true);
211 if (filename == NULL) {
212 debug_error("[CB] failed to get watch pipe");
215 if ((fd_FOCUS = open(filename, O_WRONLY|O_NONBLOCK)) == -1) {
217 strerror_r(errno, str_error, sizeof(str_error));
218 debug_error("[CB] failed to open watch pipe (%s, err:%s)\n", filename, str_error);
222 /******************************************
224 * Open callback result pipe
225 * before writing callback cmd to pipe
227 ******************************************/
228 filename2 = __get_focus_pipe_path(cb_data.pid, cb_data.handle, "r", true);
229 if (filename2 == NULL) {
230 debug_error("[RETCB] failed to get watch return pipe");
233 if ((fd_FOCUS_R= open(filename2, O_RDONLY|O_NONBLOCK)) == -1) {
235 strerror_r(errno, str_error, sizeof(str_error));
236 debug_error("[RETCB] failed to open watch return pipe (%s, err:%s)\n", filename2, str_error);
240 /*******************************************
242 *******************************************/
243 if (write(fd_FOCUS, &cb_data ,sizeof(cb_data)) == -1) {
245 strerror_r(errno, str_error, sizeof(str_error));
246 debug_error("[CB] failed to write (err:%s)\n", str_error);
250 /*********************************************
252 * Wait callback result msg
254 ********************************************/
258 debug_msg("[RETCB] wait WATCH CALLBACK (client pid=%d, cmd=%d, timeout=%d(ms))\n", cb_data.pid, command, CALLBACK_TIMEOUT);
259 pret = poll(&pfd, 1, CALLBACK_TIMEOUT);
261 debug_error("[RETCB] poll failed (%d)\n", pret);
264 if (pfd.revents & POLLIN) {
265 if (read(fd_FOCUS_R, &ret, sizeof(ret)) == -1) {
267 strerror_r(errno, str_error, sizeof(str_error));
268 debug_error("[RETCB] failed to read (err:%s)\n", str_error);
273 /* Calculate endtime and display*/
274 gettimeofday(&time, NULL);
275 endtime = time.tv_sec * 1000000 + time.tv_usec;
276 debug_msg("[RETCB] WATCH CALLBACK returned (cbtimelab=%d(ms), client pid=%d, return handle=%d)\n", ((endtime-starttime)/1000), cb_data.pid, ret);
278 /**************************************
280 * Close callback result pipe
282 **************************************/
290 if (fd_FOCUS != -1) {
294 if (fd_FOCUS_R != -1) {
304 return MM_ERROR_NONE;
307 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, const char *assaulter_stream_type)
309 int res = MM_ERROR_NONE;
310 char *filename = NULL;
311 char *filename2 = NULL;
321 int flag_for_focus_type = 0;
322 int flag_for_taken_index = 0;
326 bool taken_by_session = false;
327 bool reacquisition_changed = false;
329 focus_cb_data cb_data;
331 debug_msg(" __mm_sound_mgr_focus_do_callback_ for pid(%d) handle(%d)\n", victim_node->pid, victim_node->handle_id);
333 memset(&cb_data, 0, sizeof(focus_cb_data));
334 cb_data.pid= victim_node->pid;
335 cb_data.handle= victim_node->handle_id;
336 if (command == FOCUS_COMMAND_RELEASE) {
337 /* client will lost the acquired focus */
338 cb_data.type= assaulter_param->request_type & victim_node->status;
339 cb_data.state= FOCUS_STATUS_DEACTIVATED;
340 /* remove ext info. */
341 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
342 if (cb_data.type & (i+1)) {
343 memset(victim_node->ext_info[i], 0x0, MM_SOUND_NAME_NUM);
344 victim_node->option[i] = 0;
348 /* client will gain the lost focus */
349 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
350 if ((victim_node->taken_by_id[i].pid == assaulter_param->pid) && ((victim_node->taken_by_id[i].handle_id == assaulter_param->handle_id) || victim_node->taken_by_id[i].by_session)) {
351 flag_for_focus_type |= i+1; /* playback:1, capture:2 */
354 cb_data.type = flag_for_focus_type & assaulter_param->request_type;
355 cb_data.state = !FOCUS_STATUS_DEACTIVATED;
357 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
358 if (cb_data.type & (i+1)) {
359 MMSOUND_STRNCPY(victim_node->ext_info[i], assaulter_param->ext_info, MM_SOUND_NAME_NUM);
360 victim_node->option[i] = assaulter_param->option;
364 MMSOUND_STRNCPY(cb_data.stream_type, assaulter_stream_type, MAX_STREAM_TYPE_LEN);
365 MMSOUND_STRNCPY(cb_data.ext_info, assaulter_param->ext_info, MM_SOUND_NAME_NUM);
366 cb_data.option = assaulter_param->option;
369 gettimeofday(&time, NULL);
370 starttime = time.tv_sec * 1000000 + time.tv_usec;
372 /**************************************
374 * Open callback cmd pipe
376 **************************************/
377 filename = __get_focus_pipe_path(cb_data.pid, cb_data.handle, NULL, false);
378 if (filename == NULL) {
379 debug_error("[CB] failed to get pipe");
383 if ((fd_FOCUS = open(filename, O_WRONLY|O_NONBLOCK)) == -1) {
385 strerror_r(errno, str_error, sizeof(str_error));
386 debug_error("[CB] failed to open pipe (%s, err:%s)\n", filename, str_error);
391 /******************************************
393 * Open callback result pipe
394 * before writing callback cmd to pipe
396 ******************************************/
397 filename2 = __get_focus_pipe_path(cb_data.pid, cb_data.handle, "r", false);
398 if (filename2 == NULL) {
399 debug_error("[RETCB] failed to get return pipe");
403 if ((fd_FOCUS_R = open(filename2,O_RDONLY|O_NONBLOCK)) == -1) {
405 strerror_r(errno, str_error, sizeof(str_error));
406 debug_error("[RETCB] failed to open return pipe (%s, err:%s)\n", filename2, str_error);
411 /*******************************************
413 *******************************************/
414 if (write(fd_FOCUS, &cb_data, sizeof(cb_data)) == -1) {
416 strerror_r(errno, str_error, sizeof(str_error));
417 debug_error("[CB] failed to write (err:%s)\n", str_error);
422 /*********************************************
424 * Wait callback result msg
426 ********************************************/
430 debug_msg("[RETCB] wait CALLBACK (client pid=%d, handle=%d, cmd=%d, timeout=%d(ms))\n",cb_data.pid, cb_data.handle, command, CALLBACK_TIMEOUT);
431 pret = poll(&pfd, 1, CALLBACK_TIMEOUT);
433 debug_error("[RETCB] poll failed (%d)\n", pret);
437 if (pfd.revents & POLLIN) {
438 if (read(fd_FOCUS_R, &ret, sizeof(ret)) == -1) {
440 strerror_r(errno, str_error, sizeof(str_error));
441 debug_error("[RETCB] failed to read (err:%s)\n", str_error);
445 /* ret contains data as below,
446 * |<--12bits--><--4bits (reacquisition)--><--16bits (handle)-->| */
447 ret_handle = (int)(ret & 0x0000ffff);
448 if (victim_node->reacquisition != (bool)((ret >> 16) & 0xf)) {
449 reacquisition_changed = true;
450 victim_node->reacquisition = (bool)((ret >> 16) & 0xf);
451 debug_msg("[RETCB] victim's reacquisition is changed to (%d)\n", victim_node->reacquisition);
455 /* Calculate endtime and display*/
456 gettimeofday(&time, NULL);
457 endtime = time.tv_sec * 1000000 + time.tv_usec;
458 debug_msg("[RETCB] CALLBACK returned (cbtimelab=%d(ms), client pid=%d, returned handle=%d)\n", ((endtime-starttime)/1000), cb_data.pid, ret_handle);
460 /* update victim node */
461 if (command == FOCUS_COMMAND_RELEASE) {
462 taken_pid = assaulter_param->pid;
463 taken_hid = assaulter_param->handle_id;
464 taken_by_session = assaulter_param->is_for_session;
465 flag_for_taken_index = assaulter_param->request_type & victim_node->status;
469 taken_by_session = false;
470 flag_for_taken_index = assaulter_param->request_type;
473 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
474 if (flag_for_taken_index & (i+1)) {
476 focus_node_t *node = NULL;
478 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 && !(victim_node->taken_by_id[i].by_session & assaulter_param->is_for_session)))) {
480 debug_error("skip updating victim node");
483 if (reacquisition_changed) {
484 if (!victim_node->reacquisition) {
485 for (list = g_focus_node_list; list != NULL; list = list->next) {
486 if (!(node = (focus_node_t *)list->data))
488 if (node->taken_by_id[i].pid == victim_node->pid) {
489 UPDATE_FOCUS_TAKEN_INFO(backup, node, node->taken_by_id[i].pid, node->taken_by_id[i].handle_id, node->taken_by_id[i].by_session);
490 UPDATE_FOCUS_TAKEN_INFO(by_id, node, taken_pid, taken_hid, taken_by_session);
491 } else if (!list->next) {
492 UPDATE_FOCUS_TAKEN_INFO(backup, victim_node, taken_pid, taken_hid, taken_by_session);
493 UPDATE_FOCUS_TAKEN_INFO(by_id, victim_node, 0, 0, false);
497 for (list = g_focus_node_list; list != NULL; list = list->next) {
498 if (!(node = (focus_node_t *)list->data))
500 if (node->taken_backup[i].pid == victim_node->pid) {
501 UPDATE_FOCUS_TAKEN_INFO(by_id, node, node->taken_backup[i].pid, node->taken_backup[i].handle_id, node->taken_backup[i].by_session);
502 UPDATE_FOCUS_TAKEN_INFO(backup, node, 0, 0, false);
503 } else if (!list->next) {
504 UPDATE_FOCUS_TAKEN_INFO(by_id, victim_node, taken_pid, taken_hid, taken_by_session);
505 UPDATE_FOCUS_TAKEN_INFO(backup, victim_node, 0, 0, false);
510 if (victim_node->reacquisition)
511 UPDATE_FOCUS_TAKEN_INFO(by_id, victim_node, taken_pid, taken_hid, taken_by_session);
513 UPDATE_FOCUS_TAKEN_INFO(backup, victim_node, taken_pid, taken_hid, taken_by_session);
518 if (command == FOCUS_COMMAND_RELEASE || victim_node->reacquisition_with_released_state)
519 victim_node->status = victim_node->status & ~(cb_data.type);
520 else if (command == FOCUS_COMMAND_ACQUIRE)
521 victim_node->status = victim_node->status | cb_data.type;
523 if (strncmp(assaulter_stream_type, victim_node->stream_type, MAX_STREAM_TYPE_LEN))
524 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)assaulter_param->request_type, command, victim_node, assaulter_param);
533 if (fd_FOCUS != -1) {
537 if (fd_FOCUS_R != -1) {
545 static int _mm_sound_mgr_focus_list_dump ()
547 int ret = MM_ERROR_NONE;
549 focus_node_t *node = NULL;
551 debug_msg("================================================ focus node list : start ===================================================\n");
552 for (list = g_focus_node_list; list != NULL; list = list->next) {
553 if ((node = (focus_node_t *)list->data) && !node->is_for_watch) {
554 debug_msg("*** pid[%5d]/handle_id[%2d]/[%14s]:priority[%2d],status[%s],taken_by[P(%5d/%2d/%2d)C(%5d/%2d/%2d)],session[%d],option[0x%x/0x%x],ext_info[%s/%s]\n",
555 node->pid, node->handle_id, node->stream_type, node->priority, focus_status_str[node->status],
556 node->taken_by_id[0].pid, node->taken_by_id[0].handle_id, node->taken_by_id[0].by_session, node->taken_by_id[1].pid,
557 node->taken_by_id[1].handle_id, node->taken_by_id[1].by_session, node->is_for_session, node->option[0], node->option[1], node->ext_info[0], node->ext_info[1]);
560 debug_msg("================================================ focus node list : end =====================================================\n");
565 static int _mm_sound_mgr_focus_watch_list_dump()
567 int ret = MM_ERROR_NONE;
569 focus_node_t *node = NULL;
571 debug_msg("============================================= focus watch node list : start =================================================\n");
572 for (list = g_focus_node_list; list != NULL; list = list->next) {
573 if ((node = (focus_node_t *)list->data) && node->is_for_watch)
574 debug_msg("*** pid[%5d]/handle_id[%d]/watch on focus status[%s]/for_session[%d]\n", node->pid, node->handle_id, focus_status_str[node->status], node->is_for_session);
576 debug_msg("============================================= focus watch node list : end ===================================================\n");
581 static void _mm_sound_mgr_focus_fill_info_from_msg(focus_node_t *node, const _mm_sound_mgr_focus_param_t *msg)
584 node->pid = msg->pid;
585 node->handle_id = msg->handle_id;
586 node->callback = msg->callback;
587 node->cbdata = msg->cbdata;
588 node->is_for_session = msg->is_for_session;
594 int mm_sound_mgr_focus_create_node(const _mm_sound_mgr_focus_param_t *param)
596 int ret = MM_ERROR_NONE;
598 focus_node_t *node = NULL;
603 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
605 /* Update list for dead process */
606 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
608 for (list = g_focus_node_list; list != NULL; list = list->next) {
609 node = (focus_node_t *)list->data;
610 if (CHECK_MY_NODE(node, param)) {
611 debug_error("the node of pid[%d]/handle_id[%d] is already created\n", param->pid, param->handle_id);
612 ret = MM_ERROR_INVALID_ARGUMENT;
617 /* get priority from stream type */
618 ret = _mm_sound_mgr_focus_get_priority_from_stream_type(&priority, param->stream_type);
622 node = g_malloc0(sizeof(focus_node_t));
624 /* fill up information to the node */
625 _mm_sound_mgr_focus_fill_info_from_msg(node, param);
626 node->priority = priority;
627 node->status = FOCUS_STATUS_DEACTIVATED;
628 node->reacquisition = true;
629 MMSOUND_STRNCPY(node->stream_type, param->stream_type, MAX_STREAM_TYPE_LEN);
631 g_focus_node_list = g_list_append(g_focus_node_list, node);
632 if (g_focus_node_list) {
633 debug_log("new focus node is added\n");
635 debug_error("g_list_append failed\n");
636 ret = MM_ERROR_SOUND_INTERNAL;
640 _mm_sound_mgr_focus_list_dump();
642 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
648 int mm_sound_mgr_focus_destroy_node(const _mm_sound_mgr_focus_param_t *param)
650 int ret = MM_ERROR_NONE;
652 focus_node_t *node = NULL;
653 focus_node_t *my_node = NULL;
654 bool need_to_trigger = true;
659 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
661 /* Update list for dead process */
662 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
664 for (list = g_focus_node_list; list != NULL; list = list->next) {
665 node = (focus_node_t *)list->data;
666 if (CHECK_MY_NODE(node, param)) {
667 debug_log("found the node of pid[%d]/handle_id[%d]\n", param->pid, param->handle_id);
672 if (my_node == NULL) {
673 debug_error("could not find any node of pid[%d]/handle_id[%d]/is_for_session[%d]\n", param->pid, param->handle_id, param->is_for_session);
674 ret = MM_ERROR_INVALID_ARGUMENT;
678 /* Check if there's remaining focus for session for the same PID of incomming param */
679 if (my_node->is_for_session) {
680 for (list = g_focus_node_list; list != NULL; list = list->next) {
681 if (!(node = (focus_node_t *)list->data))
683 if (my_node == node || node->is_for_watch)
685 if (node->pid == my_node->pid && node->is_for_session && node->status) {
686 debug_error("focus for session for this pid still remains, skip updating victim focus nodes");
687 need_to_trigger = false;
693 if (need_to_trigger) {
694 for (list = g_focus_node_list; list != NULL; list = list->next) {
695 if (!(node = (focus_node_t *)list->data))
697 if (my_node == node || node->is_for_watch)
699 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
700 if (node->taken_by_id[i].pid == param->pid) {
701 if (my_node->taken_by_id[i].pid) {
702 /* If exists update the taken focus info to my victim node */
703 if (node->taken_by_id[i].by_session && !node->status) {
704 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id, my_node->taken_by_id[i].by_session);
705 } else if (node->taken_by_id[i].handle_id == param->handle_id) {
706 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id, false);
708 } else if (my_node->status & (i+1)) {
709 if (node->is_for_session)
711 _mm_sound_mgr_focus_param_t *new_param = g_malloc0(sizeof(_mm_sound_mgr_focus_param_t));
713 debug_warning("Fail to g_malloc0 for new_param, but keep going\n");
716 new_param->pid = param->pid;
717 new_param->handle_id = param->handle_id;
718 new_param->is_for_session = my_node->is_for_session;
719 new_param->request_type = my_node->status;
720 new_param->option = my_node->option[i];
721 MMSOUND_STRNCPY(new_param->ext_info, my_node->ext_info[i], MM_SOUND_NAME_NUM);
723 if (node->taken_by_id[i].handle_id == new_param->handle_id || node->taken_by_id[i].by_session) {
724 /* do callback for resumption */
725 if ((ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_ACQUIRE, node, new_param, my_node->stream_type)))
726 debug_error("Fail to _focus_do_callback for COMMAND ACQUIRE to node[%x], ret[0x%x]\n", node, ret);
727 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN)) {
728 my_node->status &= ~(new_param->request_type);
729 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)new_param->request_type, FOCUS_COMMAND_RELEASE, my_node, new_param);
740 /* Destroy my node */
741 __clear_focus_pipe(my_node);
742 g_focus_node_list = g_list_remove(g_focus_node_list, my_node);
745 _mm_sound_mgr_focus_list_dump();
746 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
752 int mm_sound_mgr_focus_set_reacquisition(const _mm_sound_mgr_focus_param_t *param)
754 int ret = MM_ERROR_NONE;
756 focus_node_t *node = NULL;
757 focus_node_t *my_node = NULL;
762 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
764 /* Update list for dead process */
765 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
767 /* Find node to set reacquisition */
768 for (list = g_focus_node_list; list != NULL; list = list->next) {
769 node = (focus_node_t *)list->data;
770 if (CHECK_MY_NODE(node, param)) {
771 if (node->reacquisition == param->reacquisition) {
772 debug_msg("it is already set as same value of reacquisition(%d)\n", param->reacquisition);
775 node->reacquisition = param->reacquisition;
776 debug_msg("found a node(pid[%d]/handle_id[%d]) to set reacquisition to (%d)\n", node->pid, node->handle_id, param->reacquisition);
781 if (my_node == NULL) {
782 debug_error("could not find any node of pid[%d]/handle_id[%d]\n", param->pid, param->handle_id);
783 ret = MM_ERROR_INVALID_ARGUMENT;
787 if (!param->reacquisition) {
788 for (list = g_focus_node_list; list != NULL; list = list->next) {
789 if (!(node = (focus_node_t *)list->data))
791 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
792 if (node->taken_by_id[i].pid == param->pid) {
793 /* victim node : append my node's taken info to my victim node */
794 if (my_node->taken_by_id[i].pid != 0) {
795 UPDATE_FOCUS_TAKEN_INFO(backup, node, node->taken_by_id[i].pid, node->taken_by_id[i].handle_id, node->taken_by_id[i].by_session);
796 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id, my_node->taken_by_id[i].by_session);
798 } else if (!list->next) {
799 /* my node : backup and reset */
800 UPDATE_FOCUS_TAKEN_INFO(backup, my_node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id, my_node->taken_by_id[i].by_session);
801 UPDATE_FOCUS_TAKEN_INFO(by_id, my_node, 0, 0, false);
806 for (list = g_focus_node_list; list != NULL; list = list->next) {
807 if (!(node = (focus_node_t *)list->data))
809 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
810 /* rollback and reset backup info. */
811 if (node->taken_by_id[i].pid && (node->taken_by_id[i].pid == my_node->taken_backup[i].pid)) {
812 UPDATE_FOCUS_TAKEN_INFO(by_id, node, node->taken_backup[i].pid, node->taken_backup[i].handle_id, node->taken_backup[i].by_session);
813 UPDATE_FOCUS_TAKEN_INFO(backup, node, 0, 0, false);
814 } else if (!list->next) {
815 UPDATE_FOCUS_TAKEN_INFO(by_id, my_node, my_node->taken_backup[i].pid, my_node->taken_backup[i].handle_id, my_node->taken_backup[i].by_session);
816 UPDATE_FOCUS_TAKEN_INFO(backup, my_node, 0, 0, false);
823 _mm_sound_mgr_focus_list_dump();
824 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
830 int mm_sound_mgr_focus_get_stream_type_of_acquired_focus(focus_type_e focus_type, char **stream_type, int *option, char **ext_info)
832 int ret = MM_ERROR_SOUND_NO_DATA;
834 focus_node_t *node = NULL;
838 if (focus_type == FOCUS_TYPE_BOTH) /* focus_type should be "playback" or "capture" */
839 return MM_ERROR_INVALID_ARGUMENT;
841 return MM_ERROR_INVALID_ARGUMENT;
843 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
845 /* Update list for dead process */
846 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
848 /* Find node to set reacquisition */
849 for (list = g_focus_node_list; list != NULL; list = list->next) {
850 if (!(node = (focus_node_t *)list->data))
852 if (!node->is_for_watch && (node->status & focus_type)) {
853 debug_msg("found a node : request_focus_type(%d), stream_type(%s)/ext info(%s) of acquired focus\n", focus_type, node->stream_type, node->ext_info);
854 *stream_type = node->stream_type;
855 *option = node->option[focus_type-1];
857 *ext_info = node->ext_info[focus_type-1];
863 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
870 static void update_reacquisition_with_released_state(focus_node_t *node, int direction)
873 debug_error("node is null");
876 if (direction >= NUM_OF_STREAM_IO_TYPE) {
877 debug_error("invalid direction(%d)", direction);
881 /* In case of session backward compatibility for audio-io, mm-player, mm-camcorder, we mark a specific flag here.
882 When invoking focus state changed callback for acquiring, state of the node will not be updated rather updated
883 when the next request to acquire. */
884 if (!strncmp("audio-io acquire focus", node->ext_info[direction], MM_SOUND_NAME_NUM) ||
885 !strncmp("mm-player acquire focus", node->ext_info[direction], MM_SOUND_NAME_NUM) ||
886 !strncmp("mm-camcorder acquire focus", node->ext_info[direction], MM_SOUND_NAME_NUM)) {
887 debug_msg("this node[pid:%d, handle_id:%d] needs reacquisition with focus released state\n", node->pid, node->handle_id);
888 node->reacquisition_with_released_state = true;
892 int mm_sound_mgr_focus_request_acquire(const _mm_sound_mgr_focus_param_t *param)
894 int ret = MM_ERROR_NONE;
896 focus_node_t *node = NULL;
897 focus_node_t *my_node = NULL;
898 bool need_to_trigger_cb = false;
899 bool need_to_trigger_watch_cb = true;
904 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
906 /* Update list for dead process */
907 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
909 for (list = g_focus_node_list; list != NULL; list = list->next) {
910 node = (focus_node_t *)list->data;
911 if (CHECK_MY_NODE(node, param)) {
913 if ((my_node->status > FOCUS_STATUS_DEACTIVATED) && (my_node->status & param->request_type)) {
914 debug_error("focus status is already activated");
915 ret = MM_ERROR_SOUND_INVALID_STATE;
921 if (my_node == NULL) {
922 debug_error("node is null");
923 ret = MM_ERROR_INVALID_ARGUMENT;
927 /* check if the priority of any node is higher than its based on io direction */
928 for (list = g_focus_node_list; list != NULL; list = list->next) {
929 if (!(node = (focus_node_t *)list->data))
931 if (my_node == node || node->is_for_watch)
933 if (param->request_type == FOCUS_TYPE_BOTH || node->status == FOCUS_STATUS_ACTIVATED_BOTH ||
934 (node->status & param->request_type)) {
935 if (node->status > FOCUS_STATUS_DEACTIVATED) {
936 if ((my_node->priority < node->priority)) {
937 ret = MM_ERROR_POLICY_BLOCKED;
938 need_to_trigger_cb = false;
941 need_to_trigger_cb = true;
947 if (need_to_trigger_cb) {
948 _mm_sound_mgr_focus_param_t *param_s = (_mm_sound_mgr_focus_param_t *)param;
949 param_s->is_for_session = my_node->is_for_session;
950 for (list = g_focus_node_list; list != NULL; list = list->next) {
951 if (!(node = (focus_node_t *)list->data))
953 if (node == my_node || node->is_for_watch || (node->pid == my_node->pid && node->is_for_session && my_node->is_for_session))
955 if (param_s->request_type == FOCUS_TYPE_BOTH || node->status == FOCUS_STATUS_ACTIVATED_BOTH ||
956 (node->status & param_s->request_type)) {
957 if (node->status > FOCUS_STATUS_DEACTIVATED) {
958 if (my_node->priority >= node->priority) {
959 /* do callback for interruption */
960 ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_RELEASE, node, param_s, my_node->stream_type);
962 debug_error("Fail to _focus_do_callback for COMMAND RELEASE to node[%x], ret[0x%x]\n", node, ret);
963 /* but, keep going */
966 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN)) {
967 need_to_trigger_watch_cb = false;
975 if (ret != MM_ERROR_POLICY_BLOCKED) {
977 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
978 if (param->request_type & (i+1)) {
979 MMSOUND_STRNCPY(my_node->ext_info[i], param->ext_info, MM_SOUND_NAME_NUM);
980 my_node->option[i] = param->option;
981 update_reacquisition_with_released_state(my_node, i);
985 my_node->status |= param->request_type;
986 /* do watch callback due to the status of mine */
987 if (need_to_trigger_watch_cb)
988 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)param->request_type, FOCUS_COMMAND_ACQUIRE, my_node, param);
989 /* update taken information */
990 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
991 if (param->request_type & (i+1)) {
992 UPDATE_FOCUS_TAKEN_INFO(by_id, my_node, 0, 0, false);
993 UPDATE_FOCUS_TAKEN_INFO(backup, my_node, 0, 0, false);
998 _mm_sound_mgr_focus_list_dump();
999 _mm_sound_mgr_focus_watch_list_dump ();
1001 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1007 int mm_sound_mgr_focus_request_release(const _mm_sound_mgr_focus_param_t *param)
1009 int ret = MM_ERROR_NONE;
1011 focus_node_t *node = NULL;
1012 focus_node_t *my_node = NULL;
1013 bool need_to_trigger_watch_cb = true;
1014 bool need_to_trigger_cb = true;
1019 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1021 /* Update list for dead process */
1022 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1024 for (list = g_focus_node_list; list != NULL; list = list->next) {
1025 node = (focus_node_t *)list->data;
1026 if (CHECK_MY_NODE(node, param)) {
1028 if (my_node->status == FOCUS_STATUS_DEACTIVATED) {
1029 debug_error("focus status is already deactivated");
1030 ret = MM_ERROR_SOUND_INVALID_STATE;
1032 } else if ((my_node->status != FOCUS_STATUS_ACTIVATED_BOTH) && (my_node->status != (focus_status_e)param->request_type)) {
1033 debug_error("request type is not matched with current focus type");
1034 ret = MM_ERROR_SOUND_INVALID_STATE;
1041 if (my_node == NULL) {
1042 debug_error("node is null");
1043 ret = MM_ERROR_INVALID_ARGUMENT;
1047 /* Check if there's activating focus for session for the same PID of incomming param*/
1048 if (my_node->is_for_session) {
1049 for (list = g_focus_node_list; list != NULL; list = list->next) {
1050 if (!(node = (focus_node_t *)list->data))
1052 if (node == my_node || node->is_for_watch)
1054 if (node->pid == my_node->pid && node->is_for_session && node->status) {
1055 debug_error("focus for session for this pid is active, skip callbacks");
1056 need_to_trigger_watch_cb = false;
1057 need_to_trigger_cb = false;
1063 if (need_to_trigger_cb) {
1064 _mm_sound_mgr_focus_param_t *param_s = (_mm_sound_mgr_focus_param_t *)param;
1065 param_s->is_for_session = my_node->is_for_session;
1066 for (list = g_focus_node_list; list != NULL; list = list->next) {
1067 if (!(node = (focus_node_t *)list->data))
1069 if (node == my_node || node->is_for_watch)
1071 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1072 if (param_s->request_type & (i+1)) {
1073 if (node->taken_by_id[i].pid == param_s->pid && (node->taken_by_id[i].handle_id == param_s->handle_id || node->taken_by_id[i].by_session)) {
1074 /* do callback for resumption */
1075 ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_ACQUIRE, node, param_s, my_node->stream_type);
1077 debug_error("Fail to _focus_do_callback for COMMAND ACQUIRE to node[%x], ret[0x%x]\n", node, ret);
1079 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN)) {
1080 need_to_trigger_watch_cb = false;
1088 my_node->status &= ~(param->request_type);
1089 /* remove ext info. */
1090 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1091 if (!(my_node->status & (i+1))) {
1092 memset(my_node->ext_info[i], 0x0, MM_SOUND_NAME_NUM);
1093 my_node->option[i] = 0;
1096 /* do watch callback due to the status of mine */
1097 if (need_to_trigger_watch_cb)
1098 _mm_sound_mgr_focus_do_watch_callback((focus_type_e)param->request_type, FOCUS_COMMAND_RELEASE, my_node, param);
1100 _mm_sound_mgr_focus_list_dump();
1101 _mm_sound_mgr_focus_watch_list_dump ();
1103 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1109 int mm_sound_mgr_focus_set_watch_cb(const _mm_sound_mgr_focus_param_t *param)
1111 int ret = MM_ERROR_NONE;
1113 focus_node_t *node = NULL;
1117 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1119 /* Update list for dead process */
1120 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1122 for (list = g_focus_node_list; list != NULL; list = list->next) {
1123 if (!(node = (focus_node_t *)list->data))
1125 if ((node->pid == param->pid) && (node->handle_id == param->handle_id) && node->is_for_watch) {
1126 debug_error("the node of pid[%d]/handle_id[%d] for watch focus is already created\n", param->pid, param->handle_id);
1127 ret = MM_ERROR_INVALID_ARGUMENT;
1132 node = g_malloc0(sizeof(focus_node_t));
1134 /* fill up information to the node */
1135 _mm_sound_mgr_focus_fill_info_from_msg(node, param);
1136 node->is_for_watch = true;
1137 node->status = param->request_type;
1139 g_focus_node_list = g_list_append(g_focus_node_list, node);
1140 if (g_focus_node_list) {
1141 debug_log("new focus node is added\n");
1143 debug_error("g_list_append failed\n");
1144 ret = MM_ERROR_SOUND_INTERNAL;
1148 _mm_sound_mgr_focus_watch_list_dump();
1150 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1156 int mm_sound_mgr_focus_unset_watch_cb(const _mm_sound_mgr_focus_param_t *param)
1158 int ret = MM_ERROR_SOUND_INTERNAL;
1160 focus_node_t *node = NULL;
1164 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1166 /* Update list for dead process */
1167 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1169 for (list = g_focus_node_list; list != NULL; list = list->next) {
1170 if (!(node = (focus_node_t *)list->data))
1172 if ((node->pid == param->pid) && (node->handle_id == param->handle_id) && node->is_for_watch) {
1173 debug_log("found the node of pid[%d]/handle_id[%d] for watch focus\n", param->pid, param->handle_id);
1174 __clear_focus_pipe(node);
1175 g_focus_node_list = g_list_remove(g_focus_node_list, node);
1177 ret = MM_ERROR_NONE;
1182 debug_error("could not find any node of pid[%d]/handle_id[%d] for watch focus\n", param->pid, param->handle_id);
1183 ret = MM_ERROR_INVALID_ARGUMENT;
1187 _mm_sound_mgr_focus_watch_list_dump();
1189 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1195 int mm_sound_mgr_focus_emergent_exit(const _mm_sound_mgr_focus_param_t *param)
1197 int ret = MM_ERROR_NONE;
1199 GList *list_s =NULL;
1200 focus_node_t *node = NULL;
1201 focus_node_t *my_node = NULL;
1206 MMSOUND_ENTER_CRITICAL_SECTION_WITH_RETURN(&g_focus_node_list_mutex, MM_ERROR_SOUND_INTERNAL);
1208 /* Update list for dead process */
1209 g_list_foreach (g_focus_node_list, (GFunc)_clear_focus_node_list_func, NULL);
1211 list = g_focus_node_list;
1213 if (!(node = (focus_node_t *)list->data))
1215 if (node->pid == param->pid) {
1216 debug_log("found pid node");
1217 if (node->is_for_watch) {
1218 debug_log("clearing watch cb of pid(%d) handle(%d)", node->pid, node->handle_id);
1219 __clear_focus_pipe(node);
1220 g_focus_node_list = g_list_remove(g_focus_node_list, node);
1221 list = g_focus_node_list;
1223 } else if (node->status == FOCUS_STATUS_DEACTIVATED) {
1224 debug_log("clearing deactivated focus node of pid(%d) hande(%d)", node->pid, node->handle_id);
1226 /* update info of nodes that are lost their focus by the process exited */
1227 for (list_s = g_focus_node_list; list_s != NULL; list_s = list_s->next) {
1228 if (!(node = (focus_node_t *)list_s->data))
1230 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1231 if (node->taken_by_id[i].pid == param->pid) {
1232 if (my_node->taken_by_id[i].pid) {
1233 UPDATE_FOCUS_TAKEN_INFO(by_id, node, my_node->taken_by_id[i].pid, my_node->taken_by_id[i].handle_id, my_node->taken_by_id[i].by_session);
1235 UPDATE_FOCUS_TAKEN_INFO(by_id, node, 0, 0, false);
1240 __clear_focus_pipe(my_node);
1241 g_focus_node_list = g_list_remove(g_focus_node_list, my_node);
1242 list = g_focus_node_list;
1244 } else { /* node that acquired focus */
1245 bool need_to_trigger_watch_cb = true;
1246 _mm_sound_mgr_focus_param_t param_s;
1247 debug_log("clearing activated focus node of pid(%d) handle(%d)", node->pid, node->handle_id);
1250 memset(¶m_s, 0x00, sizeof(_mm_sound_mgr_focus_param_t));
1251 param_s.pid = my_node->pid;
1252 param_s.handle_id = my_node->handle_id;
1253 param_s.request_type = my_node->status;
1254 for (list_s = g_focus_node_list; list_s != NULL; list_s = list_s->next) {
1255 if (!(node = (focus_node_t *)list_s->data))
1257 if (my_node->pid == node->pid || node->is_for_watch)
1259 for (i = 0; i < NUM_OF_STREAM_IO_TYPE; i++) {
1260 if (my_node->status & (i+1)) {
1261 if (node->taken_by_id[i].pid == param_s.pid && node->taken_by_id[i].handle_id == param_s.handle_id) {
1262 /* do callback for resumption */
1263 ret = _mm_sound_mgr_focus_do_callback(FOCUS_COMMAND_ACQUIRE, node, ¶m_s, my_node->stream_type);
1265 debug_error("Fail to _focus_do_callback for COMMAND ACQUIRE to node[%x], ret[0x%x]\n", node, ret);
1267 if (!strncmp(my_node->stream_type, node->stream_type, MAX_STREAM_TYPE_LEN)) {
1268 need_to_trigger_watch_cb = false;
1274 if (need_to_trigger_watch_cb) {
1275 ret = _mm_sound_mgr_focus_do_watch_callback((focus_type_e)param_s.request_type, FOCUS_COMMAND_RELEASE, my_node, ¶m_s);
1277 debug_error("Fail to _focus_do_watch_callback, ret[0x%x]\n", ret);
1280 __clear_focus_pipe(my_node);
1281 g_focus_node_list = g_list_remove(g_focus_node_list, my_node);
1282 list = g_focus_node_list;
1286 debug_log("node not found, next list = %p",list);
1290 _mm_sound_mgr_focus_list_dump();
1291 _mm_sound_mgr_focus_watch_list_dump ();
1293 MMSOUND_LEAVE_CRITICAL_SECTION(&g_focus_node_list_mutex);
1300 int MMSoundMgrFocusInit(void)
1302 int ret = MM_ERROR_NONE;
1305 ret = __mm_sound_mgr_focus_dbus_get_stream_list(&g_stream_list);
1307 debug_error("failed to __mm_sound_mgr_ipc_dbus_get_stream_list()\n");
1313 int MMSoundMgrFocusFini(void)
1318 for (i = 0; i < AVAIL_STREAMS_MAX; i++) {
1319 if (g_stream_list.stream_types[i]) {
1320 free(g_stream_list.stream_types[i]);
1325 return MM_ERROR_NONE;