/* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "APM::AudioSession" //#define LOG_NDEBUG 0 #include #include "AudioSession.h" #include "AudioGain.h" #include "TypeConverter.h" #include #include namespace android { AudioSession::AudioSession(audio_session_t session, audio_source_t inputSource, audio_format_t format, uint32_t sampleRate, audio_channel_mask_t channelMask, audio_input_flags_t flags, uid_t uid, bool isSoundTrigger, AudioMix* policyMix, AudioPolicyClientInterface *clientInterface) : mSession(session), mInputSource(inputSource), mConfig({ .format = format, .sample_rate = sampleRate, .channel_mask = channelMask}), mFlags(flags), mUid(uid), mIsSoundTrigger(isSoundTrigger), mOpenCount(1), mActiveCount(0), mPolicyMix(policyMix), mClientInterface(clientInterface), mInfoProvider(NULL) { } uint32_t AudioSession::changeOpenCount(int delta) { if ((delta + (int)mOpenCount) < 0) { ALOGW("%s invalid delta %d, open count %d", __FUNCTION__, delta, mOpenCount); mOpenCount = (uint32_t)(-delta); } mOpenCount += delta; ALOGV("%s open count %d", __FUNCTION__, mOpenCount); return mOpenCount; } uint32_t AudioSession::changeActiveCount(int delta) { const uint32_t oldActiveCount = mActiveCount; if ((delta + (int)mActiveCount) < 0) { ALOGW("%s invalid delta %d, active count %d", __FUNCTION__, delta, mActiveCount); mActiveCount = (uint32_t)(-delta); } mActiveCount += delta; ALOGV("%s active count %d", __FUNCTION__, mActiveCount); int event = RECORD_CONFIG_EVENT_NONE; if ((oldActiveCount == 0) && (mActiveCount > 0)) { event = RECORD_CONFIG_EVENT_START; } else if ((oldActiveCount > 0) && (mActiveCount == 0)) { event = RECORD_CONFIG_EVENT_STOP; } if (event != RECORD_CONFIG_EVENT_NONE) { // Dynamic policy callback: // if input maps to a dynamic policy with an activity listener, notify of state change if ((mPolicyMix != NULL) && ((mPolicyMix->mCbFlags & AudioMix::kCbFlagNotifyActivity) != 0)) { mClientInterface->onDynamicPolicyMixStateUpdate(mPolicyMix->mDeviceAddress, (event == RECORD_CONFIG_EVENT_START) ? MIX_STATE_MIXING : MIX_STATE_IDLE); } // Recording configuration callback: const AudioSessionInfoProvider* provider = mInfoProvider; const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() : AUDIO_CONFIG_BASE_INITIALIZER; const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() : AUDIO_PATCH_HANDLE_NONE; mClientInterface->onRecordingConfigurationUpdate(event, mSession, mInputSource, &mConfig, &deviceConfig, patchHandle); } return mActiveCount; } bool AudioSession::matches(const sp &other) const { if (other->session() == mSession && other->inputSource() == mInputSource && other->format() == mConfig.format && other->sampleRate() == mConfig.sample_rate && other->channelMask() == mConfig.channel_mask && other->flags() == mFlags && other->uid() == mUid) { return true; } return false; } void AudioSession::setInfoProvider(AudioSessionInfoProvider *provider) { mInfoProvider = provider; } void AudioSession::onSessionInfoUpdate() const { if (mActiveCount > 0) { // resend the callback after requerying the informations from the info provider const AudioSessionInfoProvider* provider = mInfoProvider; const audio_config_base_t deviceConfig = (provider != NULL) ? provider->getConfig() : AUDIO_CONFIG_BASE_INITIALIZER; const audio_patch_handle_t patchHandle = (provider != NULL) ? provider->getPatchHandle() : AUDIO_PATCH_HANDLE_NONE; mClientInterface->onRecordingConfigurationUpdate(RECORD_CONFIG_EVENT_START, mSession, mInputSource, &mConfig, &deviceConfig, patchHandle); } } status_t AudioSession::dump(int fd, int spaces, int index) const { const size_t SIZE = 256; char buffer[SIZE]; String8 result; snprintf(buffer, SIZE, "%*sAudio session %d:\n", spaces, "", index+1); result.append(buffer); snprintf(buffer, SIZE, "%*s- session: %2d\n", spaces, "", mSession); result.append(buffer); snprintf(buffer, SIZE, "%*s- owner uid: %2d\n", spaces, "", mUid); result.append(buffer); snprintf(buffer, SIZE, "%*s- input source: %d\n", spaces, "", mInputSource); result.append(buffer); snprintf(buffer, SIZE, "%*s- format: %08x\n", spaces, "", mConfig.format); result.append(buffer); snprintf(buffer, SIZE, "%*s- sample: %d\n", spaces, "", mConfig.sample_rate); result.append(buffer); snprintf(buffer, SIZE, "%*s- channel mask: %08x\n", spaces, "", mConfig.channel_mask); result.append(buffer); snprintf(buffer, SIZE, "%*s- is soundtrigger: %s\n", spaces, "", mIsSoundTrigger ? "true" : "false"); result.append(buffer); snprintf(buffer, SIZE, "%*s- open count: %d\n", spaces, "", mOpenCount); result.append(buffer); snprintf(buffer, SIZE, "%*s- active count: %d\n", spaces, "", mActiveCount); result.append(buffer); write(fd, result.string(), result.size()); return NO_ERROR; } status_t AudioSessionCollection::addSession(audio_session_t session, const sp& audioSession, AudioSessionInfoProvider *provider) { ssize_t index = indexOfKey(session); if (index >= 0) { ALOGW("addSession() session %d already in", session); return ALREADY_EXISTS; } audioSession->setInfoProvider(provider); add(session, audioSession); ALOGV("addSession() session %d client %d source %d", session, audioSession->uid(), audioSession->inputSource()); return NO_ERROR; } status_t AudioSessionCollection::removeSession(audio_session_t session) { ssize_t index = indexOfKey(session); if (index < 0) { ALOGW("removeSession() session %d not in", session); return ALREADY_EXISTS; } ALOGV("removeSession() session %d", session); valueAt(index)->setInfoProvider(NULL); removeItemsAt(index); return NO_ERROR; } uint32_t AudioSessionCollection::getOpenCount() const { uint32_t openCount = 0; for (size_t i = 0; i < size(); i++) { openCount += valueAt(i)->openCount(); } return openCount; } AudioSessionCollection AudioSessionCollection::getActiveSessions() const { AudioSessionCollection activeSessions; for (size_t i = 0; i < size(); i++) { if (valueAt(i)->activeCount() != 0) { activeSessions.add(valueAt(i)->session(), valueAt(i)); } } return activeSessions; } bool AudioSessionCollection::hasActiveSession() const { return getActiveSessions().size() != 0; } bool AudioSessionCollection::isSourceActive(audio_source_t source) const { for (size_t i = 0; i < size(); i++) { const sp audioSession = valueAt(i); // AUDIO_SOURCE_HOTWORD is equivalent to AUDIO_SOURCE_VOICE_RECOGNITION only if it // corresponds to an active capture triggered by a hardware hotword recognition if (audioSession->activeCount() > 0 && ((audioSession->inputSource() == source) || ((source == AUDIO_SOURCE_VOICE_RECOGNITION) && (audioSession->inputSource() == AUDIO_SOURCE_HOTWORD) && audioSession->isSoundTrigger()))) { return true; } } return false; } void AudioSessionCollection::onSessionInfoUpdate() const { for (size_t i = 0; i < size(); i++) { valueAt(i)->onSessionInfoUpdate(); } } status_t AudioSessionCollection::dump(int fd, int spaces) const { const size_t SIZE = 256; char buffer[SIZE]; snprintf(buffer, SIZE, "%*sAudio Sessions:\n", spaces, ""); write(fd, buffer, strlen(buffer)); for (size_t i = 0; i < size(); i++) { valueAt(i)->dump(fd, spaces + 2, i); } return NO_ERROR; } }; // namespace android