4 * Copyright (c) 2017 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.
35 #include "include/mm_sound_focus_private.h"
37 focus_sound_info_t g_focus_sound_handle[FOCUS_HANDLE_MAX];
39 static void unlink_if_symbolic_link(const char *path)
42 char *resolved_path = NULL;
47 /* return if it does not exist */
48 if ((ret = access(path, F_OK)))
51 if ((resolved_path = realpath(path, NULL))) {
52 /* assume that the path paramether is an absolute path */
53 if (strcmp(path, resolved_path)) {
54 debug_warning("unexpected symbolic link!, unlink the symbolic link(%s) to the resolved path(%s)", path, resolved_path);
60 strerror_r(errno, str_error, sizeof(str_error));
61 debug_warning("failed to realpath() for path:%s, err:%s", path, str_error);
65 static gpointer _focus_thread_func(gpointer data)
67 unsigned int thread_id = (unsigned int)pthread_self();
68 GMainLoop *focus_loop = (GMainLoop*)data;
70 debug_warning(">>> thread id(%u), mainloop[%p]", thread_id, focus_loop);
72 g_main_loop_run(focus_loop);
73 debug_warning("<<< quit : thread id(%u), mainloop[%p]", thread_id, focus_loop);
78 static gboolean _focus_fd_prepare(GSource *source, gint *timeout)
81 debug_warning("[ PREPARE : %p, (%p, %d)", source, timeout, timeout ? *timeout : -1);
86 static gboolean _focus_fd_check(GSource * source)
88 FocusSource* fsource = (FocusSource *)source;
91 debug_error("GSource is null");
95 debug_warning("CHECK : %p, 0x%x ]", source, fsource->poll_fd.revents);
97 if (fsource->poll_fd.revents & (POLLIN | POLLPRI))
103 static gboolean _focus_fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
105 debug_warning("*** DISPATCH : %p, (%p, %p)", source, callback, user_data);
106 return callback(user_data);
109 static void _focus_fd_finalize(GSource *source)
111 debug_warning("### FINALIZE : %p", source);
114 static gboolean _focus_callback_handler(gpointer user_data)
116 focus_sound_info_t *focus_handle = (focus_sound_info_t *)user_data;
120 focus_cb_data_lib cb_data;
122 debug_msg(">>> thread id(%u)", (unsigned int)pthread_self());
125 debug_error("focus_handle is null");
126 return G_SOURCE_REMOVE;
128 if (!focus_handle->fsrc) {
129 debug_error("fsrc is null");
130 return G_SOURCE_REMOVE;
132 if (focus_handle->is_destroying) {
133 debug_warning("focus_handle is destroying now, skip it");
134 return G_SOURCE_REMOVE;
137 g_mutex_lock(&focus_handle->focus_lock);
139 poll_fd = &focus_handle->fsrc->poll_fd;
140 debug_msg("focus_handle(%p), poll_fd(%p)", focus_handle, poll_fd);
142 memset(&cb_data, 0, sizeof(focus_cb_data_lib));
144 if (poll_fd->revents & (POLLIN | POLLPRI)) {
145 int changed_state = -1;
147 count = read(poll_fd->fd, &cb_data, sizeof(cb_data));
150 strerror_r(errno, str_error, sizeof(str_error));
151 debug_error("GpollFD read fail, errno=%d(%s)", errno, str_error);
152 g_mutex_unlock(&focus_handle->focus_lock);
153 return G_SOURCE_CONTINUE;
155 changed_state = cb_data.state;
157 tid = focus_handle->focus_pid;
159 if (changed_state != -1) {
160 debug_msg("Got and start CB : TID(%d), handle(%d), type(%d), state(%d,(DEACTIVATED(0)/ACTIVATED(1)), trigger(%s)",
161 tid, cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type);
162 if (focus_handle->focus_callback == NULL) {
163 debug_error("focus callback is null..");
164 g_mutex_unlock(&focus_handle->focus_lock);
165 return G_SOURCE_CONTINUE;
167 debug_msg("[CALLBACK(%p) START]", focus_handle->focus_callback);
168 (focus_handle->focus_callback)(cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type,
169 cb_data.option, cb_data.ext_info, focus_handle->user_data);
170 debug_msg("[CALLBACK END]");
175 unsigned int buf = 0;
176 char *filename2 = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%dr", focus_handle->focus_pid, cb_data.handle);
178 unlink_if_symbolic_link(filename2);
179 tmpfd = open(filename2, O_WRONLY | O_NONBLOCK);
182 strerror_r(errno, str_error, sizeof(str_error));
183 debug_warning("[RETCB][Failed(May Server Close First)]tid(%d) fd(%d) %s errno=%d(%s)",
184 tid, tmpfd, filename2, errno, str_error);
186 g_mutex_unlock(&focus_handle->focus_lock);
187 return G_SOURCE_CONTINUE;
189 /* buf contains data as below,
190 * |<--12bits--><--4bits (reacquisition)--><--16bits (handle)-->| */
191 buf = (unsigned int)((0x0000ffff & cb_data.handle) | (focus_handle->auto_reacquire << 16));
192 rett = write(tmpfd, &buf, sizeof(buf));
195 debug_msg("[RETCB] tid(%d) finishing CB (write=%d)", tid, rett);
199 g_mutex_unlock(&focus_handle->focus_lock);
203 return G_SOURCE_CONTINUE;
206 static gboolean _focus_watch_callback_handler(gpointer user_data)
208 focus_sound_info_t *focus_handle = (focus_sound_info_t *)user_data;
212 focus_cb_data_lib cb_data;
214 debug_msg(">>> thread id(%u)", (unsigned int)pthread_self());
217 debug_error("focus_handle is null");
218 return G_SOURCE_REMOVE;
220 if (focus_handle->unset_watch_callback_requested) {
221 debug_warning("unset_watch_callback_requested..");
222 return G_SOURCE_REMOVE;
224 if (!focus_handle->fsrc) {
225 debug_error("fsrc is null");
226 return G_SOURCE_REMOVE;
229 poll_fd = &focus_handle->fsrc->poll_fd;
230 debug_msg("focus_handle(%p), poll_fd(%p)", focus_handle, poll_fd);
232 memset(&cb_data, 0, sizeof(focus_cb_data_lib));
234 if (poll_fd->revents & (POLLIN | POLLPRI)) {
235 count = read(poll_fd->fd, &cb_data, sizeof(cb_data));
238 strerror_r(errno, str_error, sizeof(str_error));
239 debug_error("GpollFD read fail, errno=%d(%s)", errno, str_error);
240 return G_SOURCE_CONTINUE;
243 if (!focus_handle->is_used) {
244 debug_warning("unsetting watch calllback has been already requested");
245 goto SKIP_CB_AND_RET;
248 g_mutex_lock(&focus_handle->focus_lock);
250 tid = focus_handle->focus_pid;
252 debug_msg("Got and start CB : TID(%d), handle(%d), type(%d), state(%d,(DEACTIVATED(0)/ACTIVATED(1)), trigger(%s)",
253 tid, cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type);
255 if (focus_handle->watch_callback == NULL) {
256 debug_warning("watch callback is null..");
257 goto SKIP_CB_AND_RET;
260 debug_msg("[CALLBACK(%p) START]", focus_handle->watch_callback);
261 (focus_handle->watch_callback)(cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type,
262 cb_data.ext_info, focus_handle->user_data);
263 debug_msg("[CALLBACK END]");
270 char *filename2 = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d.wchr", focus_handle->focus_pid, cb_data.handle);
272 unlink_if_symbolic_link(filename2);
273 tmpfd = open(filename2, O_WRONLY | O_NONBLOCK);
276 strerror_r(errno, str_error, sizeof(str_error));
277 debug_warning("[RETCB][Failed(May Server Close First)]tid(%d) fd(%d) %s errno=%d(%s)",
278 tid, tmpfd, filename2, errno, str_error);
280 if (focus_handle->is_used)
281 g_mutex_unlock(&focus_handle->focus_lock);
282 return G_SOURCE_CONTINUE;
284 buf = cb_data.handle;
285 rett = write(tmpfd, &buf, sizeof(buf));
288 debug_msg("[RETCB] tid(%d) finishing CB (write=%d)", tid, rett);
292 if (focus_handle->is_used) {
293 debug_msg("unlock focus_lock = %p", &focus_handle->focus_lock);
294 g_mutex_unlock(&focus_handle->focus_lock);
299 return G_SOURCE_CONTINUE;
302 #define INTERVAL_MS 20
303 static int _focus_loop_is_running_timed_wait(GMainLoop *focus_loop, int timeout_ms)
305 int reduced_time_ms = timeout_ms;
306 if (!focus_loop || timeout_ms < 0) {
307 debug_error("invalid argument, focus_loop(%p), timeout_ms(%d)", focus_loop, timeout_ms);
308 return MM_ERROR_INVALID_ARGUMENT;
312 if (g_main_loop_is_running(focus_loop))
313 return MM_ERROR_NONE;
315 usleep(INTERVAL_MS * 1000);
316 if (reduced_time_ms < timeout_ms)
317 debug_warning("reduced_time_ms(%d)", reduced_time_ms);
318 } while ((reduced_time_ms -= INTERVAL_MS) >= 0);
320 debug_error("focus_loop is not running for timeout_ms(%d), focus_loop(%p) ", timeout_ms, focus_loop);
322 return MM_ERROR_SOUND_INTERNAL;
325 static void _focus_open_callback(int index, bool is_for_watching)
332 if (index < 0 || index >= FOCUS_HANDLE_MAX) {
333 debug_error("Invalid focus handle index [%d]", index);
337 if (is_for_watching) {
338 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d.wch",
339 g_focus_sound_handle[index].focus_pid,
340 g_focus_sound_handle[index].handle);
342 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d",
343 g_focus_sound_handle[index].focus_pid,
344 g_focus_sound_handle[index].handle);
346 unlink_if_symbolic_link(filename);
348 if (mknod(filename, S_IFIFO|0666, 0))
349 debug_error("mknod(%s) failure, errno(%d)", filename, errno);
351 g_focus_sound_handle[index].focus_fd = open(filename, O_RDWR|O_NONBLOCK);
352 if (g_focus_sound_handle[index].focus_fd == -1) {
353 debug_error("Open fail : index(%d), file(%s) open error(%d)", index, filename, errno);
355 debug_msg("Open success : index(%d), file(%s), fd(%d)",
356 index, filename, g_focus_sound_handle[index].focus_fd);
361 if (is_for_watching) {
362 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d.wchr",
363 g_focus_sound_handle[index].focus_pid,
364 g_focus_sound_handle[index].handle);
366 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%dr",
367 g_focus_sound_handle[index].focus_pid,
368 g_focus_sound_handle[index].handle);
371 if (mknod(filename, S_IFIFO | 0666, 0))
372 debug_error("mknod(%s) failure, errno(%d)", filename, errno);
380 void _focus_close_callback(int index, bool is_for_watching)
382 char *filename = NULL;
386 if (index < 0 || index >= FOCUS_HANDLE_MAX) {
387 debug_error("Invalid focus handle index [%d]", index);
391 if (g_focus_sound_handle[index].focus_fd < 0) {
392 debug_error("Close fail : index(%d)", index);
394 close(g_focus_sound_handle[index].focus_fd);
395 debug_msg("Close Success : index(%d)", index);
398 if (is_for_watching) {
399 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d.wch",
400 g_focus_sound_handle[index].focus_pid,
401 g_focus_sound_handle[index].handle);
403 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d",
404 g_focus_sound_handle[index].focus_pid,
405 g_focus_sound_handle[index].handle);
407 if (remove(filename)) {
408 debug_warning("remove(%s) failure (focus_server probably removed it in advance), errno(%d)",
414 if (is_for_watching) {
415 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d.wchr",
416 g_focus_sound_handle[index].focus_pid,
417 g_focus_sound_handle[index].handle);
419 filename = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%dr",
420 g_focus_sound_handle[index].focus_pid,
421 g_focus_sound_handle[index].handle);
423 if (remove(filename)) {
424 debug_warning("remove(%s) failure (focus_server probably removed it in advance), errno(%d)",
433 static GSourceFuncs event_funcs = {
434 .prepare = _focus_fd_prepare,
435 .check = _focus_fd_check,
436 .dispatch = _focus_fd_dispatch,
437 .finalize = _focus_fd_finalize,
440 static bool _focus_add_sound_callback(int index, focus_callback_handler_t focus_cb_handler)
442 FocusSource *fsrc = NULL;
448 src = g_source_new(&event_funcs, sizeof(FocusSource));
450 debug_error("failed to g_source_new for focus source");
454 fsrc = (FocusSource*) src;
456 fsrc->poll_fd.fd = g_focus_sound_handle[index].focus_fd;
457 fsrc->poll_fd.events = (gushort)(POLLIN | POLLPRI);
458 g_source_add_poll(src, &fsrc->poll_fd);
460 g_source_set_callback(src, focus_cb_handler, (gpointer)&g_focus_sound_handle[index], NULL);
462 debug_warning("fsrc(%p), src_funcs(%p), pollfd(%p), fd(%d)",
463 fsrc, &event_funcs, &fsrc->poll_fd, fsrc->poll_fd.fd);
465 fsrc_id = g_source_attach(src, g_main_loop_get_context(g_focus_sound_handle[index].focus_loop));
467 debug_error("failed to attach the source to context");
472 g_focus_sound_handle[index].fsrc = fsrc;
484 static bool _focus_remove_sound_callback(int index)
486 focus_sound_info_t *h = NULL;
488 if (index < 0 || index >= FOCUS_HANDLE_MAX) {
489 debug_error("Invalid focus handle index [%d]", index);
493 h = &g_focus_sound_handle[index];
495 g_source_destroy((GSource *)h->fsrc);
499 h->focus_callback = NULL;
500 h->watch_callback = NULL;
502 debug_msg("callbacks of focus handle[%d] are cleared", index);
507 static void _focus_add_callback(int index, bool is_for_watching)
511 if (index < 0 || index >= FOCUS_HANDLE_MAX) {
512 debug_error("Invalid focus handle index [%d]", index);
516 if (!is_for_watching) {
517 if (!_focus_add_sound_callback(index, _focus_callback_handler))
518 debug_error("failed to _focus_add_sound_callback(%p)", _focus_callback_handler);
520 if (!_focus_add_sound_callback(index, _focus_watch_callback_handler))
521 debug_error("failed to _focus_add_sound_callback(%p)", _focus_watch_callback_handler);
526 static void _focus_remove_callback(int index)
529 if (!_focus_remove_sound_callback(index))
530 debug_error("failed to __focus_remove_sound_callback()");
534 int focus_find_empty_index(int *index)
541 for (i = 0; i < FOCUS_HANDLE_MAX; i++) {
542 if (g_focus_sound_handle[i].is_used == false) {
543 g_focus_sound_handle[i].is_used = true;
544 debug_log("use index[%d]", i);
548 if (i == FOCUS_HANDLE_MAX) {
549 debug_error("could not find empty slot, it is full");
558 int focus_find_index_by_handle(int handle)
561 for (i = 0; i < FOCUS_HANDLE_MAX; i++) {
562 if (g_focus_sound_handle[i].focus_callback && handle == g_focus_sound_handle[i].handle) {
563 /* debug_msg("found index(%d) for handle(%d)", i, handle);*/
564 return (handle == FOCUS_HANDLE_INIT_VAL) ? -1 : i;
570 int focus_watch_find_index_by_handle(int handle)
573 for (i = 0; i < FOCUS_HANDLE_MAX; i++) {
574 if (g_focus_sound_handle[i].watch_callback && handle == g_focus_sound_handle[i].handle) {
575 /* debug_msg("found index(%d) for watch handle(%d)", i, handle);*/
576 return (handle == FOCUS_HANDLE_INIT_VAL) ? -1 : i;
582 #define LOOP_RUNNING_WAIT_TIME_MS 2500
583 int focus_init_context(int index, bool is_for_watching)
585 int ret = MM_ERROR_NONE;
586 GMainContext *focus_context;
587 GThread **focus_cb_thread = NULL;
588 char *focus_cb_thread_name;
592 if (index < 0 || index >= FOCUS_HANDLE_MAX) {
593 debug_error("index(%d) is not valid", index);
594 return MM_ERROR_INVALID_ARGUMENT;
597 focus_context = g_main_context_new();
598 g_focus_sound_handle[index].focus_loop = g_main_loop_new(focus_context, FALSE);
599 g_main_context_unref(focus_context); /* FYI, this context resource will be released when calling g_main_loop_unref() */
600 if (g_focus_sound_handle[index].focus_loop == NULL) {
601 debug_error("could not create mainloop..");
605 if (is_for_watching) {
606 focus_cb_thread = &g_focus_sound_handle[index].focus_watch_cb_thread;
607 focus_cb_thread_name = "focus-watch-cb-thread";
609 focus_cb_thread = &g_focus_sound_handle[index].focus_cb_thread;
610 focus_cb_thread_name = "focus-cb-thread";
613 *focus_cb_thread = g_thread_new(focus_cb_thread_name,
615 g_focus_sound_handle[index].focus_loop);
616 if (*focus_cb_thread == NULL) {
617 debug_error("could not create thread..");
621 debug_warning("focus cb thread[%p] with mainloop[%p] is created for index(%d), is_for_watching(%d)",
622 *focus_cb_thread, g_focus_sound_handle[index].focus_loop, index, is_for_watching);
624 if ((ret = _focus_loop_is_running_timed_wait(g_focus_sound_handle[index].focus_loop, LOOP_RUNNING_WAIT_TIME_MS))) {
625 debug_error("failed to _focus_loop_is_running_timed_wait(), ret[0x%x]", ret);
631 return MM_ERROR_NONE;
634 if (g_focus_sound_handle[index].focus_loop) {
635 if (*focus_cb_thread) {
636 g_main_loop_quit(g_focus_sound_handle[index].focus_loop);
637 g_thread_join(*focus_cb_thread);
638 debug_warning("after thread join, thread[%p], mainloop[%p] for index(%d), is_for_watching(%d)",
639 *focus_cb_thread, g_focus_sound_handle[index].focus_loop, index, is_for_watching);
640 *focus_cb_thread = NULL;
642 g_main_loop_unref(g_focus_sound_handle[index].focus_loop);
643 g_focus_sound_handle[index].focus_loop = NULL;
645 return MM_ERROR_SOUND_INTERNAL;
648 void focus_deinit_context(int index, bool is_for_watching)
650 GThread **focus_cb_thread = NULL;
654 if (index < 0 || index >= FOCUS_HANDLE_MAX) {
655 debug_error("index(%d) is not valid", index);
659 if (!g_focus_sound_handle[index].focus_loop ||
660 (!is_for_watching && !g_focus_sound_handle[index].focus_cb_thread) ||
661 (is_for_watching && !g_focus_sound_handle[index].focus_watch_cb_thread)) {
662 debug_error("focus_loop[%p] or focus_cb_thread[%p] or focus_watch_cb_thread[%p] is null",
663 g_focus_sound_handle[index].focus_loop, g_focus_sound_handle[index].focus_cb_thread,
664 g_focus_sound_handle[index].focus_watch_cb_thread);
668 g_main_loop_quit(g_focus_sound_handle[index].focus_loop);
671 focus_cb_thread = &g_focus_sound_handle[index].focus_watch_cb_thread;
673 focus_cb_thread = &g_focus_sound_handle[index].focus_cb_thread;
674 g_thread_join(*focus_cb_thread);
676 debug_warning("after thread join, thread[%p], mainloop[%p] for index(%d), is_for_watching(%d)",
677 *focus_cb_thread, g_focus_sound_handle[index].focus_loop, index, is_for_watching);
678 g_main_loop_unref(g_focus_sound_handle[index].focus_loop);
679 g_focus_sound_handle[index].focus_loop = NULL;
680 *focus_cb_thread = NULL;
685 void focus_init_callback(int index, bool is_for_watching)
688 _focus_open_callback(index, is_for_watching);
689 _focus_add_callback(index, is_for_watching);
693 void focus_deinit_callback(int index, bool is_for_watching)
696 _focus_remove_callback(index);
697 _focus_close_callback(index, is_for_watching);