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