Fix convertInterruptedCode()
[platform/core/api/audio-io.git] / src / cpp / CAudioSessionHandler.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 <unistd.h>
19 #include <mm_error.h>
20 #include "CAudioIODef.h"
21
22
23 using namespace std;
24 using namespace tizen_media_audio;
25
26
27 /**
28  * class CAudioSessionHandler
29  */
30 int CAudioSessionHandler::__sCaptureRef = 0;
31
32 int CAudioSessionHandler::__pcmCaptureCountInc() {
33     int actual;
34     do {
35         actual = __sCaptureRef;
36     } while (!__sync_bool_compare_and_swap(&__sCaptureRef, actual, actual + 1));
37     AUDIO_IO_LOGD("CaptureRefCount+1 > [%d]", __sCaptureRef);
38     return __sCaptureRef;
39 }
40
41 int CAudioSessionHandler::__pcmCaptureCountDec() {
42     int actual;
43     do {
44         actual = __sCaptureRef;
45     } while (!__sync_bool_compare_and_swap(&__sCaptureRef, actual, actual - 1));
46     AUDIO_IO_LOGD("CaptureRefCount-1 > [%d]", __sCaptureRef);
47     if (__sCaptureRef < 0) {
48         AUDIO_IO_LOGE("A CaptureRef[%d] is not valid! Something is wrong!", __sCaptureRef);
49         __sCaptureRef = 0;
50     }
51     return __sCaptureRef;
52 }
53
54 int CAudioSessionHandler::__pcmCaptureCountGet() {
55     AUDIO_IO_LOGD("CaptureRefCount > [%d]", __sCaptureRef);
56     return __sCaptureRef;
57 }
58
59 int CAudioSessionHandler::__sFocusRef = 0;
60
61 int CAudioSessionHandler::__focusIdCountInc() {
62     int actual;
63     do {
64         actual = __sFocusRef;
65     } while (!__sync_bool_compare_and_swap(&__sFocusRef, actual, actual + 1));
66     AUDIO_IO_LOGD("FocusRefCount+1 > [%d]", __sFocusRef);
67     return __sFocusRef;
68 }
69
70 int CAudioSessionHandler::__focusIdCountDec() {
71     int actual;
72     do {
73         actual = __sFocusRef;
74     } while (!__sync_bool_compare_and_swap(&__sFocusRef, actual, actual - 1));
75     AUDIO_IO_LOGD("FocusRefCount-1 > [%d]", __sFocusRef);
76     return __sFocusRef;
77 }
78
79 int CAudioSessionHandler::__focusIdCountGet() {
80     /* AUDIO_IO_LOGD("FocusRefCount > [%d]", __sFocusRef); */
81     return __sFocusRef;
82 }
83
84 CAudioSessionHandler::CAudioSessionHandler(EAudioSessionType sessionType, CAudioInfo& audioInfo, IAudioSessionEventListener* listener) :
85     __mId(-1),
86     __mOptions(0),
87     __mAudioSession(sessionType),
88     __mMultimediaSession(MM_SESSION_TYPE_MEDIA),
89     __mpEventListener(listener),
90     __mIsInit(false),
91     __mSubscribeId(0),
92     __mUseFocus(false),
93     __mFocusType(FOCUS_NONE),
94     __mState(FOCUS_IS_RELEASED),
95     __mReasonForChange(NULL),
96     __mAdditionalInfo(NULL) {
97     __mAudioInfo = audioInfo;
98 }
99
100 CAudioSessionHandler::~CAudioSessionHandler() {
101 }
102
103 CAudioError CAudioSessionHandler::__convertStreamType(EAudioSessionType type1, MMSessionType type2, int *index) {
104     unsigned int i;
105     int idx = -1;
106
107     assert(index != NULL);
108
109     if (type1 == EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE) {
110         for (i = 0 ; i < sizeof(__STREAM_TYPE_TABLE_IN) / sizeof(__STREAM_TYPE_TABLE_IN[0]) ; i++) {
111             if (__STREAM_TYPE_TABLE_IN[i].type == type2) {
112                 idx = i;
113                 break;
114             }
115         }
116     } else {
117         for (i = 0 ; i < sizeof(__STREAM_TYPE_TABLE_OUT) / sizeof(__STREAM_TYPE_TABLE_OUT[0]) ; i++) {
118             if (__STREAM_TYPE_TABLE_OUT[i].type == type2) {
119                 idx = i;
120                 break;
121             }
122         }
123     }
124
125     if (idx < 0) {
126         RET_ERROR_MSG(CAudioError::EError::ERROR_NOT_SUPPORTED, "Does not support session type.");
127     }
128     *index = idx;
129     RET_ERROR(CAudioError::EError::ERROR_NONE);
130 }
131
132 CAudioError CAudioSessionHandler::__getAsmInformation(MMSessionType *type, int *options) {
133     assert(type != NULL);
134     assert(options != NULL);
135
136     MMSessionType currentSession = MM_SESSION_TYPE_MEDIA;
137     int           sessionOptions = 0;
138
139     /* Read session information */
140     int ret = 0;
141     if ((ret = _mm_session_util_read_information(-1, (int*)&currentSession, &sessionOptions)) < 0) {
142         if (ret == (int) MM_ERROR_INVALID_HANDLE) {
143             RET_ERROR_MSG(CAudioError::EError::ERROR_INVALID_HANDLE, "Failed _mm_session_util_read_information(). Invalid handle");
144         } else {
145             RET_ERROR_MSG(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed _mm_session_util_read_information(). Not exist");
146         }
147     }
148
149     *type    = currentSession;
150     *options = sessionOptions;
151
152     RET_ERROR(CAudioError::EError::ERROR_NONE);
153 }
154
155 bool CAudioSessionHandler::__isFocusRequired(MMSessionType type, int options) {
156     if ((options & MM_SESSION_OPTION_PAUSE_OTHERS)
157         || ((type != MM_SESSION_TYPE_MEDIA) && (type != MM_SESSION_TYPE_MEDIA_RECORD)))
158         return true;
159     else
160         return false;
161 }
162
163 bool CAudioSessionHandler::__isFocusDisableReacquisitionRequired(MMSessionType type, int options) {
164     if ((type == MM_SESSION_TYPE_MEDIA) &&
165         !(options & MM_SESSION_OPTION_RESUME_BY_SYSTEM_OR_MEDIA_PAUSED))
166         return true;
167
168     return false;
169 }
170
171 int CAudioSessionHandler::getId() {
172     return __mId;
173 }
174
175 int CAudioSessionHandler::getOptions() {
176     return __mOptions;
177 }
178
179 CAudioSessionHandler::EAudioSessionType CAudioSessionHandler::getAudioSession() {
180     return __mAudioSession;
181 }
182
183 MMSessionType CAudioSessionHandler::getMultimediaSession() {
184     return __mMultimediaSession;
185 }
186
187 void CAudioSessionHandler::getInternalVoipStreamInfo(sound_stream_info_h *stream_info) {
188     int ret;
189
190     if ((ret = sound_manager_get_internal_voip_stream_information(stream_info))) {
191         if (ret == SOUND_MANAGER_ERROR_NO_DATA) {
192             AUDIO_IO_LOGW("there's no internal voip stream info.");
193         } else {
194             AUDIO_IO_LOGE("failed to sound_manager_get_internal_voip_stream_information(), ret(0x%x)", ret);
195         }
196     }
197     return;
198 }
199
200 unsigned int CAudioSessionHandler::getSubscribeId() {
201     return __mSubscribeId;
202 }
203
204 CAudioInfo CAudioSessionHandler::getAudioInfo() {
205     return __mAudioInfo;
206 }
207
208 void CAudioSessionHandler::__sound_pcm_signal_cb(mm_sound_signal_name_t signal, int value, void *user_data) {
209     assert(user_data);
210
211     AUDIO_IO_LOGD("[signal:%d], [value:%d], [user_data:0x%x]", signal, value, user_data);
212
213     CAudioSessionHandler* pHandler = static_cast<CAudioSessionHandler*>(user_data);
214     if (pHandler->__mpEventListener != NULL)
215         pHandler->__mpEventListener->onSignal(pHandler, signal, value);
216 }
217
218 void CAudioSessionHandler::initialize() throw(CAudioError) {
219     AUDIO_IO_LOGD("");
220     if (__mIsInit == true) {
221         return;
222     }
223
224     MMSessionType currentSession = MM_SESSION_TYPE_MEDIA;
225     int           sessionOptions = 0;  // Mix with others by default
226
227     CAudioError err = __getAsmInformation(&currentSession, &sessionOptions);
228     if (err == CAudioError::EError::ERROR_NONE) {
229         if (currentSession == MM_SESSION_TYPE_REPLACED_BY_STREAM) {
230             __mUseFocus = false;
231             AUDIO_IO_LOGD("Stream info. was created outside, skip audio focus concept internally!");
232         } else {
233             // Session was configured before, use focus callback
234             __mUseFocus = true;
235             AUDIO_IO_LOGD("Use audio focus concept internally!");
236         }
237     } else {
238         if (err == CAudioError::EError::ERROR_INVALID_HANDLE) {
239             // No session, No stream_info, No focus watch callback before
240             // Use focus watch callback with signal subscribe
241
242             int errorCode = mm_sound_subscribe_signal(MM_SOUND_SIGNAL_RELEASE_INTERNAL_FOCUS, &__mSubscribeId, __sound_pcm_signal_cb, static_cast<void*>(this));
243             if (errorCode != MM_ERROR_NONE || __mSubscribeId == 0) {
244                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_subscribe_signal() err:0x%x, __mSubscribeId:%u",
245                                        errorCode, __mSubscribeId);
246             }
247
248             AUDIO_IO_LOGD("Subscribed mm_sound signal [id:%d]", __mSubscribeId);
249
250             sessionOptions = 0;  // Mix with others by default
251             __mUseFocus = true;
252             AUDIO_IO_LOGD("Use audio focus(watch) concept internally!");
253         } else {
254             __mUseFocus = false;
255             AUDIO_IO_LOGD("Skip audio focus concept!");
256         }
257
258         if (__mAudioSession == EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE) {
259             AUDIO_IO_LOGD("Set default \"Media_Record\" type");
260             currentSession = MM_SESSION_TYPE_MEDIA_RECORD;
261         } else {
262             AUDIO_IO_LOGD("Set default \"Media\" type");
263             currentSession = MM_SESSION_TYPE_MEDIA;
264         }
265     }
266
267     // Updates session information
268     __mMultimediaSession = currentSession;
269     __mOptions           = sessionOptions;
270
271     if (this->__mAudioSession == EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE) {
272         __pcmCaptureCountInc();
273     }
274
275     __mIsInit = true;
276 }
277
278 void CAudioSessionHandler::finalize() {
279     AUDIO_IO_LOGD("");
280     if (__mIsInit == false) {
281         return;
282     }
283
284     if (__mAudioSession == EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE) {
285         __pcmCaptureCountDec();
286     }
287
288     if (__mSubscribeId > 0) {
289         AUDIO_IO_LOGD("Unsubscribed mm_sound signal [id:%d]", __mSubscribeId);
290         mm_sound_unsubscribe_signal(__mSubscribeId);
291         __mSubscribeId = 0;
292     }
293
294     __mpEventListener = NULL;
295
296     __mIsInit = false;
297 }
298
299 bool CAudioSessionHandler::isSkipSession() {
300     if (__mMultimediaSession == MM_SESSION_TYPE_REPLACED_BY_STREAM ||
301         __mMultimediaSession == MM_SESSION_TYPE_VOIP ||
302         __mMultimediaSession == MM_SESSION_TYPE_CALL ||
303         __mMultimediaSession == MM_SESSION_TYPE_VIDEOCALL) {
304         AUDIO_IO_LOGD("__mMultimediaSession is [%d], skip session", __mMultimediaSession);
305         return true;
306     }
307
308     return false;
309 }
310
311 void CAudioSessionHandler::__sound_pcm_focus_cb(int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, int option, const char *additional_info, void *user_data) {
312     assert(user_data);
313
314     AUDIO_IO_LOGD("[id:%d], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s], [user_data:0x%x]", id, focus_type, state, reason_for_change, additional_info, user_data);
315
316 /* FIXME: disable it temporarily */
317 #ifndef DISABLE_SESSION_BACK_COMP
318
319     CAudioSessionHandler* pHandler = static_cast<CAudioSessionHandler*>(user_data);
320     pHandler->__mFocusType       = focus_type;
321     pHandler->__mState           = state;
322     pHandler->__mReasonForChange = (char *)reason_for_change;
323     pHandler->__mAdditionalInfo  = (char *)additional_info;
324
325     if (pHandler->__mpEventListener != NULL)
326         pHandler->__mpEventListener->onInterrupt(pHandler, id, focus_type, state, reason_for_change, additional_info);
327 #endif
328     return;
329 }
330
331 void CAudioSessionHandler::__sound_pcm_focus_watch_cb(int id, mm_sound_focus_type_e focus_type, mm_sound_focus_state_e state, const char *reason_for_change, const char *additional_info, void *user_data) {
332     AUDIO_IO_LOGD("[id:%d], [focus_type:%d], [state:%d], [reason_for_change:%s], [additional_info:%s], [user_data:0x%x]", id, focus_type, state, reason_for_change, additional_info, user_data);
333
334     CAudioSessionHandler::__sound_pcm_focus_cb(-1, focus_type, state, reason_for_change, 0, additional_info, user_data);
335
336     return;
337 }
338
339 void CAudioSessionHandler::registerSound() throw(CAudioError) {
340     if (__mIsInit == false) {
341         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioSessionHandler");
342     }
343
344     if (__mUseFocus == true) {
345         if (__mId >= 0) {
346             THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_POLICY_BLOCKED, "Already registered [id:%d]", __mId);
347         }
348
349         int errorCode = 0;
350
351         if (__isFocusRequired(__mMultimediaSession, __mOptions)) {
352             int index = 0;
353             CAudioError err = __convertStreamType(__mAudioSession, __mMultimediaSession, &index);
354             if (err != CAudioError::EError::ERROR_NONE) {
355                 throw err;
356             }
357
358             errorCode = mm_sound_focus_get_id(&__mId);
359             if (errorCode != MM_ERROR_NONE) {
360                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_focus_get_id() err:0x%x", errorCode);
361             }
362
363             // Register focus callback
364             errorCode = mm_sound_register_focus_for_session(__mId,
365                                                 getpid(),
366                                                 __mAudioSession == EAudioSessionType::AUDIO_SESSION_TYPE_CAPTURE ? __STREAM_TYPE_TABLE_IN[index].name : __STREAM_TYPE_TABLE_OUT[index].name,
367                                                 __sound_pcm_focus_cb,
368                                                 static_cast<void*>(this));
369             if (errorCode != MM_ERROR_NONE) {
370                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_register_focus_for_session() err:0x%x", errorCode);
371             }
372
373             if (__isFocusDisableReacquisitionRequired(__mMultimediaSession, __mOptions)) {
374                 errorCode = mm_sound_set_focus_reacquisition_for_session(__mId, false);
375                 if (errorCode != MM_ERROR_NONE) {
376                     THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_set_focus_reacquisition() err:0x%x", errorCode);
377                 }
378             }
379
380             __focusIdCountInc();
381
382             AUDIO_IO_LOGD("Focus callback registered successfully [id:%d]", __mId);
383         } else if (!(__mOptions & MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
384             // Register focus watch callback
385             errorCode = mm_sound_set_focus_watch_callback_for_session(getpid(), FOCUS_FOR_BOTH, __sound_pcm_focus_watch_cb, static_cast<void*>(this), &__mId);
386             if (errorCode < 0) {
387                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_set_focus_watch_callback_for_session() err:0x%x", errorCode);
388             }
389
390             __focusIdCountInc();
391
392             AUDIO_IO_LOGD("Focus watch callback registered successfully [id:%d]", __mId);
393         }
394     }
395 }
396
397 void CAudioSessionHandler::unregisterSound() throw(CAudioError) {
398     if (__mIsInit == false) {
399         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioSessionHandler");
400     }
401
402     if (__mUseFocus == true && __mId >= 0) {
403         int errorCode = 0;
404
405         if (__isFocusRequired(__mMultimediaSession, __mOptions)) {
406             // Unregister focus callback
407             errorCode = mm_sound_unregister_focus(__mId);
408             if (errorCode != MM_ERROR_NONE) {
409                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_unregister_focus() err:0x%x", errorCode);
410             }
411
412             __focusIdCountDec();
413
414             AUDIO_IO_LOGD("Focus callback unregistered successfully [id:%d]", __mId);
415             __mId = -1;
416         } else if (!(__mOptions & MM_SESSION_OPTION_UNINTERRUPTIBLE)) {
417             // Unregister focus watch callback.
418             errorCode = mm_sound_unset_focus_watch_callback(__mId);
419             if (errorCode < 0) {
420                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_unset_focus_watch_callback() err:0x%x", errorCode);
421             }
422
423             __focusIdCountDec();
424
425             AUDIO_IO_LOGD("Focus watch callback unregistered successfully [id:%d]", __mId);
426             __mId = -1;
427         }
428     }
429 }
430
431 void CAudioSessionHandler::updatePlaying() throw(CAudioError) {
432     if (__mIsInit == false) {
433         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioSessionHandler");
434     }
435
436     if (__mUseFocus && __isFocusRequired(__mMultimediaSession, __mOptions)) {
437         if (__mId >= 0) {
438             int ret = MM_ERROR_NONE;
439             if (__mMultimediaSession == MM_SESSION_TYPE_MEDIA)
440                 ret = mm_sound_acquire_focus_with_option(__mId, FOCUS_FOR_BOTH, 1, "audio-io acquire focus"); /* option: 1 for no-resume */
441             else
442                 ret = mm_sound_acquire_focus(__mId, FOCUS_FOR_BOTH, "audio-io acquire focus");
443             if (ret != MM_ERROR_NONE) {
444                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_POLICY_BLOCKED, "Failed mm_sound_acquire_focus() err:0x%x", ret);
445             }
446             AUDIO_IO_LOGD("Focus acquired successfully [id:%d]", __mId);
447         }
448     }
449 }
450
451 void CAudioSessionHandler::updateStop() throw(CAudioError) {
452     if (__mIsInit == false) {
453         THROW_ERROR_MSG(CAudioError::EError::ERROR_NOT_INITIALIZED, "Doesn't initialize CAudioSessionHandler");
454     }
455
456     if (__mUseFocus && __isFocusRequired(__mMultimediaSession, __mOptions)) {
457         if (__mId >= 0) {
458             int ret = MM_ERROR_NONE;
459             if (__mMultimediaSession == MM_SESSION_TYPE_MEDIA)
460                 ret = mm_sound_release_focus_with_option(__mId, FOCUS_FOR_BOTH, 1, "audio-io release focus"); /* option: 1 for no-resume */
461             else
462                 ret = mm_sound_release_focus(__mId, FOCUS_FOR_BOTH, "audio-io release focus");
463             if (ret != MM_ERROR_NONE) {
464                 THROW_ERROR_MSG_FORMAT(CAudioError::EError::ERROR_FAILED_OPERATION, "Failed mm_sound_release_focus() err:0x%x", ret);
465             }
466             AUDIO_IO_LOGD("Focus released successfully [id:%d]", __mId);
467         }
468     }
469 }
470
471 void CAudioSessionHandler::disableSessionHandler() throw(CAudioError) {
472     CAudioSessionHandler::updateStop();
473     CAudioSessionHandler::unregisterSound();
474
475     CAudioSessionHandler::__mUseFocus = false;
476 }
477
478 /**
479  * class IAudioSessionEventListener
480  */
481 IAudioSessionEventListener::EInterruptCode IAudioSessionEventListener::convertInterruptedCode(int code, const char *reason_for_change) {
482     EInterruptCode e = EInterruptCode::INTERRUPT_BY_MEDIA;
483
484     switch (code) {
485     case FOCUS_IS_ACQUIRED:
486         e = EInterruptCode::INTERRUPT_COMPLETED;
487         break;
488
489     case FOCUS_IS_RELEASED:
490         if (!strcmp(reason_for_change, "media"))              e = EInterruptCode::INTERRUPT_BY_MEDIA;
491         if (!strcmp(reason_for_change, "radio"))              e = EInterruptCode::INTERRUPT_BY_MEDIA;
492         if (!strcmp(reason_for_change, "loopback"))           e = EInterruptCode::INTERRUPT_BY_MEDIA;
493         if (!strcmp(reason_for_change, "system"))             e = EInterruptCode::INTERRUPT_BY_MEDIA;
494         if (!strcmp(reason_for_change, "alarm"))              e = EInterruptCode::INTERRUPT_BY_ALARM;
495         if (!strcmp(reason_for_change, "notification"))       e = EInterruptCode::INTERRUPT_BY_NOTIFICATION;
496         if (!strcmp(reason_for_change, "emergency"))          e = EInterruptCode::INTERRUPT_BY_EMERGENCY;
497         if (!strcmp(reason_for_change, "voice-information"))  e = EInterruptCode::INTERRUPT_BY_MEDIA;
498         if (!strcmp(reason_for_change, "voice-recognition"))  e = EInterruptCode::INTERRUPT_BY_MEDIA;
499         if (!strcmp(reason_for_change, "ringtone-voip"))      e = EInterruptCode::INTERRUPT_BY_CALL;
500         if (!strcmp(reason_for_change, "ringtone-call"))      e = EInterruptCode::INTERRUPT_BY_CALL;
501         if (!strcmp(reason_for_change, "voip"))               e = EInterruptCode::INTERRUPT_BY_CALL;
502         if (!strcmp(reason_for_change, "call-voice"))         e = EInterruptCode::INTERRUPT_BY_CALL;
503         if (!strcmp(reason_for_change, "call-video"))         e = EInterruptCode::INTERRUPT_BY_CALL;
504         break;
505     }
506
507     return e;
508 }