From 1035194cee4fbd57e35ea15c56e66cd09b63d56e Mon Sep 17 00:00:00 2001 From: Eric Laurent Date: Thu, 8 May 2014 18:49:52 -0700 Subject: audioflinger: refactor thread config events Merge config events and set parameters messaging mechanism. - setting parameters now uses the config event mechanism - config event now allow to wait for a condition and synchronize caller binder thread with execution thread and return an execution status. - simplify locking mechanism to avoid unlocking the thread mutex while processing events. Change-Id: Ia49cb3e617abec4bacb6c1f9a8cb304c4ed3902e --- services/audioflinger/AudioFlinger.cpp | 23 +- services/audioflinger/AudioFlinger.h | 9 +- services/audioflinger/Threads.cpp | 617 ++++++++++++++++----------------- services/audioflinger/Threads.h | 171 ++++++--- 4 files changed, 436 insertions(+), 384 deletions(-) (limited to 'services/audioflinger') diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index eb00c82..46d1211 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1195,12 +1195,17 @@ void AudioFlinger::removeNotificationClient(pid_t pid) } // audioConfigChanged_l() must be called with AudioFlinger::mLock held -void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2) +void AudioFlinger::audioConfigChanged_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients, + int event, + audio_io_handle_t ioHandle, + const void *param2) { - size_t size = mNotificationClients.size(); + size_t size = notificationClients.size(); for (size_t i = 0; i < size; i++) { - mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle, - param2); + notificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, + ioHandle, + param2); } } @@ -1625,7 +1630,7 @@ audio_io_handle_t AudioFlinger::openOutput(audio_module_handle_t module, } // notify client processes of the new output creation - thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); + thread->audioConfigChanged_l(mNotificationClients, AudioSystem::OUTPUT_OPENED); // the first primary output opened designates the primary hw device if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) { @@ -1661,7 +1666,7 @@ audio_io_handle_t AudioFlinger::openDuplicateOutput(audio_io_handle_t output1, thread->addOutputTrack(thread2); mPlaybackThreads.add(id, thread); // notify client processes of the new output creation - thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED); + thread->audioConfigChanged_l(mNotificationClients, AudioSystem::OUTPUT_OPENED); return id; } @@ -1711,7 +1716,7 @@ status_t AudioFlinger::closeOutput_nonvirtual(audio_io_handle_t output) } } } - audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL); + audioConfigChanged_l(mNotificationClients, AudioSystem::OUTPUT_CLOSED, output, NULL); } thread->exit(); // The thread entity (active unit of execution) is no longer running here, @@ -1891,7 +1896,7 @@ audio_io_handle_t AudioFlinger::openInput(audio_module_handle_t module, } // notify client processes of the new input creation - thread->audioConfigChanged_l(AudioSystem::INPUT_OPENED); + thread->audioConfigChanged_l(mNotificationClients, AudioSystem::INPUT_OPENED); return id; } @@ -1916,7 +1921,7 @@ status_t AudioFlinger::closeInput_nonvirtual(audio_io_handle_t input) } ALOGV("closeInput() %d", input); - audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, NULL); + audioConfigChanged_l(mNotificationClients, AudioSystem::INPUT_CLOSED, input, NULL); mRecordThreads.removeItem(input); } thread->exit(); diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index ec32edd..bfbc46c 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -452,7 +452,11 @@ private: // no range check, doesn't check per-thread stream volume, AudioFlinger::mLock held float streamVolume_l(audio_stream_type_t stream) const { return mStreamTypes[stream].volume; } - void audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2); + void audioConfigChanged_l(const DefaultKeyedVector< pid_t,sp >& + notificationClients, + int event, + audio_io_handle_t ioHandle, + const void *param2); // Allocate an audio_io_handle_t, session ID, effect ID, or audio_module_handle_t. // They all share the same ID space, but the namespaces are actually independent @@ -477,7 +481,8 @@ private: void removeClient_l(pid_t pid); void removeNotificationClient(pid_t pid); - + DefaultKeyedVector< pid_t,sp > notificationClients() { + Mutex::Autolock _l(mLock); return mNotificationClients; } bool isNonOffloadableGlobalEffectEnabled_l(); void onNonOffloadableGlobalEffectEnable(); diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp index 2c5a0eb..cd3fa7f 100644 --- a/services/audioflinger/Threads.cpp +++ b/services/audioflinger/Threads.cpp @@ -97,8 +97,8 @@ static const nsecs_t kWarningThrottleNs = seconds(5); // RecordThread loop sleep time upon application overrun or audio HAL read error static const int kRecordThreadSleepUs = 5000; -// maximum time to wait for setParameters to complete -static const nsecs_t kSetParametersTimeoutNs = seconds(2); +// maximum time to wait in sendConfigEvent_l() for a status to be received +static const nsecs_t kConfigEventTimeoutNs = seconds(2); // minimum sleep time for the mixer thread loop when tracks are active but in underrun static const uint32_t kMinThreadSleepTimeUs = 5000; @@ -283,7 +283,6 @@ AudioFlinger::ThreadBase::ThreadBase(const sp& audioFlinger, audio // mSampleRate, mFrameCount, mChannelMask, mChannelCount, mFrameSize, mFormat, mBufferSize // are set by PlaybackThread::readOutputParameters_l() or // RecordThread::readInputParameters_l() - mParamStatus(NO_ERROR), //FIXME: mStandby should be true here. Is this some kind of hack? mStandby(false), mOutDevice(outDevice), mInDevice(inDevice), mAudioSource(AUDIO_SOURCE_DEFAULT), mId(id), @@ -295,12 +294,8 @@ AudioFlinger::ThreadBase::ThreadBase(const sp& audioFlinger, audio AudioFlinger::ThreadBase::~ThreadBase() { // mConfigEvents should be empty, but just in case it isn't, free the memory it owns - for (size_t i = 0; i < mConfigEvents.size(); i++) { - delete mConfigEvents[i]; - } mConfigEvents.clear(); - mParamCond.broadcast(); // do not lock the mutex in destructor releaseWakeLock_l(); if (mPowerManager != 0) { @@ -351,16 +346,30 @@ status_t AudioFlinger::ThreadBase::setParameters(const String8& keyValuePairs) ALOGV("ThreadBase::setParameters() %s", keyValuePairs.string()); Mutex::Autolock _l(mLock); - mNewParameters.add(keyValuePairs); + return sendSetParameterConfigEvent_l(keyValuePairs); +} + +// sendConfigEvent_l() must be called with ThreadBase::mLock held +// Can temporarily release the lock if waiting for a reply from processConfigEvents_l(). +status_t AudioFlinger::ThreadBase::sendConfigEvent_l(sp& event) +{ + status_t status = NO_ERROR; + + mConfigEvents.add(event); + ALOGV("sendConfigEvent_l() num events %d event %d", mConfigEvents.size(), event->mType); mWaitWorkCV.signal(); - // wait condition with timeout in case the thread loop has exited - // before the request could be processed - if (mParamCond.waitRelative(mLock, kSetParametersTimeoutNs) == NO_ERROR) { - status = mParamStatus; - mWaitWorkCV.signal(); - } else { - status = TIMED_OUT; + mLock.unlock(); + { + Mutex::Autolock _l(event->mLock); + while (event->mWaitStatus) { + if (event->mCond.waitRelative(event->mLock, kConfigEventTimeoutNs) != NO_ERROR) { + event->mStatus = TIMED_OUT; + event->mWaitStatus = false; + } + } + status = event->mStatus; } + mLock.lock(); return status; } @@ -373,63 +382,71 @@ void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param) // sendIoConfigEvent_l() must be called with ThreadBase::mLock held void AudioFlinger::ThreadBase::sendIoConfigEvent_l(int event, int param) { - IoConfigEvent *ioEvent = new IoConfigEvent(event, param); - mConfigEvents.add(static_cast(ioEvent)); - ALOGV("sendIoConfigEvent() num events %d event %d, param %d", mConfigEvents.size(), event, - param); - mWaitWorkCV.signal(); + sp configEvent = (ConfigEvent *)new IoConfigEvent(event, param); + sendConfigEvent_l(configEvent); } // sendPrioConfigEvent_l() must be called with ThreadBase::mLock held void AudioFlinger::ThreadBase::sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio) { - PrioConfigEvent *prioEvent = new PrioConfigEvent(pid, tid, prio); - mConfigEvents.add(static_cast(prioEvent)); - ALOGV("sendPrioConfigEvent_l() num events %d pid %d, tid %d prio %d", - mConfigEvents.size(), pid, tid, prio); - mWaitWorkCV.signal(); + sp configEvent = (ConfigEvent *)new PrioConfigEvent(pid, tid, prio); + sendConfigEvent_l(configEvent); } -void AudioFlinger::ThreadBase::processConfigEvents() +// sendSetParameterConfigEvent_l() must be called with ThreadBase::mLock held +status_t AudioFlinger::ThreadBase::sendSetParameterConfigEvent_l(const String8& keyValuePair) { - Mutex::Autolock _l(mLock); - processConfigEvents_l(); + sp configEvent = (ConfigEvent *)new SetParameterConfigEvent(keyValuePair); + return sendConfigEvent_l(configEvent); } // post condition: mConfigEvents.isEmpty() -void AudioFlinger::ThreadBase::processConfigEvents_l() +void AudioFlinger::ThreadBase::processConfigEvents_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients) { + bool configChanged = false; + while (!mConfigEvents.isEmpty()) { - ALOGV("processConfigEvents() remaining events %d", mConfigEvents.size()); - ConfigEvent *event = mConfigEvents[0]; + ALOGV("processConfigEvents_l() remaining events %d", mConfigEvents.size()); + sp event = mConfigEvents[0]; mConfigEvents.removeAt(0); - // release mLock before locking AudioFlinger mLock: lock order is always - // AudioFlinger then ThreadBase to avoid cross deadlock - mLock.unlock(); - switch (event->type()) { + switch (event->mType) { case CFG_EVENT_PRIO: { - PrioConfigEvent *prioEvent = static_cast(event); - // FIXME Need to understand why this has be done asynchronously - int err = requestPriority(prioEvent->pid(), prioEvent->tid(), prioEvent->prio(), + PrioConfigEventData *data = (PrioConfigEventData *)event->mData.get(); + // FIXME Need to understand why this has to be done asynchronously + int err = requestPriority(data->mPid, data->mTid, data->mPrio, true /*asynchronous*/); if (err != 0) { ALOGW("Policy SCHED_FIFO priority %d is unavailable for pid %d tid %d; error %d", - prioEvent->prio(), prioEvent->pid(), prioEvent->tid(), err); + data->mPrio, data->mPid, data->mTid, err); } } break; case CFG_EVENT_IO: { - IoConfigEvent *ioEvent = static_cast(event); - { - Mutex::Autolock _l(mAudioFlinger->mLock); - audioConfigChanged_l(ioEvent->event(), ioEvent->param()); + IoConfigEventData *data = (IoConfigEventData *)event->mData.get(); + audioConfigChanged_l(notificationClients, data->mEvent, data->mParam); + } break; + case CFG_EVENT_SET_PARAMETER: { + SetParameterConfigEventData *data = (SetParameterConfigEventData *)event->mData.get(); + if (checkForNewParameter_l(data->mKeyValuePairs, event->mStatus)) { + configChanged = true; } } break; default: - ALOGE("processConfigEvents() unknown event type %d", event->type()); + ALOG_ASSERT(false, "processConfigEvents_l() unknown event type %d", event->mType); break; } - delete event; - mLock.lock(); + { + Mutex::Autolock _l(event->mLock); + if (event->mWaitStatus) { + event->mWaitStatus = false; + event->mCond.signal(); + } + } + ALOGV_IF(mConfigEvents.isEmpty(), "processConfigEvents_l() DONE thread %p", this); + } + + if (configChanged) { + cacheParameters_l(); } } @@ -502,18 +519,6 @@ void AudioFlinger::ThreadBase::dumpBase(int fd, const Vector& args __u channelMaskToString(mChannelMask, mType != RECORD).string()); fdprintf(fd, " Format: 0x%x (%s)\n", mFormat, formatToString(mFormat)); fdprintf(fd, " Frame size: %zu\n", mFrameSize); - fdprintf(fd, " Pending setParameters commands:"); - size_t numParams = mNewParameters.size(); - if (numParams) { - fdprintf(fd, "\n Index Command"); - for (size_t i = 0; i < numParams; ++i) { - fdprintf(fd, "\n %02zu ", i); - fdprintf(fd, mNewParameters[i]); - } - fdprintf(fd, "\n"); - } else { - fdprintf(fd, " none\n"); - } fdprintf(fd, " Pending config events:"); size_t numConfig = mConfigEvents.size(); if (numConfig) { @@ -1634,7 +1639,10 @@ String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys) } // audioConfigChanged_l() must be called with AudioFlinger::mLock held -void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { +void AudioFlinger::PlaybackThread::audioConfigChanged_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients, + int event, + int param) { AudioSystem::OutputDescriptor desc; void *param2 = NULL; @@ -1649,7 +1657,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { desc.format = mFormat; desc.frameCount = mNormalFrameCount; // FIXME see // AudioFlinger::frameCount(audio_io_handle_t) - desc.latency = latency(); + desc.latency = latency_l(); param2 = &desc; break; @@ -1659,7 +1667,7 @@ void AudioFlinger::PlaybackThread::audioConfigChanged_l(int event, int param) { default: break; } - mAudioFlinger->audioConfigChanged_l(event, mId, param2); + mAudioFlinger->audioConfigChanged_l(notificationClients, event, mId, param2); } void AudioFlinger::PlaybackThread::writeCallback() @@ -2309,12 +2317,16 @@ bool AudioFlinger::PlaybackThread::threadLoop() Vector< sp > effectChains; - processConfigEvents(); + DefaultKeyedVector< pid_t,sp > notificationClients = + mAudioFlinger->notificationClients(); { // scope for mLock Mutex::Autolock _l(mLock); + processConfigEvents_l(notificationClients); + notificationClients.clear(); + if (logString != NULL) { mNBLogWriter->logTimestamp(); mNBLogWriter->log(logString); @@ -2327,10 +2339,6 @@ bool AudioFlinger::PlaybackThread::threadLoop() mLatchQValid = true; } - if (checkForNewParameters_l()) { - cacheParameters_l(); - } - saveOutputTracks(); if (mSignalPending) { // A signal was raised while we were unlocked @@ -3535,128 +3543,117 @@ void AudioFlinger::MixerThread::deleteTrackName_l(int name) mAudioMixer->deleteTrackName(name); } -// checkForNewParameters_l() must be called with ThreadBase::mLock held -bool AudioFlinger::MixerThread::checkForNewParameters_l() +// checkForNewParameter_l() must be called with ThreadBase::mLock held +bool AudioFlinger::MixerThread::checkForNewParameter_l(const String8& keyValuePair, + status_t& status) { - // if !&IDLE, holds the FastMixer state to restore after new parameters processed - FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE; bool reconfig = false; - while (!mNewParameters.isEmpty()) { + status = NO_ERROR; - if (mFastMixer != NULL) { - FastMixerStateQueue *sq = mFastMixer->sq(); - FastMixerState *state = sq->begin(); - if (!(state->mCommand & FastMixerState::IDLE)) { - previousCommand = state->mCommand; - state->mCommand = FastMixerState::HOT_IDLE; - sq->end(); - sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED); - } else { - sq->end(false /*didModify*/); - } + // if !&IDLE, holds the FastMixer state to restore after new parameters processed + FastMixerState::Command previousCommand = FastMixerState::HOT_IDLE; + if (mFastMixer != NULL) { + FastMixerStateQueue *sq = mFastMixer->sq(); + FastMixerState *state = sq->begin(); + if (!(state->mCommand & FastMixerState::IDLE)) { + previousCommand = state->mCommand; + state->mCommand = FastMixerState::HOT_IDLE; + sq->end(); + sq->push(FastMixerStateQueue::BLOCK_UNTIL_ACKED); + } else { + sq->end(false /*didModify*/); } + } - status_t status = NO_ERROR; - String8 keyValuePair = mNewParameters[0]; - AudioParameter param = AudioParameter(keyValuePair); - int value; - - if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + AudioParameter param = AudioParameter(keyValuePair); + int value; + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { + status = BAD_VALUE; + } else { + // no need to save value, since it's constant reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { - if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { - status = BAD_VALUE; - } else { - // no need to save value, since it's constant - reconfig = true; - } - } - if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { - if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) { - status = BAD_VALUE; - } else { - // no need to save value, since it's constant - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + if ((audio_channel_mask_t) value != AUDIO_CHANNEL_OUT_STEREO) { + status = BAD_VALUE; + } else { + // no need to save value, since it's constant + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { - // do not accept frame count changes if tracks are open as the track buffer - // size depends on frame count and correct behavior would not be guaranteed - // if frame count is changed after track creation - if (!mTracks.isEmpty()) { - status = INVALID_OPERATION; - } else { - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be guaranteed + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + } + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { #ifdef ADD_BATTERY_DATA - // when changing the audio output device, call addBatteryData to notify - // the change - if (mOutDevice != value) { - uint32_t params = 0; - // check whether speaker is on - if (value & AUDIO_DEVICE_OUT_SPEAKER) { - params |= IMediaPlayerService::kBatteryDataSpeakerOn; - } + // when changing the audio output device, call addBatteryData to notify + // the change + if (mOutDevice != value) { + uint32_t params = 0; + // check whether speaker is on + if (value & AUDIO_DEVICE_OUT_SPEAKER) { + params |= IMediaPlayerService::kBatteryDataSpeakerOn; + } - audio_devices_t deviceWithoutSpeaker - = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER; - // check if any other device (except speaker) is on - if (value & deviceWithoutSpeaker ) { - params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn; - } + audio_devices_t deviceWithoutSpeaker + = AUDIO_DEVICE_OUT_ALL & ~AUDIO_DEVICE_OUT_SPEAKER; + // check if any other device (except speaker) is on + if (value & deviceWithoutSpeaker ) { + params |= IMediaPlayerService::kBatteryDataOtherAudioDeviceOn; + } - if (params != 0) { - addBatteryData(params); - } + if (params != 0) { + addBatteryData(params); } + } #endif - // forward device change to effects that have requested to be - // aware of attached audio device. - if (value != AUDIO_DEVICE_NONE) { - mOutDevice = value; - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice_l(mOutDevice); - } + // forward device change to effects that have requested to be + // aware of attached audio device. + if (value != AUDIO_DEVICE_NONE) { + mOutDevice = value; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(mOutDevice); } } + } - if (status == NO_ERROR) { + if (status == NO_ERROR) { + status = mOutput->stream->common.set_parameters(&mOutput->stream->common, + keyValuePair.string()); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->stream->common.standby(&mOutput->stream->common); + mStandby = true; + mBytesWritten = 0; status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - if (!mStandby && status == INVALID_OPERATION) { - mOutput->stream->common.standby(&mOutput->stream->common); - mStandby = true; - mBytesWritten = 0; - status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - } - if (status == NO_ERROR && reconfig) { - readOutputParameters_l(); - delete mAudioMixer; - mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); - for (size_t i = 0; i < mTracks.size() ; i++) { - int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId); - if (name < 0) { - break; - } - mTracks[i]->mName = name; + keyValuePair.string()); + } + if (status == NO_ERROR && reconfig) { + readOutputParameters_l(); + delete mAudioMixer; + mAudioMixer = new AudioMixer(mNormalFrameCount, mSampleRate); + for (size_t i = 0; i < mTracks.size() ; i++) { + int name = getTrackName_l(mTracks[i]->mChannelMask, mTracks[i]->mSessionId); + if (name < 0) { + break; } - sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); + mTracks[i]->mName = name; } + sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } - - mNewParameters.removeAt(0); - - mParamStatus = status; - mParamCond.signal(); - // wait for condition with time out in case the thread calling ThreadBase::setParameters() - // already timed out waiting for the status and will never signal the condition. - mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs); } if (!(previousCommand & FastMixerState::IDLE)) { @@ -3946,61 +3943,52 @@ void AudioFlinger::DirectOutputThread::deleteTrackName_l(int name __unused) { } -// checkForNewParameters_l() must be called with ThreadBase::mLock held -bool AudioFlinger::DirectOutputThread::checkForNewParameters_l() +// checkForNewParameter_l() must be called with ThreadBase::mLock held +bool AudioFlinger::DirectOutputThread::checkForNewParameter_l(const String8& keyValuePair, + status_t& status) { bool reconfig = false; - while (!mNewParameters.isEmpty()) { - status_t status = NO_ERROR; - String8 keyValuePair = mNewParameters[0]; - AudioParameter param = AudioParameter(keyValuePair); - int value; - - if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { - // forward device change to effects that have requested to be - // aware of attached audio device. - if (value != AUDIO_DEVICE_NONE) { - mOutDevice = value; - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice_l(mOutDevice); - } + status = NO_ERROR; + + AudioParameter param = AudioParameter(keyValuePair); + int value; + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + // forward device change to effects that have requested to be + // aware of attached audio device. + if (value != AUDIO_DEVICE_NONE) { + mOutDevice = value; + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(mOutDevice); } } - if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { - // do not accept frame count changes if tracks are open as the track buffer - // size depends on frame count and correct behavior would not be garantied - // if frame count is changed after track creation - if (!mTracks.isEmpty()) { - status = INVALID_OPERATION; - } else { - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be garantied + // if frame count is changed after track creation + if (!mTracks.isEmpty()) { + status = INVALID_OPERATION; + } else { + reconfig = true; } - if (status == NO_ERROR) { + } + if (status == NO_ERROR) { + status = mOutput->stream->common.set_parameters(&mOutput->stream->common, + keyValuePair.string()); + if (!mStandby && status == INVALID_OPERATION) { + mOutput->stream->common.standby(&mOutput->stream->common); + mStandby = true; + mBytesWritten = 0; status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - if (!mStandby && status == INVALID_OPERATION) { - mOutput->stream->common.standby(&mOutput->stream->common); - mStandby = true; - mBytesWritten = 0; - status = mOutput->stream->common.set_parameters(&mOutput->stream->common, - keyValuePair.string()); - } - if (status == NO_ERROR && reconfig) { - readOutputParameters_l(); - sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); - } + keyValuePair.string()); + } + if (status == NO_ERROR && reconfig) { + readOutputParameters_l(); + sendIoConfigEvent_l(AudioSystem::OUTPUT_CONFIG_CHANGED); } - - mNewParameters.removeAt(0); - - mParamStatus = status; - mParamCond.signal(); - // wait for condition with time out in case the thread calling ThreadBase::setParameters() - // already timed out waiting for the status and will never signal the condition. - mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs); } + return reconfig; } @@ -4707,12 +4695,14 @@ reacquire_wakelock: // activeTracks accumulates a copy of a subset of mActiveTracks Vector< sp > activeTracks; + DefaultKeyedVector< pid_t,sp > notificationClients = + mAudioFlinger->notificationClients(); + { // scope for mLock Mutex::Autolock _l(mLock); - processConfigEvents_l(); - // return value 'reconfig' is currently unused - bool reconfig = checkForNewParameters_l(); + processConfigEvents_l(notificationClients); + notificationClients.clear(); // check exitPending here because checkForNewParameters_l() and // checkForNewParameters_l() can temporarily release mLock @@ -5486,126 +5476,118 @@ void AudioFlinger::RecordThread::ResamplerBufferProvider::releaseBuffer( buffer->frameCount = 0; } -bool AudioFlinger::RecordThread::checkForNewParameters_l() +bool AudioFlinger::RecordThread::checkForNewParameter_l(const String8& keyValuePair, + status_t& status) { bool reconfig = false; - while (!mNewParameters.isEmpty()) { - status_t status = NO_ERROR; - String8 keyValuePair = mNewParameters[0]; - AudioParameter param = AudioParameter(keyValuePair); - int value; - audio_format_t reqFormat = mFormat; - uint32_t samplingRate = mSampleRate; - audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount); - - // TODO Investigate when this code runs. Check with audio policy when a sample rate and - // channel count change can be requested. Do we mandate the first client defines the - // HAL sampling rate and channel count or do we allow changes on the fly? - if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { - samplingRate = value; + status = NO_ERROR; + + audio_format_t reqFormat = mFormat; + uint32_t samplingRate = mSampleRate; + audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(mChannelCount); + + AudioParameter param = AudioParameter(keyValuePair); + int value; + // TODO Investigate when this code runs. Check with audio policy when a sample rate and + // channel count change can be requested. Do we mandate the first client defines the + // HAL sampling rate and channel count or do we allow changes on the fly? + if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) { + samplingRate = value; + reconfig = true; + } + if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { + if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { + status = BAD_VALUE; + } else { + reqFormat = (audio_format_t) value; reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFormat), value) == NO_ERROR) { - if ((audio_format_t) value != AUDIO_FORMAT_PCM_16_BIT) { - status = BAD_VALUE; - } else { - reqFormat = (audio_format_t) value; - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { + audio_channel_mask_t mask = (audio_channel_mask_t) value; + if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) { + status = BAD_VALUE; + } else { + channelMask = mask; + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) { - audio_channel_mask_t mask = (audio_channel_mask_t) value; - if (mask != AUDIO_CHANNEL_IN_MONO && mask != AUDIO_CHANNEL_IN_STEREO) { - status = BAD_VALUE; - } else { - channelMask = mask; - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { + // do not accept frame count changes if tracks are open as the track buffer + // size depends on frame count and correct behavior would not be guaranteed + // if frame count is changed after track creation + if (mActiveTracks.size() > 0) { + status = INVALID_OPERATION; + } else { + reconfig = true; } - if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) { - // do not accept frame count changes if tracks are open as the track buffer - // size depends on frame count and correct behavior would not be guaranteed - // if frame count is changed after track creation - if (mActiveTracks.size() > 0) { - status = INVALID_OPERATION; - } else { - reconfig = true; - } + } + if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { + // forward device change to effects that have requested to be + // aware of attached audio device. + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setDevice_l(value); } - if (param.getInt(String8(AudioParameter::keyRouting), value) == NO_ERROR) { - // forward device change to effects that have requested to be - // aware of attached audio device. - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setDevice_l(value); - } - // store input device and output device but do not forward output device to audio HAL. - // Note that status is ignored by the caller for output device - // (see AudioFlinger::setParameters() - if (audio_is_output_devices(value)) { - mOutDevice = value; - status = BAD_VALUE; - } else { - mInDevice = value; - // disable AEC and NS if the device is a BT SCO headset supporting those - // pre processings - if (mTracks.size() > 0) { - bool suspend = audio_is_bluetooth_sco_device(mInDevice) && - mAudioFlinger->btNrecIsOff(); - for (size_t i = 0; i < mTracks.size(); i++) { - sp track = mTracks[i]; - setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId()); - setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId()); - } + // store input device and output device but do not forward output device to audio HAL. + // Note that status is ignored by the caller for output device + // (see AudioFlinger::setParameters() + if (audio_is_output_devices(value)) { + mOutDevice = value; + status = BAD_VALUE; + } else { + mInDevice = value; + // disable AEC and NS if the device is a BT SCO headset supporting those + // pre processings + if (mTracks.size() > 0) { + bool suspend = audio_is_bluetooth_sco_device(mInDevice) && + mAudioFlinger->btNrecIsOff(); + for (size_t i = 0; i < mTracks.size(); i++) { + sp track = mTracks[i]; + setEffectSuspended_l(FX_IID_AEC, suspend, track->sessionId()); + setEffectSuspended_l(FX_IID_NS, suspend, track->sessionId()); } } } - if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR && - mAudioSource != (audio_source_t)value) { - // forward device change to effects that have requested to be - // aware of attached audio device. - for (size_t i = 0; i < mEffectChains.size(); i++) { - mEffectChains[i]->setAudioSource_l((audio_source_t)value); - } - mAudioSource = (audio_source_t)value; + } + if (param.getInt(String8(AudioParameter::keyInputSource), value) == NO_ERROR && + mAudioSource != (audio_source_t)value) { + // forward device change to effects that have requested to be + // aware of attached audio device. + for (size_t i = 0; i < mEffectChains.size(); i++) { + mEffectChains[i]->setAudioSource_l((audio_source_t)value); } + mAudioSource = (audio_source_t)value; + } - if (status == NO_ERROR) { + if (status == NO_ERROR) { + status = mInput->stream->common.set_parameters(&mInput->stream->common, + keyValuePair.string()); + if (status == INVALID_OPERATION) { + inputStandBy(); status = mInput->stream->common.set_parameters(&mInput->stream->common, keyValuePair.string()); - if (status == INVALID_OPERATION) { - inputStandBy(); - status = mInput->stream->common.set_parameters(&mInput->stream->common, - keyValuePair.string()); + } + if (reconfig) { + if (status == BAD_VALUE && + reqFormat == mInput->stream->common.get_format(&mInput->stream->common) && + reqFormat == AUDIO_FORMAT_PCM_16_BIT && + (mInput->stream->common.get_sample_rate(&mInput->stream->common) + <= (2 * samplingRate)) && + popcount(mInput->stream->common.get_channels(&mInput->stream->common)) + <= FCC_2 && + (channelMask == AUDIO_CHANNEL_IN_MONO || + channelMask == AUDIO_CHANNEL_IN_STEREO)) { + status = NO_ERROR; } - if (reconfig) { - if (status == BAD_VALUE && - reqFormat == mInput->stream->common.get_format(&mInput->stream->common) && - reqFormat == AUDIO_FORMAT_PCM_16_BIT && - (mInput->stream->common.get_sample_rate(&mInput->stream->common) - <= (2 * samplingRate)) && - popcount(mInput->stream->common.get_channels(&mInput->stream->common)) - <= FCC_2 && - (channelMask == AUDIO_CHANNEL_IN_MONO || - channelMask == AUDIO_CHANNEL_IN_STEREO)) { - status = NO_ERROR; - } - if (status == NO_ERROR) { - readInputParameters_l(); - sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); - } + if (status == NO_ERROR) { + readInputParameters_l(); + sendIoConfigEvent_l(AudioSystem::INPUT_CONFIG_CHANGED); } } - - mNewParameters.removeAt(0); - - mParamStatus = status; - mParamCond.signal(); - // wait for condition with time out in case the thread calling ThreadBase::setParameters() - // already timed out waiting for the status and will never signal the condition. - mWaitWorkCV.waitRelative(mLock, kSetParametersTimeoutNs); } + return reconfig; } @@ -5622,7 +5604,10 @@ String8 AudioFlinger::RecordThread::getParameters(const String8& keys) return out_s8; } -void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param __unused) { +void AudioFlinger::RecordThread::audioConfigChanged_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients, + int event, + int param __unused) { AudioSystem::OutputDescriptor desc; const void *param2 = NULL; @@ -5641,7 +5626,7 @@ void AudioFlinger::RecordThread::audioConfigChanged_l(int event, int param __unu default: break; } - mAudioFlinger->audioConfigChanged_l(event, mId, param2); + mAudioFlinger->audioConfigChanged_l(notificationClients, event, mId, param2); } void AudioFlinger::RecordThread::readInputParameters_l() diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h index 8ea8683..9578993 100644 --- a/services/audioflinger/Threads.h +++ b/services/audioflinger/Threads.h @@ -46,60 +46,121 @@ public: // base for record and playback enum { CFG_EVENT_IO, - CFG_EVENT_PRIO + CFG_EVENT_PRIO, + CFG_EVENT_SET_PARAMETER, }; - class ConfigEvent { + class ConfigEventData: public RefBase { + public: + virtual ~ConfigEventData() {} + + virtual void dump(char *buffer, size_t size) = 0; + protected: + ConfigEventData() {} + }; + + // Config event sequence by client if status needed (e.g binder thread calling setParameters()): + // 1. create SetParameterConfigEvent. This sets mWaitStatus in config event + // 2. Lock mLock + // 3. Call sendConfigEvent_l(): Append to mConfigEvents and mWaitWorkCV.signal + // 4. sendConfigEvent_l() reads status from event->mStatus; + // 5. sendConfigEvent_l() returns status + // 6. Unlock + // + // Parameter sequence by server: threadLoop calling processConfigEvents_l(): + // 1. Lock mLock + // 2. If there is an entry in mConfigEvents proceed ... + // 3. Read first entry in mConfigEvents + // 4. Remove first entry from mConfigEvents + // 5. Process + // 6. Set event->mStatus + // 7. event->mCond.signal + // 8. Unlock + + class ConfigEvent: public RefBase { public: - ConfigEvent(int type) : mType(type) {} virtual ~ConfigEvent() {} - int type() const { return mType; } + void dump(char *buffer, size_t size) { mData->dump(buffer, size); } - virtual void dump(char *buffer, size_t size) = 0; + const int mType; // event type e.g. CFG_EVENT_IO + Mutex mLock; // mutex associated with mCond + Condition mCond; // condition for status return + status_t mStatus; // status communicated to sender + bool mWaitStatus; // true if sender is waiting for status + sp mData; // event specific parameter data - private: - const int mType; + protected: + ConfigEvent(int type) : mType(type), mStatus(NO_ERROR), mWaitStatus(false), mData(NULL) {} }; - class IoConfigEvent : public ConfigEvent { + class IoConfigEventData : public ConfigEventData { public: - IoConfigEvent(int event, int param) : - ConfigEvent(CFG_EVENT_IO), mEvent(event), mParam(param) {} - virtual ~IoConfigEvent() {} - - int event() const { return mEvent; } - int param() const { return mParam; } + IoConfigEventData(int event, int param) : + mEvent(event), mParam(param) {} virtual void dump(char *buffer, size_t size) { snprintf(buffer, size, "IO event: event %d, param %d\n", mEvent, mParam); } - private: const int mEvent; const int mParam; }; - class PrioConfigEvent : public ConfigEvent { + class IoConfigEvent : public ConfigEvent { public: - PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) : - ConfigEvent(CFG_EVENT_PRIO), mPid(pid), mTid(tid), mPrio(prio) {} - virtual ~PrioConfigEvent() {} + IoConfigEvent(int event, int param) : + ConfigEvent(CFG_EVENT_IO) { + mData = new IoConfigEventData(event, param); + } + virtual ~IoConfigEvent() {} + }; - pid_t pid() const { return mPid; } - pid_t tid() const { return mTid; } - int32_t prio() const { return mPrio; } + class PrioConfigEventData : public ConfigEventData { + public: + PrioConfigEventData(pid_t pid, pid_t tid, int32_t prio) : + mPid(pid), mTid(tid), mPrio(prio) {} virtual void dump(char *buffer, size_t size) { snprintf(buffer, size, "Prio event: pid %d, tid %d, prio %d\n", mPid, mTid, mPrio); } - private: const pid_t mPid; const pid_t mTid; const int32_t mPrio; }; + class PrioConfigEvent : public ConfigEvent { + public: + PrioConfigEvent(pid_t pid, pid_t tid, int32_t prio) : + ConfigEvent(CFG_EVENT_PRIO) { + mData = new PrioConfigEventData(pid, tid, prio); + } + virtual ~PrioConfigEvent() {} + }; + + class SetParameterConfigEventData : public ConfigEventData { + public: + SetParameterConfigEventData(String8 keyValuePairs) : + mKeyValuePairs(keyValuePairs) {} + + virtual void dump(char *buffer, size_t size) { + snprintf(buffer, size, "KeyValue: %s\n", mKeyValuePairs.string()); + } + + const String8 mKeyValuePairs; + }; + + class SetParameterConfigEvent : public ConfigEvent { + public: + SetParameterConfigEvent(String8 keyValuePairs) : + ConfigEvent(CFG_EVENT_SET_PARAMETER) { + mData = new SetParameterConfigEventData(keyValuePairs); + mWaitStatus = true; + } + virtual ~SetParameterConfigEvent() {} + }; + class PMDeathRecipient : public IBinder::DeathRecipient { public: @@ -135,15 +196,25 @@ public: // Should be "virtual status_t requestExitAndWait()" and override same // method in Thread, but Thread::requestExitAndWait() is not yet virtual. void exit(); - virtual bool checkForNewParameters_l() = 0; + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status) = 0; virtual status_t setParameters(const String8& keyValuePairs); virtual String8 getParameters(const String8& keys) = 0; - virtual void audioConfigChanged_l(int event, int param = 0) = 0; + virtual void audioConfigChanged_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients, + int event, + int param = 0) = 0; + // sendConfigEvent_l() must be called with ThreadBase::mLock held + // Can temporarily release the lock if waiting for a reply from + // processConfigEvents_l(). + status_t sendConfigEvent_l(sp& event); void sendIoConfigEvent(int event, int param = 0); void sendIoConfigEvent_l(int event, int param = 0); void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio); - void processConfigEvents(); - void processConfigEvents_l(); + status_t sendSetParameterConfigEvent_l(const String8& keyValuePair); + void processConfigEvents_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients); + virtual void cacheParameters_l() = 0; // see note at declaration of mStandby, mOutDevice and mInDevice bool standby() const { return mStandby; } @@ -287,31 +358,7 @@ protected: audio_format_t mFormat; size_t mBufferSize; // HAL buffer size for read() or write() - // Parameter sequence by client: binder thread calling setParameters(): - // 1. Lock mLock - // 2. Append to mNewParameters - // 3. mWaitWorkCV.signal - // 4. mParamCond.waitRelative with timeout - // 5. read mParamStatus - // 6. mWaitWorkCV.signal - // 7. Unlock - // - // Parameter sequence by server: threadLoop calling checkForNewParameters_l(): - // 1. Lock mLock - // 2. If there is an entry in mNewParameters proceed ... - // 2. Read first entry in mNewParameters - // 3. Process - // 4. Remove first entry from mNewParameters - // 5. Set mParamStatus - // 6. mParamCond.signal - // 7. mWaitWorkCV.wait with timeout (this is to avoid overwriting mParamStatus) - // 8. Unlock - Condition mParamCond; - Vector mNewParameters; - status_t mParamStatus; - - // vector owns each ConfigEvent *, so must delete after removing - Vector mConfigEvents; + Vector< sp > mConfigEvents; // These fields are written and read by thread itself without lock or barrier, // and read by other threads without lock or barrier via standby(), outDevice() @@ -455,7 +502,10 @@ public: { return android_atomic_acquire_load(&mSuspended) > 0; } virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged_l(int event, int param = 0); + virtual void audioConfigChanged_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients, + int event, + int param = 0); status_t getRenderPosition(uint32_t *halFrames, uint32_t *dspFrames); // FIXME rename mixBuffer() to sinkBuffer() and remove int16_t* dependency. // Consider also removing and passing an explicit mMainBuffer initialization @@ -724,7 +774,8 @@ public: // Thread virtuals - virtual bool checkForNewParameters_l(); + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status); virtual void dumpInternals(int fd, const Vector& args); protected: @@ -778,7 +829,8 @@ public: // Thread virtuals - virtual bool checkForNewParameters_l(); + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status); protected: virtual int getTrackName_l(audio_channel_mask_t channelMask, int sessionId); @@ -981,9 +1033,14 @@ public: virtual audio_stream_t* stream() const; - virtual bool checkForNewParameters_l(); + virtual bool checkForNewParameter_l(const String8& keyValuePair, + status_t& status); + virtual void cacheParameters_l() {} virtual String8 getParameters(const String8& keys); - virtual void audioConfigChanged_l(int event, int param = 0); + virtual void audioConfigChanged_l( + const DefaultKeyedVector< pid_t,sp >& notificationClients, + int event, + int param = 0); void readInputParameters_l(); virtual uint32_t getInputFramesLost(); -- cgit v1.1