Fix issue 4111672: AudioTrack control block flags

Make sure that all read/modify/write operations on the AudioTrack
and AudioRecord control block flags field are protected by the
control block's mutex.

Also fix potential infinite loop in AudioTrack::write() if the
written size is not a multiple of frame size.

Change-Id: Ib3d557eb45dcc3abeb32c9aa56058e2873afee27
This commit is contained in:
Eric Laurent
2011-03-17 09:36:51 -07:00
parent a30f43624f
commit 913af0b48f
3 changed files with 40 additions and 18 deletions

View File

@ -722,9 +722,12 @@ bool AudioRecord::processAudioBuffer(const sp<ClientRecordThread>& thread)
// Manage overrun callback // Manage overrun callback
if (mActive && (cblk->framesAvailable() == 0)) { if (mActive && (cblk->framesAvailable() == 0)) {
LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); LOGV("Overrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
AutoMutex _l(cblk->lock);
if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
mCbf(EVENT_OVERRUN, mUserData, 0);
cblk->flags |= CBLK_UNDERRUN_ON; cblk->flags |= CBLK_UNDERRUN_ON;
cblk->lock.unlock();
mCbf(EVENT_OVERRUN, mUserData, 0);
cblk->lock.lock();
} }
} }

View File

@ -329,6 +329,7 @@ void AudioTrack::start()
if (mActive == 0) { if (mActive == 0) {
mActive = 1; mActive = 1;
mNewPosition = cblk->server + mUpdatePeriod; mNewPosition = cblk->server + mUpdatePeriod;
cblk->lock.lock();
cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS; cblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;
cblk->waitTimeMs = 0; cblk->waitTimeMs = 0;
cblk->flags &= ~CBLK_DISABLED_ON; cblk->flags &= ~CBLK_DISABLED_ON;
@ -339,7 +340,6 @@ void AudioTrack::start()
} }
LOGV("start %p before lock cblk %p", this, mCblk); LOGV("start %p before lock cblk %p", this, mCblk);
cblk->lock.lock();
if (!(cblk->flags & CBLK_INVALID_MSK)) { if (!(cblk->flags & CBLK_INVALID_MSK)) {
cblk->lock.unlock(); cblk->lock.unlock();
status = mAudioTrack->start(); status = mAudioTrack->start();
@ -892,10 +892,15 @@ create_new_track:
} }
// restart track if it was disabled by audioflinger due to previous underrun // restart track if it was disabled by audioflinger due to previous underrun
if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
AutoMutex _l(cblk->lock);
if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) { if (mActive && (cblk->flags & CBLK_DISABLED_MSK)) {
cblk->flags &= ~CBLK_DISABLED_ON; cblk->flags &= ~CBLK_DISABLED_ON;
cblk->lock.unlock();
LOGW("obtainBuffer() track %p disabled, restarting", this); LOGW("obtainBuffer() track %p disabled, restarting", this);
mAudioTrack->start(); mAudioTrack->start();
cblk->lock.lock();
}
} }
cblk->waitTimeMs = 0; cblk->waitTimeMs = 0;
@ -957,9 +962,10 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
ssize_t written = 0; ssize_t written = 0;
const int8_t *src = (const int8_t *)buffer; const int8_t *src = (const int8_t *)buffer;
Buffer audioBuffer; Buffer audioBuffer;
size_t frameSz = (size_t)frameSize();
do { do {
audioBuffer.frameCount = userSize/frameSize(); audioBuffer.frameCount = userSize/frameSz;
// Calling obtainBuffer() with a negative wait count causes // Calling obtainBuffer() with a negative wait count causes
// an (almost) infinite wait time. // an (almost) infinite wait time.
@ -991,7 +997,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
written += toWrite; written += toWrite;
releaseBuffer(&audioBuffer); releaseBuffer(&audioBuffer);
} while (userSize); } while (userSize >= frameSz);
return written; return written;
} }
@ -1015,12 +1021,15 @@ bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
// Manage underrun callback // Manage underrun callback
if (mActive && (cblk->framesReady() == 0)) { if (mActive && (cblk->framesReady() == 0)) {
LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags); LOGV("Underrun user: %x, server: %x, flags %04x", cblk->user, cblk->server, cblk->flags);
AutoMutex _l(cblk->lock);
if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) { if ((cblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {
cblk->flags |= CBLK_UNDERRUN_ON;
cblk->lock.unlock();
mCbf(EVENT_UNDERRUN, mUserData, 0); mCbf(EVENT_UNDERRUN, mUserData, 0);
if (cblk->server == cblk->frameCount) { if (cblk->server == cblk->frameCount) {
mCbf(EVENT_BUFFER_END, mUserData, 0); mCbf(EVENT_BUFFER_END, mUserData, 0);
} }
cblk->flags |= CBLK_UNDERRUN_ON; cblk->lock.lock();
if (mSharedBuffer != 0) return false; if (mSharedBuffer != 0) return false;
} }
} }
@ -1279,7 +1288,12 @@ uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)
this->user = u; this->user = u;
// Clear flow control error condition as new data has been written/read to/from buffer. // Clear flow control error condition as new data has been written/read to/from buffer.
if (flags & CBLK_UNDERRUN_MSK) {
AutoMutex _l(lock);
if (flags & CBLK_UNDERRUN_MSK) {
flags &= ~CBLK_UNDERRUN_MSK; flags &= ~CBLK_UNDERRUN_MSK;
}
}
return u; return u;
} }

