keysound: skip hard frequent request of keysound playback
[platform/core/multimedia/libmm-sound.git] / mm_sound_focus_private.c
1 /*
2  * libmm-sound
3  *
4  * Copyright (c) 2017 Samsung Electronics Co., Ltd. All rights reserved.
5  *
6  * Contact: Sangchul Lee <sc11.lee@samsung.com>
7  *
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
11  *
12  * http://www.apache.org/licenses/LICENSE-2.0
13  *
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.
19  *
20  */
21
22 #include <stdio.h>
23 #include <stdbool.h>
24 #include <string.h>
25 #include <pthread.h>
26 #include <glib.h>
27 #include <poll.h>
28 #include <fcntl.h>
29 #include <sys/stat.h>
30 #include <unistd.h>
31 #include <stdlib.h>
32
33 #include <mm_debug.h>
34 #include <mm_error.h>
35 #include "include/mm_sound_focus_private.h"
36
37 focus_sound_info_t g_focus_sound_handle[FOCUS_HANDLE_MAX];
38
39 static void unlink_if_symbolic_link(const char *path)
40 {
41         int ret = 0;
42         char *resolved_path = NULL;
43
44         if (path == NULL)
45                 return;
46
47         /* return if it does not exist */
48         if ((ret = access(path, F_OK)))
49                 return;
50
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);
55                         unlink(path);
56                 }
57                 free(resolved_path);
58         } else {
59                 char str_error[256];
60                 strerror_r(errno, str_error, sizeof(str_error));
61                 debug_warning("failed to realpath() for path:%s, err:%s", path, str_error);
62         }
63 }
64
65 static gpointer _focus_thread_func(gpointer data)
66 {
67         unsigned int thread_id = (unsigned int)pthread_self();
68         GMainLoop *focus_loop = (GMainLoop*)data;
69
70         debug_warning(">>> thread id(%u), mainloop[%p]", thread_id, focus_loop);
71         if (focus_loop)
72                 g_main_loop_run(focus_loop);
73         debug_warning("<<< quit : thread id(%u), mainloop[%p]", thread_id, focus_loop);
74
75         return NULL;
76 }
77
78 static gboolean _focus_fd_prepare(GSource *source, gint *timeout)
79 {
80 #ifdef __DEBUG__
81         debug_warning("[ PREPARE : %p, (%p, %d)", source, timeout, timeout ? *timeout : -1);
82 #endif
83         return FALSE;
84 }
85
86 static gboolean _focus_fd_check(GSource * source)
87 {
88         FocusSource* fsource = (FocusSource *)source;
89
90         if (!fsource) {
91                 debug_error("GSource is null");
92                 return FALSE;
93         }
94 #ifdef __DEBUG__
95         debug_warning("CHECK : %p, 0x%x ]", source, fsource->poll_fd.revents);
96 #endif
97         if (fsource->poll_fd.revents & (POLLIN | POLLPRI))
98                 return TRUE;
99         else
100                 return FALSE;
101 }
102
103 static gboolean _focus_fd_dispatch(GSource *source, GSourceFunc callback, gpointer user_data)
104 {
105         debug_warning("*** DISPATCH : %p, (%p, %p)", source, callback, user_data);
106         return callback(user_data);
107 }
108
109 static void _focus_fd_finalize(GSource *source)
110 {
111         debug_warning("### FINALIZE : %p", source);
112 }
113
114 static gboolean _focus_callback_handler(gpointer user_data)
115 {
116         focus_sound_info_t *focus_handle = (focus_sound_info_t *)user_data;
117         GPollFD *poll_fd;
118         int count;
119         int tid = 0;
120         focus_cb_data_lib cb_data;
121
122         debug_msg(">>> thread id(%u)", (unsigned int)pthread_self());
123
124         if (!focus_handle) {
125                 debug_error("focus_handle is null");
126                 return G_SOURCE_REMOVE;
127         }
128         if (!focus_handle->fsrc) {
129                 debug_error("fsrc is null");
130                 return G_SOURCE_REMOVE;
131         }
132         if (focus_handle->is_destroying) {
133                 debug_warning("focus_handle is destroying now, skip it");
134                 return G_SOURCE_REMOVE;
135         }
136
137         g_mutex_lock(&focus_handle->focus_lock);
138
139         poll_fd = &focus_handle->fsrc->poll_fd;
140         debug_msg("focus_handle(%p), poll_fd(%p)", focus_handle, poll_fd);
141
142         memset(&cb_data, 0, sizeof(focus_cb_data_lib));
143
144         if (poll_fd->revents & (POLLIN | POLLPRI)) {
145                 int changed_state = -1;
146
147                 count = read(poll_fd->fd, &cb_data, sizeof(cb_data));
148                 if (count < 0) {
149                         char str_error[256];
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;
154                 }
155                 changed_state = cb_data.state;
156
157                 tid = focus_handle->focus_pid;
158
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;
166                         }
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]");
171                 }
172                 {
173                         int rett = 0;
174                         int tmpfd = -1;
175                         unsigned int buf = 0;
176                         char *filename2 = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%dr", focus_handle->focus_pid, cb_data.handle);
177
178                         unlink_if_symbolic_link(filename2);
179                         tmpfd = open(filename2, O_WRONLY | O_NONBLOCK);
180                         if (tmpfd < 0) {
181                                 char str_error[256];
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);
185                                 g_free(filename2);
186                                 g_mutex_unlock(&focus_handle->focus_lock);
187                                 return G_SOURCE_CONTINUE;
188                         }
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));
193                         close(tmpfd);
194                         g_free(filename2);
195                         debug_msg("[RETCB] tid(%d) finishing CB (write=%d)", tid, rett);
196                 }
197         }
198
199         g_mutex_unlock(&focus_handle->focus_lock);
200
201         debug_fleave();
202
203         return G_SOURCE_CONTINUE;
204 }
205
206 static gboolean _focus_watch_callback_handler(gpointer user_data)
207 {
208         focus_sound_info_t *focus_handle = (focus_sound_info_t *)user_data;
209         GPollFD *poll_fd;
210         int count;
211         int tid = 0;
212         focus_cb_data_lib cb_data;
213
214         debug_msg(">>> thread id(%u)", (unsigned int)pthread_self());
215
216         if (!focus_handle) {
217                 debug_error("focus_handle is null");
218                 return G_SOURCE_REMOVE;
219         }
220         if (focus_handle->unset_watch_callback_requested) {
221                 debug_warning("unset_watch_callback_requested..");
222                 return G_SOURCE_REMOVE;
223         }
224         if (!focus_handle->fsrc) {
225                 debug_error("fsrc is null");
226                 return G_SOURCE_REMOVE;
227         }
228
229         poll_fd = &focus_handle->fsrc->poll_fd;
230         debug_msg("focus_handle(%p), poll_fd(%p)", focus_handle, poll_fd);
231
232         memset(&cb_data, 0, sizeof(focus_cb_data_lib));
233
234         if (poll_fd->revents & (POLLIN | POLLPRI)) {
235                 count = read(poll_fd->fd, &cb_data, sizeof(cb_data));
236                 if (count < 0) {
237                         char str_error[256];
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;
241                 }
242
243                 if (!focus_handle->is_used) {
244                         debug_warning("unsetting watch calllback has been already requested");
245                         goto SKIP_CB_AND_RET;
246                 }
247
248                 g_mutex_lock(&focus_handle->focus_lock);
249
250                 tid = focus_handle->focus_pid;
251
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);
254
255                 if (focus_handle->watch_callback == NULL) {
256                         debug_warning("watch callback is null..");
257                         goto SKIP_CB_AND_RET;
258                 }
259
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]");
264
265 SKIP_CB_AND_RET:
266                 {
267                         int rett = 0;
268                         int tmpfd = -1;
269                         int buf = -1;
270                         char *filename2 = g_strdup_printf(FOCUS_PATH_PREFIX"%d.%d.wchr", focus_handle->focus_pid, cb_data.handle);
271
272                         unlink_if_symbolic_link(filename2);
273                         tmpfd = open(filename2, O_WRONLY | O_NONBLOCK);
274                         if (tmpfd < 0) {
275                                 char str_error[256];
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);
279                                 g_free(filename2);
280                                 if (focus_handle->is_used)
281                                         g_mutex_unlock(&focus_handle->focus_lock);
282                                 return G_SOURCE_CONTINUE;
283                         }
284                         buf = cb_data.handle;
285                         rett = write(tmpfd, &buf, sizeof(buf));
286                         close(tmpfd);
287                         g_free(filename2);
288                         debug_msg("[RETCB] tid(%d) finishing CB (write=%d)", tid, rett);
289                 }
290         }
291
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);
295         }
296
297         debug_fleave();
298
299         return G_SOURCE_CONTINUE;
300 }
301
302 #define INTERVAL_MS 20
303 static int _focus_loop_is_running_timed_wait(GMainLoop *focus_loop, int timeout_ms)
304 {
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;
309         }
310
311         do {
312                 if (g_main_loop_is_running(focus_loop))
313                         return MM_ERROR_NONE;
314
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);
319
320         debug_error("focus_loop is not running for timeout_ms(%d), focus_loop(%p) ", timeout_ms, focus_loop);
321
322         return MM_ERROR_SOUND_INTERNAL;
323 }
324
325 static void _focus_open_callback(int index, bool is_for_watching)
326 {
327         mode_t pre_mask;
328         char *filename;
329
330         debug_fenter();
331
332         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
333                 debug_error("Invalid focus handle index [%d]", index);
334                 return;
335         }
336
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);
341         } else {
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);
345         }
346         unlink_if_symbolic_link(filename);
347         pre_mask = umask(0);
348         if (mknod(filename, S_IFIFO|0666, 0))
349                 debug_error("mknod(%s) failure, errno(%d)", filename, errno);
350         umask(pre_mask);
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);
354         } else {
355                 debug_msg("Open success : index(%d), file(%s), fd(%d)",
356                                 index, filename, g_focus_sound_handle[index].focus_fd);
357         }
358         g_free(filename);
359         filename = NULL;
360
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);
365         } else {
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);
369         }
370         pre_mask = umask(0);
371         if (mknod(filename, S_IFIFO | 0666, 0))
372                 debug_error("mknod(%s) failure, errno(%d)", filename, errno);
373         umask(pre_mask);
374         g_free(filename);
375         filename = NULL;
376
377         debug_fleave();
378 }
379
380 void _focus_close_callback(int index, bool is_for_watching)
381 {
382         char *filename = NULL;
383
384         debug_fenter();
385
386         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
387                 debug_error("Invalid focus handle index [%d]", index);
388                 return;
389         }
390
391         if (g_focus_sound_handle[index].focus_fd < 0) {
392                 debug_error("Close fail : index(%d)", index);
393         } else {
394                 close(g_focus_sound_handle[index].focus_fd);
395                 debug_msg("Close Success : index(%d)", index);
396         }
397
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);
402         } else {
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);
406         }
407         if (remove(filename)) {
408                 debug_warning("remove(%s) failure (focus_server probably removed it in advance), errno(%d)",
409                                         filename, errno);
410         }
411         g_free(filename);
412         filename = NULL;
413
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);
418         } else {
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);
422         }
423         if (remove(filename)) {
424                 debug_warning("remove(%s) failure (focus_server probably removed it in advance), errno(%d)",
425                                         filename, errno);
426         }
427         g_free(filename);
428         filename = NULL;
429
430         debug_fleave();
431 }
432
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,
438 };
439
440 static bool _focus_add_sound_callback(int index, focus_callback_handler_t focus_cb_handler)
441 {
442         FocusSource *fsrc = NULL;
443         GSource *src = NULL;
444         guint fsrc_id = 0;
445
446         debug_fenter();
447
448         src = g_source_new(&event_funcs, sizeof(FocusSource));
449         if (!src) {
450                 debug_error("failed to g_source_new for focus source");
451                 goto ERROR;
452         }
453
454         fsrc = (FocusSource*) src;
455
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);
459
460         g_source_set_callback(src, focus_cb_handler, (gpointer)&g_focus_sound_handle[index], NULL);
461
462         debug_warning("fsrc(%p), src_funcs(%p), pollfd(%p), fd(%d)",
463                                 fsrc, &event_funcs, &fsrc->poll_fd, fsrc->poll_fd.fd);
464
465         fsrc_id = g_source_attach(src, g_main_loop_get_context(g_focus_sound_handle[index].focus_loop));
466         if (!fsrc_id) {
467                 debug_error("failed to attach the source to context");
468                 goto ERROR;
469         }
470         g_source_unref(src);
471
472         g_focus_sound_handle[index].fsrc = fsrc;
473
474         debug_fleave();
475         return true;
476
477 ERROR:
478         if (src)
479                 g_source_unref(src);
480
481         return false;
482 }
483
484 static bool _focus_remove_sound_callback(int index)
485 {
486         focus_sound_info_t *h = NULL;
487
488         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
489                 debug_error("Invalid focus handle index [%d]", index);
490                 return false;
491         }
492
493         h = &g_focus_sound_handle[index];
494         if (h->fsrc) {
495                 g_source_destroy((GSource *)h->fsrc);
496                 h->fsrc = NULL;
497         }
498
499         h->focus_callback = NULL;
500         h->watch_callback = NULL;
501
502         debug_msg("callbacks of focus handle[%d] are cleared", index);
503
504         return true;
505 }
506
507 static void _focus_add_callback(int index, bool is_for_watching)
508 {
509         debug_fenter();
510
511         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
512                 debug_error("Invalid focus handle index [%d]", index);
513                 return;
514         }
515
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);
519         } else {
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);
522         }
523         debug_fleave();
524 }
525
526 static void _focus_remove_callback(int index)
527 {
528         debug_fenter();
529         if (!_focus_remove_sound_callback(index))
530                 debug_error("failed to __focus_remove_sound_callback()");
531         debug_fleave();
532 }
533
534 int focus_find_empty_index(int *index)
535 {
536         int i = 0;
537
538         if (!index)
539                 return -1;
540
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);
545                         break;
546                 }
547         }
548         if (i == FOCUS_HANDLE_MAX) {
549                 debug_error("could not find empty slot, it is full");
550                 return -1;
551         }
552
553         *index = i;
554
555         return 0;
556 }
557
558 int focus_find_index_by_handle(int handle)
559 {
560         int i = 0;
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;
565                 }
566         }
567         return -1;
568 }
569
570 int focus_watch_find_index_by_handle(int handle)
571 {
572         int i = 0;
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;
577                 }
578         }
579         return -1;
580 }
581
582 #define LOOP_RUNNING_WAIT_TIME_MS 2500
583 int focus_init_context(int index, bool is_for_watching)
584 {
585         int ret = MM_ERROR_NONE;
586         GMainContext *focus_context;
587         GThread **focus_cb_thread = NULL;
588         char *focus_cb_thread_name;
589
590         debug_fenter();
591
592         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
593                 debug_error("index(%d) is not valid", index);
594                 return MM_ERROR_INVALID_ARGUMENT;
595         }
596
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..");
602                 goto ERROR;
603         }
604
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";
608         } else {
609                 focus_cb_thread = &g_focus_sound_handle[index].focus_cb_thread;
610                 focus_cb_thread_name = "focus-cb-thread";
611         }
612
613         *focus_cb_thread = g_thread_new(focus_cb_thread_name,
614                                                                         _focus_thread_func,
615                                                                         g_focus_sound_handle[index].focus_loop);
616         if (*focus_cb_thread == NULL) {
617                 debug_error("could not create thread..");
618                 goto ERROR;
619         }
620
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);
623
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);
626                 goto ERROR;
627         }
628
629         debug_fleave();
630
631         return MM_ERROR_NONE;
632
633 ERROR:
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;
641                 }
642                 g_main_loop_unref(g_focus_sound_handle[index].focus_loop);
643                 g_focus_sound_handle[index].focus_loop = NULL;
644         }
645         return MM_ERROR_SOUND_INTERNAL;
646 }
647
648 void focus_deinit_context(int index, bool is_for_watching)
649 {
650         GThread **focus_cb_thread = NULL;
651
652         debug_fenter();
653
654         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
655                 debug_error("index(%d) is not valid", index);
656                 return;
657         }
658
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);
665                 return;
666         }
667
668         g_main_loop_quit(g_focus_sound_handle[index].focus_loop);
669
670         if (is_for_watching)
671                 focus_cb_thread = &g_focus_sound_handle[index].focus_watch_cb_thread;
672         else
673                 focus_cb_thread = &g_focus_sound_handle[index].focus_cb_thread;
674         g_thread_join(*focus_cb_thread);
675
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;
681
682         debug_fleave();
683 }
684
685 void focus_init_callback(int index, bool is_for_watching)
686 {
687         debug_fenter();
688         _focus_open_callback(index, is_for_watching);
689         _focus_add_callback(index, is_for_watching);
690         debug_fleave();
691 }
692
693 void focus_deinit_callback(int index, bool is_for_watching)
694 {
695         debug_fenter();
696         _focus_remove_callback(index);
697         _focus_close_callback(index, is_for_watching);
698         debug_fleave();
699 }