Manage focus cb thread and focus watch cb thread separately
[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->pollfd.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_log(">>> thread id(%u)", (unsigned int)pthread_self());
123
124         if (!focus_handle) {
125                 debug_error("focus_handle is null");
126                 return G_SOURCE_CONTINUE;
127         }
128         poll_fd = &focus_handle->fsrc->poll_fd;
129         debug_log("focus_handle(%p), poll_fd(%p)", focus_handle, poll_fd);
130
131         memset(&cb_data, 0, sizeof(focus_cb_data_lib));
132
133         if (poll_fd->revents & (POLLIN | POLLPRI)) {
134                 int changed_state = -1;
135
136                 count = read(poll_fd->fd, &cb_data, sizeof(cb_data));
137                 if (count < 0) {
138                         char str_error[256];
139                         strerror_r(errno, str_error, sizeof(str_error));
140                         debug_error("GpollFD read fail, errno=%d(%s)", errno, str_error);
141                         return G_SOURCE_CONTINUE;
142                 }
143                 changed_state = cb_data.state;
144
145                 g_mutex_lock(&focus_handle->focus_lock);
146
147                 tid = focus_handle->focus_pid;
148
149                 if (changed_state != -1) {
150                         debug_msg("Got and start CB : TID(%d), handle(%d), type(%d), state(%d,(DEACTIVATED(0)/ACTIVATED(1)), trigger(%s)",
151                                         tid, cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type);
152                         if (focus_handle->focus_callback == NULL) {
153                                         debug_error("focus callback is null..");
154                                         g_mutex_unlock(&focus_handle->focus_lock);
155                                         return G_SOURCE_CONTINUE;
156                         }
157                         debug_msg("[CALLBACK(%p) START]", focus_handle->focus_callback);
158                         (focus_handle->focus_callback)(cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type,
159                                                                                 cb_data.option, cb_data.ext_info, focus_handle->user_data);
160                         debug_msg("[CALLBACK END]");
161                 }
162                 {
163                         int rett = 0;
164                         int tmpfd = -1;
165                         unsigned int buf = 0;
166                         char *filename2 = g_strdup_printf("/tmp/FOCUS.%d.%dr", focus_handle->focus_pid, cb_data.handle);
167
168                         unlink_if_symbolic_link(filename2);
169                         tmpfd = open(filename2, O_WRONLY | O_NONBLOCK);
170                         if (tmpfd < 0) {
171                                 char str_error[256];
172                                 strerror_r(errno, str_error, sizeof(str_error));
173                                 debug_warning("[RETCB][Failed(May Server Close First)]tid(%d) fd(%d) %s errno=%d(%s)",
174                                                         tid, tmpfd, filename2, errno, str_error);
175                                 g_free(filename2);
176                                 g_mutex_unlock(&focus_handle->focus_lock);
177                                 return G_SOURCE_CONTINUE;
178                         }
179                         /* buf contains data as below,
180                          * |<--12bits--><--4bits (reacquisition)--><--16bits (handle)-->| */
181                         buf = (unsigned int)((0x0000ffff & cb_data.handle) | (focus_handle->auto_reacquire << 16));
182                         rett = write(tmpfd, &buf, sizeof(buf));
183                         close(tmpfd);
184                         g_free(filename2);
185                         debug_msg("[RETCB] tid(%d) finishing CB (write=%d)", tid, rett);
186                 }
187         }
188
189         g_mutex_unlock(&focus_handle->focus_lock);
190
191         debug_fleave();
192
193         return G_SOURCE_CONTINUE;
194 }
195
196 static gboolean _focus_watch_callback_handler(gpointer user_data)
197 {
198         focus_sound_info_t *focus_handle = (focus_sound_info_t *)user_data;
199         GPollFD *poll_fd;
200         int count;
201         int tid = 0;
202         focus_cb_data_lib cb_data;
203
204         debug_log(">>> thread id(%u)", (unsigned int)pthread_self());
205
206         if (!focus_handle) {
207                 debug_error("focus_handle is null");
208                 return G_SOURCE_CONTINUE;
209         }
210         poll_fd = &focus_handle->fsrc->poll_fd;
211         debug_log("focus_handle(%p), poll_fd(%p)", focus_handle, poll_fd);
212
213         memset(&cb_data, 0, sizeof(focus_cb_data_lib));
214
215         if (poll_fd->revents & (POLLIN | POLLPRI)) {
216                 count = read(poll_fd->fd, &cb_data, sizeof(cb_data));
217                 if (count < 0) {
218                         char str_error[256];
219                         strerror_r(errno, str_error, sizeof(str_error));
220                         debug_error("GpollFD read fail, errno=%d(%s)", errno, str_error);
221                         return G_SOURCE_CONTINUE;
222                 }
223
224                 if (!focus_handle->is_used) {
225                         debug_warning("unsetting watch calllback has been already requested");
226                         goto SKIP_CB_AND_RET;
227                 }
228
229                 g_mutex_lock(&focus_handle->focus_lock);
230
231                 tid = focus_handle->focus_pid;
232
233                 debug_msg("Got and start CB : TID(%d), handle(%d), type(%d), state(%d,(DEACTIVATED(0)/ACTIVATED(1)), trigger(%s)",
234                                 tid, cb_data.handle,  cb_data.type, cb_data.state, cb_data.stream_type);
235
236                 if (focus_handle->watch_callback == NULL) {
237                         debug_warning("watch callback is null..");
238                         goto SKIP_CB_AND_RET;
239                 }
240                 if (focus_handle->unset_watch_callback_requested == true) {
241                         debug_warning("unset_watch_callback_requested..");
242                         goto SKIP_CB_AND_RET;
243                 }
244
245                 debug_msg("[CALLBACK(%p) START]", focus_handle->watch_callback);
246                 (focus_handle->watch_callback)(cb_data.handle, cb_data.type, cb_data.state, cb_data.stream_type,
247                                                                         cb_data.ext_info, focus_handle->user_data);
248                 debug_msg("[CALLBACK END]");
249
250 SKIP_CB_AND_RET:
251                 {
252                         int rett = 0;
253                         int tmpfd = -1;
254                         int buf = -1;
255                         char *filename2 = g_strdup_printf("/tmp/FOCUS.%d.%d.wchr", focus_handle->focus_pid, cb_data.handle);
256
257                         unlink_if_symbolic_link(filename2);
258                         tmpfd = open(filename2, O_WRONLY | O_NONBLOCK);
259                         if (tmpfd < 0) {
260                                 char str_error[256];
261                                 strerror_r(errno, str_error, sizeof(str_error));
262                                 debug_warning("[RETCB][Failed(May Server Close First)]tid(%d) fd(%d) %s errno=%d(%s)",
263                                                         tid, tmpfd, filename2, errno, str_error);
264                                 g_free(filename2);
265                                 if (focus_handle->is_used)
266                                         g_mutex_unlock(&focus_handle->focus_lock);
267                                 return G_SOURCE_CONTINUE;
268                         }
269                         buf = cb_data.handle;
270                         rett = write(tmpfd, &buf, sizeof(buf));
271                         close(tmpfd);
272                         g_free(filename2);
273                         debug_msg("[RETCB] tid(%d) finishing CB (write=%d)", tid, rett);
274                 }
275         }
276
277         if (focus_handle->is_used) {
278                 debug_msg("unlock focus_lock = %p", &focus_handle->focus_lock);
279                 g_mutex_unlock(&focus_handle->focus_lock);
280         }
281
282         debug_fleave();
283
284         return G_SOURCE_CONTINUE;
285 }
286
287 #define INTERVAL_MS 20
288 static int _focus_loop_is_running_timed_wait(GMainLoop *focus_loop, int timeout_ms)
289 {
290         int reduced_time_ms = timeout_ms;
291         if (!focus_loop || timeout_ms < 0) {
292                 debug_error("invalid argument, focus_loop(%p), timeout_ms(%d)", focus_loop, timeout_ms);
293                 return MM_ERROR_INVALID_ARGUMENT;
294         }
295
296         do {
297                 if (g_main_loop_is_running(focus_loop))
298                         return MM_ERROR_NONE;
299
300                 usleep(INTERVAL_MS * 1000);
301                 if (reduced_time_ms < timeout_ms)
302                         debug_warning("reduced_time_ms(%d)", reduced_time_ms);
303         } while ((reduced_time_ms -= INTERVAL_MS) >= 0);
304
305         debug_error("focus_loop is not running for timeout_ms(%d), focus_loop(%p) ", timeout_ms, focus_loop);
306
307         return MM_ERROR_SOUND_INTERNAL;
308 }
309
310 static void _focus_open_callback(int index, bool is_for_watching)
311 {
312         mode_t pre_mask;
313         char *filename;
314
315         debug_fenter();
316
317         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
318                 debug_error("Invalid focus handle index [%d]", index);
319                 return;
320         }
321
322         if (is_for_watching) {
323                 filename = g_strdup_printf("/tmp/FOCUS.%d.%d.wch",
324                                                                 g_focus_sound_handle[index].focus_pid,
325                                                                 g_focus_sound_handle[index].handle);
326         } else {
327                 filename = g_strdup_printf("/tmp/FOCUS.%d.%d",
328                                                                 g_focus_sound_handle[index].focus_pid,
329                                                                 g_focus_sound_handle[index].handle);
330         }
331         unlink_if_symbolic_link(filename);
332         pre_mask = umask(0);
333         if (mknod(filename, S_IFIFO|0666, 0))
334                 debug_error("mknod() failure, errno(%d)", errno);
335         umask(pre_mask);
336         g_focus_sound_handle[index].focus_fd = open(filename, O_RDWR|O_NONBLOCK);
337         if (g_focus_sound_handle[index].focus_fd == -1) {
338                 debug_error("Open fail : index(%d), file open error(%d)", index, errno);
339         } else {
340                 debug_log("Open success : index(%d), filename(%s), fd(%d)",
341                                 index, filename, g_focus_sound_handle[index].focus_fd);
342         }
343         g_free(filename);
344         filename = NULL;
345
346         if (is_for_watching) {
347                 filename = g_strdup_printf("/tmp/FOCUS.%d.%d.wchr",
348                                                                         g_focus_sound_handle[index].focus_pid,
349                                                                         g_focus_sound_handle[index].handle);
350         } else {
351                 filename = g_strdup_printf("/tmp/FOCUS.%d.%dr",
352                                                                         g_focus_sound_handle[index].focus_pid,
353                                                                         g_focus_sound_handle[index].handle);
354         }
355         pre_mask = umask(0);
356         if (mknod(filename, S_IFIFO | 0666, 0))
357                 debug_error("mknod() failure, errno(%d)", errno);
358         umask(pre_mask);
359         g_free(filename);
360         filename = NULL;
361
362         debug_fleave();
363 }
364
365 void _focus_close_callback(int index, bool is_for_watching)
366 {
367         char *filename = NULL;
368
369         debug_fenter();
370
371         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
372                 debug_error("Invalid focus handle index [%d]", index);
373                 return;
374         }
375
376         if (g_focus_sound_handle[index].focus_fd < 0) {
377                 debug_error("Close fail : index(%d)", index);
378         } else {
379                 close(g_focus_sound_handle[index].focus_fd);
380                 debug_log("Close Success : index(%d)", index);
381         }
382
383         if (is_for_watching) {
384                 filename = g_strdup_printf("/tmp/FOCUS.%d.%d.wch",
385                                                                 g_focus_sound_handle[index].focus_pid,
386                                                                 g_focus_sound_handle[index].handle);
387         } else {
388                 filename = g_strdup_printf("/tmp/FOCUS.%d.%d",
389                                                                 g_focus_sound_handle[index].focus_pid,
390                                                                 g_focus_sound_handle[index].handle);
391         }
392         if (remove(filename)) {
393                 debug_warning("remove(%s) failure (focus_server probably removed it in advance), errno(%d)",
394                                         filename, errno);
395         }
396         g_free(filename);
397         filename = NULL;
398
399         if (is_for_watching) {
400                 filename = g_strdup_printf("/tmp/FOCUS.%d.%d.wchr",
401                                                                         g_focus_sound_handle[index].focus_pid,
402                                                                         g_focus_sound_handle[index].handle);
403         } else {
404                 filename = g_strdup_printf("/tmp/FOCUS.%d.%dr",
405                                                                         g_focus_sound_handle[index].focus_pid,
406                                                                         g_focus_sound_handle[index].handle);
407         }
408         if (remove(filename)) {
409                 debug_warning("remove(%s) failure (focus_server probably removed it in advance), errno(%d)",
410                                         filename, errno);
411         }
412         g_free(filename);
413         filename = NULL;
414
415         debug_fleave();
416 }
417
418 static GSourceFuncs event_funcs = {
419         .prepare = _focus_fd_prepare,
420         .check = _focus_fd_check,
421         .dispatch = _focus_fd_dispatch,
422         .finalize = _focus_fd_finalize,
423 };
424
425 static bool _focus_add_sound_callback(int index, focus_callback_handler_t focus_cb_handler)
426 {
427         FocusSource *fsrc = NULL;
428         GSource *src = NULL;
429         guint fsrc_id = 0;
430
431         debug_fenter();
432
433         g_mutex_init(&g_focus_sound_handle[index].focus_lock);
434
435         src = g_source_new(&event_funcs, sizeof(FocusSource));
436         if (!src) {
437                 debug_error("failed to g_source_new for focus source");
438                 goto ERROR;
439         }
440
441         fsrc = (FocusSource*) src;
442
443         fsrc->poll_fd.fd = g_focus_sound_handle[index].focus_fd;
444         fsrc->poll_fd.events = (gushort)(POLLIN | POLLPRI);
445         g_source_add_poll(src, &fsrc->poll_fd);
446
447         g_source_set_callback(src, focus_cb_handler, (gpointer)&g_focus_sound_handle[index], NULL);
448
449         debug_warning("fsrc(%p), src_funcs(%p), pollfd(%p), fd(%d)",
450                                 fsrc, &event_funcs, &fsrc->poll_fd, fsrc->poll_fd.fd);
451
452         fsrc_id = g_source_attach(src, g_main_loop_get_context(g_focus_sound_handle[index].focus_loop));
453         if (!fsrc_id) {
454                 debug_error("failed to attach the source to context");
455                 goto ERROR;
456         }
457         g_source_unref(src);
458
459         g_focus_sound_handle[index].fsrc = fsrc;
460
461         debug_fleave();
462         return true;
463
464 ERROR:
465         if (src)
466                 g_source_unref(src);
467
468         return false;
469 }
470
471 static bool _focus_remove_sound_callback(int index)
472 {
473         focus_sound_info_t *h = NULL;
474
475         debug_fenter();
476
477         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
478                 debug_error("Invalid focus handle index [%d]", index);
479                 return false;
480         }
481
482         h = &g_focus_sound_handle[index];
483         if (h->fsrc) {
484                 g_source_destroy((GSource *)h->fsrc);
485                 h->fsrc = NULL;
486         }
487
488         h->focus_callback = NULL;
489         h->watch_callback = NULL;
490
491         g_mutex_clear(&h->focus_lock);
492
493         debug_fleave();
494
495         return true;
496 }
497
498 static void _focus_add_callback(int index, bool is_for_watching)
499 {
500         debug_fenter();
501
502         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
503                 debug_error("Invalid focus handle index [%d]", index);
504                 return;
505         }
506
507         if (!is_for_watching) {
508                 if (!_focus_add_sound_callback(index, _focus_callback_handler))
509                         debug_error("failed to _focus_add_sound_callback(%p)", _focus_callback_handler);
510         } else {
511                 if (!_focus_add_sound_callback(index, _focus_watch_callback_handler))
512                         debug_error("failed to _focus_add_sound_callback(%p)", _focus_watch_callback_handler);
513         }
514         debug_fleave();
515 }
516
517 static void _focus_remove_callback(int index)
518 {
519         debug_fenter();
520         if (!_focus_remove_sound_callback(index))
521                 debug_error("failed to __focus_remove_sound_callback()");
522         debug_fleave();
523 }
524
525 int focus_find_empty_index(int *index)
526 {
527         int i = 0;
528
529         if (!index)
530                 return -1;
531
532         for (i = 0; i < FOCUS_HANDLE_MAX; i++) {
533                 if (g_focus_sound_handle[i].is_used == false) {
534                         g_focus_sound_handle[i].is_used = true;
535                         debug_log("use index[%d]", i);
536                         break;
537                 }
538         }
539         if (i == FOCUS_HANDLE_MAX) {
540                 debug_error("could not find empty slot, it is full");
541                 return -1;
542         }
543
544         *index = i;
545
546         return 0;
547 }
548
549 int focus_find_index_by_handle(int handle)
550 {
551         int i = 0;
552         for (i = 0; i < FOCUS_HANDLE_MAX; i++) {
553                 if (g_focus_sound_handle[i].focus_callback && handle == g_focus_sound_handle[i].handle) {
554                         /* debug_msg("found index(%d) for handle(%d)", i, handle);*/
555                         return (handle == FOCUS_HANDLE_INIT_VAL) ? -1 : i;
556                 }
557         }
558         return -1;
559 }
560
561 int focus_watch_find_index_by_handle(int handle)
562 {
563         int i = 0;
564         for (i = 0; i < FOCUS_HANDLE_MAX; i++) {
565                 if (g_focus_sound_handle[i].watch_callback && handle == g_focus_sound_handle[i].handle) {
566                         /* debug_msg("found index(%d) for watch handle(%d)", i, handle);*/
567                         return (handle == FOCUS_HANDLE_INIT_VAL) ? -1 : i;
568                 }
569         }
570         return -1;
571 }
572
573 #define LOOP_RUNNING_WAIT_TIME_MS 2500
574 int focus_init_context(int index, bool is_for_watching)
575 {
576         int ret = MM_ERROR_NONE;
577         GMainContext *focus_context;
578         GThread **focus_cb_thread = NULL;
579         char *focus_cb_thread_name;
580
581         debug_fenter();
582
583         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
584                 debug_error("index(%d) is not valid", index);
585                 return MM_ERROR_INVALID_ARGUMENT;
586         }
587
588         focus_context = g_main_context_new();
589         g_focus_sound_handle[index].focus_loop = g_main_loop_new(focus_context, FALSE);
590         g_main_context_unref(focus_context); /* FYI, this context resource will be released when calling g_main_loop_unref() */
591         if (g_focus_sound_handle[index].focus_loop == NULL) {
592                 debug_error("could not create mainloop..");
593                 goto ERROR;
594         }
595
596         if (is_for_watching) {
597                 focus_cb_thread = &g_focus_sound_handle[index].focus_watch_cb_thread;
598                 focus_cb_thread_name = "focus-watch-cb-thread";
599         } else {
600                 focus_cb_thread = &g_focus_sound_handle[index].focus_cb_thread;
601                 focus_cb_thread_name = "focus-cb-thread";
602         }
603
604         *focus_cb_thread = g_thread_new(focus_cb_thread_name,
605                                                                         _focus_thread_func,
606                                                                         g_focus_sound_handle[index].focus_loop);
607         if (*focus_cb_thread == NULL) {
608                 debug_error("could not create thread..");
609                 goto ERROR;
610         }
611
612         debug_warning("focus cb thread[%p] with mainloop[%p] is created for index(%d), is_for_watching(%d)",
613                         *focus_cb_thread, g_focus_sound_handle[index].focus_loop, index, is_for_watching);
614
615         if ((ret = _focus_loop_is_running_timed_wait(g_focus_sound_handle[index].focus_loop, LOOP_RUNNING_WAIT_TIME_MS))) {
616                 debug_error("failed to _focus_loop_is_running_timed_wait(), ret[0x%x]", ret);
617                 goto ERROR;
618         }
619
620         debug_fleave();
621
622         return MM_ERROR_NONE;
623
624 ERROR:
625         if (g_focus_sound_handle[index].focus_loop) {
626                 if (*focus_cb_thread) {
627                         g_main_loop_quit(g_focus_sound_handle[index].focus_loop);
628                         g_thread_join(*focus_cb_thread);
629                         debug_warning("after thread join, thread[%p], mainloop[%p] for index(%d), is_for_watching(%d)",
630                                         *focus_cb_thread, g_focus_sound_handle[index].focus_loop, index, is_for_watching);
631                         *focus_cb_thread = NULL;
632                 }
633                 g_main_loop_unref(g_focus_sound_handle[index].focus_loop);
634                 g_focus_sound_handle[index].focus_loop = NULL;
635         }
636         return MM_ERROR_SOUND_INTERNAL;
637 }
638
639 void focus_deinit_context(int index, bool is_for_watching)
640 {
641         GThread **focus_cb_thread = NULL;
642
643         debug_fenter();
644
645         if (index < 0 || index >= FOCUS_HANDLE_MAX) {
646                 debug_error("index(%d) is not valid", index);
647                 return;
648         }
649
650         if (!g_focus_sound_handle[index].focus_loop ||
651             (!is_for_watching && !g_focus_sound_handle[index].focus_cb_thread) ||
652                 (is_for_watching && !g_focus_sound_handle[index].focus_watch_cb_thread)) {
653                 debug_error("focus_loop[%p] or focus_cb_thread[%p] or focus_watch_cb_thread[%p] is null",
654                                 g_focus_sound_handle[index].focus_loop, g_focus_sound_handle[index].focus_cb_thread,
655                                 g_focus_sound_handle[index].focus_watch_cb_thread);
656                 return;
657         }
658
659         g_main_loop_quit(g_focus_sound_handle[index].focus_loop);
660
661         if (is_for_watching)
662                 focus_cb_thread = &g_focus_sound_handle[index].focus_watch_cb_thread;
663         else
664                 focus_cb_thread = &g_focus_sound_handle[index].focus_cb_thread;
665         g_thread_join(*focus_cb_thread);
666
667         debug_warning("after thread join, thread[%p], mainloop[%p] for index(%d), is_for_watching(%d)",
668                         *focus_cb_thread, g_focus_sound_handle[index].focus_loop, index, is_for_watching);
669         g_main_loop_unref(g_focus_sound_handle[index].focus_loop);
670         g_focus_sound_handle[index].focus_loop = NULL;
671         *focus_cb_thread = NULL;
672
673         debug_fleave();
674 }
675
676 void focus_init_callback(int index, bool is_for_watching)
677 {
678         debug_fenter();
679         _focus_open_callback(index, is_for_watching);
680         _focus_add_callback(index, is_for_watching);
681         debug_fleave();
682 }
683
684 void focus_deinit_callback(int index, bool is_for_watching)
685 {
686         debug_fenter();
687         _focus_remove_callback(index);
688         _focus_close_callback(index, is_for_watching);
689         debug_fleave();
690 }