View File

@ -1738,7 +1738,10 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track
LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this); LOGV("BUFFER TIMEOUT: remove(%d) from active list on thread %p", track->name(), this);
tracksToRemove->add(track); tracksToRemove->add(track);
// indicate to client process that the track was disabled because of underrun // indicate to client process that the track was disabled because of underrun
{
AutoMutex _l(cblk->lock);
cblk->flags |= CBLK_DISABLED_ON; cblk->flags |= CBLK_DISABLED_ON;
}
} else if (mixerStatus != MIXER_TRACKS_READY) { } else if (mixerStatus != MIXER_TRACKS_READY) {
mixerStatus = MIXER_TRACKS_ENABLED; mixerStatus = MIXER_TRACKS_ENABLED;
} }
@ -1787,10 +1790,9 @@ void AudioFlinger::MixerThread::invalidateTracks(int streamType)
for (size_t i = 0; i < size; i++) { for (size_t i = 0; i < size; i++) {
sp<Track> t = mTracks[i]; sp<Track> t = mTracks[i];
if (t->type() == streamType) { if (t->type() == streamType) {
t->mCblk->lock.lock(); AutoMutex _lcblk(t->mCblk->lock);
t->mCblk->flags |= CBLK_INVALID_ON; t->mCblk->flags |= CBLK_INVALID_ON;
t->mCblk->cv.signal(); t->mCblk->cv.signal();
t->mCblk->lock.unlock();
} }
} }
} }
@ -2948,6 +2950,7 @@ bool AudioFlinger::PlaybackThread::Track::isReady() const {
if (mCblk->framesReady() >= mCblk->frameCount || if (mCblk->framesReady() >= mCblk->frameCount ||
(mCblk->flags & CBLK_FORCEREADY_MSK)) { (mCblk->flags & CBLK_FORCEREADY_MSK)) {
AutoMutex _l(mCblk->lock);
mFillingUpStatus = FS_FILLED; mFillingUpStatus = FS_FILLED;
mCblk->flags &= ~CBLK_FORCEREADY_MSK; mCblk->flags &= ~CBLK_FORCEREADY_MSK;
return true; return true;
@ -3063,19 +3066,18 @@ void AudioFlinger::PlaybackThread::Track::flush()
// STOPPED state // STOPPED state
mState = STOPPED; mState = STOPPED;
mCblk->lock.lock();
// NOTE: reset() will reset cblk->user and cblk->server with // NOTE: reset() will reset cblk->user and cblk->server with
// the risk that at the same time, the AudioMixer is trying to read // the risk that at the same time, the AudioMixer is trying to read
// data. In this case, getNextBuffer() would return a NULL pointer // data. In this case, getNextBuffer() would return a NULL pointer
// as audio buffer => the AudioMixer code MUST always test that pointer // as audio buffer => the AudioMixer code MUST always test that pointer
// returned by getNextBuffer() is not NULL! // returned by getNextBuffer() is not NULL!
reset(); reset();
mCblk->lock.unlock();
} }
} }
void AudioFlinger::PlaybackThread::Track::reset() void AudioFlinger::PlaybackThread::Track::reset()
{ {
AutoMutex _l(mCblk->lock);
// Do not reset twice to avoid discarding data written just after a flush and before // Do not reset twice to avoid discarding data written just after a flush and before
// the audioflinger thread detects the track is stopped. // the audioflinger thread detects the track is stopped.
if (!mResetDone) { if (!mResetDone) {
@ -3209,11 +3211,14 @@ void AudioFlinger::RecordThread::RecordTrack::stop()
if (thread != 0) { if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get(); RecordThread *recordThread = (RecordThread *)thread.get();
recordThread->stop(this); recordThread->stop(this);
{
AutoMutex _l(mCblk->lock);
TrackBase::reset(); TrackBase::reset();
// Force overerrun condition to avoid false overrun callback until first data is // Force overerrun condition to avoid false overrun callback until first data is
// read from buffer // read from buffer
mCblk->flags |= CBLK_UNDERRUN_ON; mCblk->flags |= CBLK_UNDERRUN_ON;
} }
}
} }
void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size)