Avoid deadlock situation between onInterrupt() and unprepare()
[platform/core/api/audio-io.git] / src / cpp / CAudioIO.cpp
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17
18 #include <mm.h>
19 #include <pthread.h>
20 #include <assert.h>
21 #include "CAudioIODef.h"
22
23 using namespace std;
24 using namespace tizen_media_audio;
25
26
27 /**
28  * class CAudioIO
29  */
30 CAudioIO::CAudioIO() :
31     mpAudioSessionHandler(NULL),
32     mpPulseAudioClient(NULL),
33     __mMutex(PTHREAD_MUTEX_INITIALIZER),
34     __mCond(PTHREAD_COND_INITIALIZER),
35     __mIsInit(false),
36     __mForceIgnore(false) {
37     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
38     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
39     mByPolicy = false;
40 }
41
42 CAudioIO::CAudioIO(CAudioInfo& audioInfo) :
43     mpAudioSessionHandler(NULL),
44     mpPulseAudioClient(NULL),
45     __mMutex(PTHREAD_MUTEX_INITIALIZER),
46     __mCond(PTHREAD_COND_INITIALIZER),
47     __mIsInit(false),
48     __mForceIgnore(false) {
49     mAudioInfo = audioInfo;
50     mState = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
51     mStatePrev = CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE;
52     mByPolicy = false;
53 }
54
55 CAudioIO::~CAudioIO() {
56 }
57
58 void CAudioIO::setInit(bool flag) {
59     __mIsInit = flag;
60 }
61
62 bool CAudioIO::isInit() {
63     return __mIsInit;
64 }
65
66 bool CAudioIO::IsReady() {
67     return ((mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING || mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED)? true : false);
68 }
69
70 void CAudioIO::internalLock() throw(CAudioError) {
71     if (__mIsInit == false) {
72         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
73     }
74
75     if (pthread_mutex_lock(&__mMutex) != 0) {
76         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
77     }
78 #ifdef _AUDIO_IO_DEBUG_TIMING_
79     AUDIO_IO_LOGD(COLOR_RED "LOCK" COLOR_END);
80 #endif
81 }
82
83 void CAudioIO::internalUnlock() throw(CAudioError) {
84     if (__mIsInit == false) {
85         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
86     }
87
88     if (pthread_mutex_unlock(&__mMutex) != 0) {
89         THROW_ERROR_MSG(CAudioError::EError::ERROR_INTERNAL_OPERATION, "Failed pthread_mutex_lock()");
90     }
91 #ifdef _AUDIO_IO_DEBUG_TIMING_
92     AUDIO_IO_LOGD(COLOR_GREEN "UNLOCK" COLOR_END);
93 #endif
94 }
95
96 void CAudioIO::internalWait() throw(CAudioError) {
97     if (__mIsInit == false) {
98         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
99     }
100
101 #ifdef _AUDIO_IO_DEBUG_TIMING_
102     AUDIO_IO_LOGD(COLOR_RED "WAIT" COLOR_END);
103 #endif
104
105     pthread_cond_wait(&__mCond, &__mMutex);
106 }
107
108 void CAudioIO::internalSignal() throw(CAudioError) {
109     if (__mIsInit == false) {
110         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
111     }
112
113 #ifdef _AUDIO_IO_DEBUG_TIMING_
114     AUDIO_IO_LOGD(COLOR_GREEN "SIGNAL" COLOR_END);
115 #endif
116
117     pthread_cond_signal(&__mCond);
118 }
119
120 bool CAudioIO::isForceIgnore() {
121     return __mForceIgnore;
122 }
123
124 void CAudioIO::initialize() throw(CAudioError) {
125     if (__mIsInit == true) {
126         return;
127     }
128
129     AUDIO_IO_LOGD("initialize");
130
131     int ret = pthread_mutex_init(&__mMutex, NULL);
132     if (ret != 0) {
133         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_init()");
134     }
135
136     ret = pthread_cond_init(&__mCond, NULL);
137     if (ret != 0) {
138         THROW_ERROR_MSG(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_init()");
139     }
140
141     __mIsInit = true;
142 }
143
144 void CAudioIO::finalize() {
145     if (__mIsInit == false) {
146         return;
147     }
148
149     AUDIO_IO_LOGD("finalize");
150
151     int ret = pthread_mutex_destroy(&__mMutex);
152     if (ret != 0) {
153         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_mutex_destroy() ret:%d", ret);
154     }
155
156     ret = pthread_cond_destroy(&__mCond);
157     if (ret != 0) {
158         THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_OUT_OF_MEMORY, "Failed pthread_cond_destroy() ret:%d", ret);
159     }
160
161     __mIsInit = false;
162 }
163
164 void CAudioIO::onStream(CPulseAudioClient* pClient, size_t length) {
165     assert(__mIsInit == true);
166     assert(pClient != NULL);
167     assert(length > 0);
168
169 #ifdef _AUDIO_IO_DEBUG_TIMING_
170     AUDIO_IO_LOGD("mStreamCallback.onStream(%p), pClient(%p), length(%zu)", mStreamCallback.onStream, pClient, length);
171 #endif
172
173     if (mStreamCallback.onStream != NULL) {
174         mStreamCallback.onStream(length, mStreamCallback.mUserData);
175     }
176 }
177
178 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state, bool byPolicy) {
179     assert(__mIsInit == true);
180     assert(state >= CAudioInfo::EAudioIOState::AUDIO_IO_STATE_NONE && state < CAudioInfo::EAudioIOState::AUDIO_IO_STATE_MAX);
181
182     mStatePrev = mState;
183     mState     = state;
184     mByPolicy  = byPolicy;
185
186     if (mState == mStatePrev)
187         return;
188
189     const char* state_string[] = { "NONE", "IDLE", "RUNNING", "PAUSED" };
190
191     AUDIO_IO_LOGD("previous(%s,%d) ===> current(%s,%d), by_policy(%d)",
192                   state_string[(int)mStatePrev], mStatePrev, state_string[(int)mState], mState, mByPolicy);
193
194     if (mStateChangedCallback.onStateChanged != NULL) {
195         mStateChangedCallback.onStateChanged(mState, mStatePrev, mByPolicy, mStateChangedCallback.mUserData);
196     }
197 }
198
199 void CAudioIO::onStateChanged(CAudioInfo::EAudioIOState state) {
200     onStateChanged(state, false);
201 }
202
203 CAudioInfo::EAudioIOState CAudioIO::getState() {
204     return mState;
205 }
206
207 void CAudioIO::onInterrupt(CAudioSessionHandler* pHandler, int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info) {
208     assert(pHandler);
209
210     int session_option = pHandler->getOptions();
211
212     if (id == -1) {
213         ///////////////////////////////////////
214         // Triggered by 'focus watch callback'
215         ///////////////////////////////////////
216
217         if (session_option & (MM_SESSION_OPTION_PAUSE_OTHERS | MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
218             AUDIO_IO_LOGD("Session option is pausing others or uninterruptible, skip...");
219             return;
220         }
221
222         if (state == FOCUS_IS_RELEASED) {
223             // Focus handle(id) of the other application was released, do resume if possible
224             internalLock();
225             if (mpPulseAudioClient) {
226                 mpPulseAudioClient->cork(false);
227                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
228             }
229             internalUnlock();
230
231             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
232             state = FOCUS_IS_ACQUIRED;
233         } else if (state == FOCUS_IS_ACQUIRED) {
234             // Focus handle(id) of the other application was acquired, do pause if possible
235             internalLock();
236             if (mpPulseAudioClient) {
237                 if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
238                     if (mpPulseAudioClient->drain() == false) {
239                         AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
240                     }
241                 }
242                 mpPulseAudioClient->cork(true);
243                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
244             }
245             internalUnlock();
246
247             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
248             state = FOCUS_IS_RELEASED;
249         }
250     } else {
251         ///////////////////////////////////////
252         // Triggered by 'focus callback'
253         ///////////////////////////////////////
254
255         if (pHandler->getId() != id) {
256             AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
257         }
258
259         if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
260             AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
261             return;
262         }
263
264         if (state == FOCUS_IS_RELEASED) {
265             // Focus handle(id) was released, do pause here
266             internalLock();
267             if (mpPulseAudioClient) {
268                 if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
269                     if (mpPulseAudioClient->drain() == false) {
270                         AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
271                     }
272                 }
273                 mpPulseAudioClient->cork(true);
274                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
275             }
276             internalUnlock();
277         } else if (state == FOCUS_IS_ACQUIRED) {
278             // Focus handle(id) was acquired again,
279             // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
280             // do resume here and call interrupt completed callback to application.
281             internalLock();
282             if (mpPulseAudioClient) {
283                 mpPulseAudioClient->cork(false);
284                 onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
285             }
286             internalUnlock();
287         }
288     }
289
290     if (mInterruptCallback.onInterrupt != NULL) {
291         IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
292         e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
293         mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
294     }
295 }
296
297 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
298     assert(pHandler);
299
300     if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
301         if (value == 1 && pHandler->getSubscribeId() > 0) {
302             // Unregister focus watch callback & disable session handler
303             pHandler->disableSessionHandler();
304             AUDIO_IO_LOGD("Session handler disabled by signal");
305         } else if (value == 0) {
306             // Currently do nothing...
307         }
308     }
309 }
310
311 void CAudioIO::prepare() throw(CAudioError) {
312     if (__mIsInit == false) {
313         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
314     }
315
316     try {
317         AUDIO_IO_LOGD("------> prepare done");
318         /* Do nothing */
319     } catch (CAudioError e) {
320         throw e;
321     }
322 }
323
324 void CAudioIO::unprepare() throw(CAudioError) {
325     if (__mIsInit == false) {
326         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
327     }
328
329     try {
330         AUDIO_IO_LOGD("unprepare ----->");
331         /* Do nothing */
332     } catch (CAudioError e) {
333         throw e;
334     }
335 }
336
337 void CAudioIO::pause() throw(CAudioError) {
338     if (__mIsInit == false || IsReady() == false) {
339         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
340     }
341
342     try {
343         internalLock();
344         AUDIO_IO_LOGD("pause");
345         mpPulseAudioClient->cork(true);
346         internalUnlock();
347     } catch (CAudioError e) {
348         internalUnlock();
349         throw e;
350     }
351 }
352
353 void CAudioIO::resume() throw(CAudioError) {
354     if (__mIsInit == false || IsReady() == false) {
355         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
356     }
357
358     try {
359         internalLock();
360         AUDIO_IO_LOGD("resume");
361         mpPulseAudioClient->cork(false);
362         internalUnlock();
363     } catch (CAudioError e) {
364         internalUnlock();
365         throw e;
366     }
367 }
368
369 void CAudioIO::drain() throw(CAudioError) {
370     if (__mIsInit == false || IsReady() == false) {
371         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
372     }
373
374     try {
375         if (mpPulseAudioClient->isInThread()) {
376             mpPulseAudioClient->drain();
377         } else {
378             internalLock();
379             mpPulseAudioClient->drain();
380             internalUnlock();
381         }
382     } catch (CAudioError e) {
383         if (!mpPulseAudioClient->isInThread()) {
384             internalUnlock();
385         }
386         throw e;
387     }
388 }
389
390 void CAudioIO::flush() throw(CAudioError) {
391     if (__mIsInit == false || IsReady() == false) {
392         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
393     }
394
395     try {
396         if (mpPulseAudioClient->isInThread()) {
397             mpPulseAudioClient->flush();
398         } else {
399             internalLock();
400             mpPulseAudioClient->flush();
401             internalUnlock();
402         }
403     } catch (CAudioError e) {
404         if (!mpPulseAudioClient->isInThread()) {
405             internalUnlock();
406         }
407         throw e;
408     }
409 }
410
411 CAudioInfo& CAudioIO::getAudioInfo() throw(CAudioError) {
412     if (__mIsInit == false) {
413         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
414     }
415
416     return mAudioInfo;
417 }
418
419 void CAudioIO::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
420     if (__mIsInit == false) {
421         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
422     }
423
424     mStreamCallback = callback;
425 }
426
427 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw(CAudioError) {
428     if (__mIsInit == false) {
429         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
430     }
431
432     return mStreamCallback;
433 }
434
435 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) throw(CAudioError) {
436     if (__mIsInit == false) {
437         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
438     }
439
440     mStateChangedCallback = callback;
441 }
442
443 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw(CAudioError) {
444     if (__mIsInit == false) {
445         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
446     }
447
448     return mStateChangedCallback;
449 }
450
451 void CAudioIO::setInterruptCallback(SInterruptCallback callback) throw(CAudioError) {
452     if (__mIsInit == false) {
453         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
454     }
455
456     mInterruptCallback = callback;
457 }
458
459 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw(CAudioError) {
460     if (__mIsInit == false) {
461         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
462     }
463
464     return mInterruptCallback;
465 }
466
467 void CAudioIO::ignoreSession() throw(CAudioError) {
468     if (__mIsInit == false)
469         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
470
471     try {
472         internalLock();
473
474         if (mpPulseAudioClient != NULL && mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
475             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
476
477         bool isSkip = mpAudioSessionHandler->isSkipSession();
478         if (isSkip == false && mpAudioSessionHandler->getId() >= 0)
479             mpAudioSessionHandler->unregisterSound();
480
481         mpAudioSessionHandler->finalize();
482         __mForceIgnore = true;
483
484         internalUnlock();
485     } catch (CAudioError e) {
486         internalUnlock();
487         throw e;
488     }
489 }
490
491 void CAudioIO::setStreamInfo(sound_stream_info_h stream_info) throw(CAudioError) {
492     if (stream_info == NULL)
493         THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_ARGUMENT, "stream_info is NULL");
494
495     if (__mIsInit == false)
496         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
497
498     try {
499         if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
500             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
501
502         int errorCode = SOUND_MANAGER_ERROR_NONE;
503         CAudioInfo::EAudioType audioType = CAudioInfo::EAudioType::AUDIO_IN_TYPE_MEDIA;
504         char *type = NULL;
505         int index = -1;
506         bool avail = false;
507
508         if ((errorCode = sound_manager_is_available_stream_information(stream_info, NATIVE_API_AUDIO_IO, &avail)) != SOUND_MANAGER_ERROR_NONE)
509             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info is invalid [ret:%d]", errorCode);
510
511         if (!avail)
512             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_NOT_SUPPORTED_TYPE, "Input stream is not supported");
513
514         if ((errorCode = sound_manager_get_type_from_stream_information(stream_info, &type)) != SOUND_MANAGER_ERROR_NONE)
515             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->stream_type is invalid [ret:%d]", errorCode);
516
517         if (mDirection == CAudioInfo::EAudioDirection::AUDIO_DIRECTION_IN)
518             getAudioInfo().convertInputStreamType2AudioType(type, &audioType);
519         else
520             getAudioInfo().convertOutputStreamType2AudioType(type, &audioType);
521         getAudioInfo().setAudioType(audioType);
522
523         if ((errorCode = sound_manager_get_index_from_stream_information(stream_info, &index)) != SOUND_MANAGER_ERROR_NONE)
524             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_INVALID_ARGUMENT, "Parameter stream_info->index is invalid [ret:%d]", errorCode);
525
526         getAudioInfo().setAudioIndex(index);
527
528         AUDIO_IO_LOGD("stream info(%p) is set", stream_info);
529     } catch (CAudioError e) {
530         throw e;
531     }
532 }
533
534 void CAudioIO::setInternalStreamInfo() throw(CAudioError) {
535     if (__mIsInit == false)
536         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
537
538     try {
539         if (mState != CAudioInfo::EAudioIOState::AUDIO_IO_STATE_IDLE)
540             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_STATE, "it is not permitted while started");
541
542         if (mpAudioSessionHandler &&
543             mpAudioSessionHandler->getMultimediaSession() == MM_SESSION_TYPE_VOIP) {
544             sound_stream_info_h stream_info = NULL;
545             mpAudioSessionHandler->getInternalVoipStreamInfo(&stream_info);
546             AUDIO_IO_LOGD("get internal VOIP stream info(%p)", stream_info);
547             setStreamInfo(stream_info);
548         }
549     } catch (CAudioError e) {
550         throw e;
551     }
552 }