Fix bug regarding ignore session
[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
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
237             if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
238                 if (mpPulseAudioClient->drain() == false) {
239                     AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
240                 }
241             }
242
243             mpPulseAudioClient->cork(true);
244             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
245
246             internalUnlock();
247
248             // Focus watch callback doesn't have focus handle, but it need to convert & report to application for convenience
249             state = FOCUS_IS_RELEASED;
250         }
251     } else {
252         ///////////////////////////////////////
253         // Triggered by 'focus callback'
254         ///////////////////////////////////////
255
256         if (pHandler->getId() != id) {
257             AUDIO_IO_LOGW("Id is different, why? [mId : %d]", pHandler->getId());
258         }
259
260         if (session_option & MM_SESSION_OPTION_UNINTERRUPTIBLE) {
261             AUDIO_IO_LOGD("Session option is uninterruptible, skip...");
262             return;
263         }
264
265         if (state == FOCUS_IS_RELEASED) {
266             // Focus handle(id) was released, do pause here
267             internalLock();
268
269             if (mpPulseAudioClient->getStreamDirection() == CPulseAudioClient::EStreamDirection::STREAM_DIRECTION_PLAYBACK) {
270                 if (mpPulseAudioClient->drain() == false) {
271                     AUDIO_IO_LOGE("Failed CPulseAudioClient::drain()");
272                 }
273             }
274
275             mpPulseAudioClient->cork(true);
276             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_PAUSED);
277
278             internalUnlock();
279         } else if (state == FOCUS_IS_ACQUIRED) {
280             // Focus handle(id) was acquired again,
281             // check reason_for_change ("call-voice","call-video","voip","alarm","notification", ...)
282             // do resume here and call interrupt completed callback to application.
283             internalLock();
284
285             mpPulseAudioClient->cork(false);
286             onStateChanged(CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING);
287
288             internalUnlock();
289         }
290     }
291
292     if (mInterruptCallback.onInterrupt != NULL) {
293         IAudioSessionEventListener::EInterruptCode e = IAudioSessionEventListener::EInterruptCode::INTERRUPT_COMPLETED;
294         e = IAudioSessionEventListener::convertInterruptedCode(state, reason_for_change);
295         mInterruptCallback.onInterrupt(e, mInterruptCallback.mUserData);
296     }
297 }
298
299 void CAudioIO::onSignal(CAudioSessionHandler* pHandler, mm_sound_signal_name_t signal, int value) {
300     assert(pHandler);
301
302     if (signal == MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS) {
303         if (value == 1 && pHandler->getSubscribeId() > 0) {
304             // Unregister focus watch callback & disable session handler
305             pHandler->disableSessionHandler();
306             AUDIO_IO_LOGD("Session handler disabled by signal");
307         } else if (value == 0) {
308             // Currently do nothing...
309         }
310     }
311 }
312
313 void CAudioIO::prepare() throw(CAudioError) {
314     if (__mIsInit == false) {
315         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
316     }
317
318     try {
319         AUDIO_IO_LOGD("------> prepare done");
320         /* Do nothing */
321     } catch (CAudioError e) {
322         throw e;
323     }
324 }
325
326 void CAudioIO::unprepare() throw(CAudioError) {
327     if (__mIsInit == false) {
328         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
329     }
330
331     try {
332         AUDIO_IO_LOGD("unprepare ----->");
333         /* Do nothing */
334     } catch (CAudioError e) {
335         throw e;
336     }
337 }
338
339 void CAudioIO::pause() throw(CAudioError) {
340     if (__mIsInit == false || IsReady() == false) {
341         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
342     }
343
344     try {
345         internalLock();
346         AUDIO_IO_LOGD("pause");
347         mpPulseAudioClient->cork(true);
348         internalUnlock();
349     } catch (CAudioError e) {
350         internalUnlock();
351         throw e;
352     }
353 }
354
355 void CAudioIO::resume() throw(CAudioError) {
356     if (__mIsInit == false || IsReady() == false) {
357         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
358     }
359
360     try {
361         internalLock();
362         AUDIO_IO_LOGD("resume");
363         mpPulseAudioClient->cork(false);
364         internalUnlock();
365     } catch (CAudioError e) {
366         internalUnlock();
367         throw e;
368     }
369 }
370
371 void CAudioIO::drain() throw(CAudioError) {
372     if (__mIsInit == false || IsReady() == false) {
373         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
374     }
375
376     try {
377         if (mpPulseAudioClient->isInThread()) {
378             mpPulseAudioClient->drain();
379         } else {
380             internalLock();
381             mpPulseAudioClient->drain();
382             internalUnlock();
383         }
384     } catch (CAudioError e) {
385         if (!mpPulseAudioClient->isInThread()) {
386             internalUnlock();
387         }
388         throw e;
389     }
390 }
391
392 void CAudioIO::flush() throw(CAudioError) {
393     if (__mIsInit == false || IsReady() == false) {
394         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Did not initialize or prepare CAudioIO");
395     }
396
397     try {
398         if (mpPulseAudioClient->isInThread()) {
399             mpPulseAudioClient->flush();
400         } else {
401             internalLock();
402             mpPulseAudioClient->flush();
403             internalUnlock();
404         }
405     } catch (CAudioError e) {
406         if (!mpPulseAudioClient->isInThread()) {
407             internalUnlock();
408         }
409         throw e;
410     }
411 }
412
413 CAudioInfo& CAudioIO::getAudioInfo() throw(CAudioError) {
414     if (__mIsInit == false) {
415         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
416     }
417
418     return mAudioInfo;
419 }
420
421 void CAudioIO::setStreamCallback(SStreamCallback callback) throw(CAudioError) {
422     if (__mIsInit == false) {
423         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
424     }
425
426     mStreamCallback = callback;
427 }
428
429 CAudioIO::SStreamCallback CAudioIO::getStreamCallback() throw(CAudioError) {
430     if (__mIsInit == false) {
431         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
432     }
433
434     return mStreamCallback;
435 }
436
437 void CAudioIO::setStateChangedCallback(SStateChangedCallback callback) throw(CAudioError) {
438     if (__mIsInit == false) {
439         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
440     }
441
442     mStateChangedCallback = callback;
443 }
444
445 CAudioIO::SStateChangedCallback CAudioIO::getStateChangedCallback() throw(CAudioError) {
446     if (__mIsInit == false) {
447         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
448     }
449
450     return mStateChangedCallback;
451 }
452
453 void CAudioIO::setInterruptCallback(SInterruptCallback callback) throw(CAudioError) {
454     if (__mIsInit == false) {
455         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
456     }
457
458     mInterruptCallback = callback;
459 }
460
461 CAudioIO::SInterruptCallback CAudioIO::getInterruptCallback() throw(CAudioError) {
462     if (__mIsInit == false) {
463         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
464     }
465
466     return mInterruptCallback;
467 }
468
469
470 void CAudioIO::ignoreSession() throw(CAudioError) {
471     if (__mIsInit == false)
472         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioIO");
473
474     try {
475         internalLock();
476
477         if (mpPulseAudioClient != NULL && mState == CAudioInfo::EAudioIOState::AUDIO_IO_STATE_RUNNING)
478             THROW_ERROR_MSG(CAudioError::EError::ERROR_INVALID_OPERATION, "An Operation is not permitted while started");
479
480         bool isSkip = mpAudioSessionHandler->isSkipSessionEvent();
481         if (isSkip == false && mpAudioSessionHandler->getId() >= 0)
482             mpAudioSessionHandler->unregisterSound();
483
484         mpAudioSessionHandler->finalize();
485         __mForceIgnore = true;
486
487         internalUnlock();
488     } catch (CAudioError e) {
489         internalUnlock();
490         throw e;
491     }
492 }