Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ede6c3b8 authored by Eric Laurent's avatar Eric Laurent
Browse files

audioflinger: fix lost offload thread resume event

It was possible that a resume request signaled by addTrack_l()
while waiting for an async write callback is lost. This is because
mSignalPending was not set and waitingAsyncCallback_l() would pause the
thread loop before executing prepareTracks_l().

The fix consists in using signal_l() method to wake the thread
loop o that mSignalPending is set.

Also make sure that sleepTime is reset to 0 when resuming to make sure
that we write any remaining bytes to the HAL.

Bug: 10810347.

Change-Id: If9a3b22cc3b9e6eb384a56c48c40e6258d0896ad
parent 5baf2af5
Loading
Loading
Loading
Loading
+32 −27
Original line number Diff line number Diff line
@@ -960,6 +960,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
        mUseAsyncWrite(false),
        mWriteAckSequence(0),
        mDrainSequence(0),
        mSignalPending(false),
        mScreenState(AudioFlinger::mScreenState),
        // index 0 is reserved for normal mixer's submix
        mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
@@ -1348,14 +1349,14 @@ void AudioFlinger::PlaybackThread::setStreamVolume(audio_stream_type_t stream, f
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].volume = value;
    signal_l();
    broadcast_l();
}

void AudioFlinger::PlaybackThread::setStreamMute(audio_stream_type_t stream, bool muted)
{
    Mutex::Autolock _l(mLock);
    mStreamTypes[stream].mute = muted;
    signal_l();
    broadcast_l();
}

float AudioFlinger::PlaybackThread::streamVolume(audio_stream_type_t stream) const
@@ -1413,8 +1414,8 @@ status_t AudioFlinger::PlaybackThread::addTrack_l(const sp<Track>& track)
        status = NO_ERROR;
    }

    ALOGV("mWaitWorkCV.broadcast");
    mWaitWorkCV.broadcast();
    ALOGV("signal playback thread");
    broadcast_l();

    return status;
}
@@ -1455,14 +1456,14 @@ void AudioFlinger::PlaybackThread::removeTrack_l(const sp<Track>& track)
    }
}

void AudioFlinger::PlaybackThread::signal_l()
void AudioFlinger::PlaybackThread::broadcast_l()
{
    // Thread could be blocked waiting for async
    // so signal it to handle state changes immediately
    // If threadLoop is currently unlocked a signal of mWaitWorkCV will
    // be lost so we also flag to prevent it blocking on mWaitWorkCV
    mSignalPending = true;
    mWaitWorkCV.signal();
    mWaitWorkCV.broadcast();
}

String8 AudioFlinger::PlaybackThread::getParameters(const String8& keys)
@@ -2143,7 +2144,6 @@ bool AudioFlinger::PlaybackThread::threadLoop()
            }

            saveOutputTracks();

            if (mSignalPending) {
                // A signal was raised while we were unlocked
                mSignalPending = false;
@@ -2158,10 +2158,10 @@ bool AudioFlinger::PlaybackThread::threadLoop()
                acquireWakeLock_l();
                standbyTime = systemTime() + standbyDelay;
                sleepTime = 0;
                if (exitPending()) {
                    break;

                continue;
            }
            } else if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
            if ((!mActiveTracks.size() && systemTime() > standbyTime) ||
                                   isSuspended()) {
                // put audio hardware into standby after short delay
                if (shouldStandby_l()) {
@@ -2203,7 +2203,6 @@ bool AudioFlinger::PlaybackThread::threadLoop()
                    continue;
                }
            }

            // mMixerStatusIgnoringFastTracks is also updated internally
            mMixerStatus = prepareTracks_l(&tracksToRemove);

@@ -3855,13 +3854,14 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr
    Vector< sp<Track> > *tracksToRemove
)
{
    ALOGV("OffloadThread::prepareTracks_l");
    size_t count = mActiveTracks.size();

    mixer_state mixerStatus = MIXER_IDLE;
    bool doHwPause = false;
    bool doHwResume = false;

    ALOGV("OffloadThread::prepareTracks_l active tracks %d", count);

    // find out which tracks need to be processed
    for (size_t i = 0; i < count; i++) {
        sp<Track> t = mActiveTracks[i].promote();
@@ -3915,23 +3915,27 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr
                // make sure processVolume_l() will apply new volume even if 0
                mLeftVolFloat = mRightVolFloat = -1.0;
                if (track->mState == TrackBase::RESUMING) {
                    track->mState = TrackBase::ACTIVE;
                    if (last) {
                        if (mPausedBytesRemaining) {
                            // Need to continue write that was interrupted
                            mCurrentWriteLength = mPausedWriteLength;
                            mBytesRemaining = mPausedBytesRemaining;
                            mPausedBytesRemaining = 0;
                        }
                    track->mState = TrackBase::ACTIVE;
                }
            }

            if (last) {
                        if (mHwPaused) {
                            doHwResume = true;
                            mHwPaused = false;
                            // threadLoop_mix() will handle the case that we need to
                            // resume an interrupted write
                        }
                        // enable write to audio HAL
                        sleepTime = 0;
                    }
                }
            }

            if (last) {
                // reset retry count
                track->mRetryCount = kMaxTrackRetriesOffload;
                mActiveTrack = t;
@@ -3948,9 +3952,9 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::OffloadThread::prepareTr
                    // has been written
                    ALOGV("OffloadThread: underrun and STOPPING_1 -> draining, STOPPING_2");
                    track->mState = TrackBase::STOPPING_2; // so presentation completes after drain
                    if (last) {
                        sleepTime = 0;
                        standbyTime = systemTime() + standbyDelay;
                    if (last) {
                        mixerStatus = MIXER_DRAIN_TRACK;
                        mDrainSequence += 2;
                        if (mHwPaused) {
@@ -4337,6 +4341,7 @@ bool AudioFlinger::RecordThread::threadLoop()
                    mStandby = false;
                }
            }

            lockEffectChains_l(effectChains);
        }

+3 −1
Original line number Diff line number Diff line
@@ -526,7 +526,7 @@ private:
    status_t    addTrack_l(const sp<Track>& track);
    bool        destroyTrack_l(const sp<Track>& track);
    void        removeTrack_l(const sp<Track>& track);
    void        signal_l();
    void        broadcast_l();

    void        readOutputParameters();

@@ -590,6 +590,8 @@ private:
    // Bit 0 is reset by the async callback thread calling resetDraining(). Out of sequence
    // callbacks are ignored.
    uint32_t                        mDrainSequence;
    // A condition that must be evaluated by prepareTrack_l() has changed and we must not wait
    // for async write callback in the thread loop before evaluating it
    bool                            mSignalPending;
    sp<AsyncCallbackThread>         mCallbackThread;

+2 −2
Original line number Diff line number Diff line
@@ -651,7 +651,7 @@ void AudioFlinger::PlaybackThread::Track::pause()
        case RESUMING:
            mState = PAUSING;
            ALOGV("ACTIVE/RESUMING => PAUSING (%d) on thread %p", mName, thread.get());
            playbackThread->signal_l();
            playbackThread->broadcast_l();
            break;

        default:
@@ -711,7 +711,7 @@ void AudioFlinger::PlaybackThread::Track::flush()
        // before mixer thread can run. This is important when offloading
        // because the hardware buffer could hold a large amount of audio
        playbackThread->flushOutput_l();
        playbackThread->signal_l();
        playbackThread->broadcast_l();
    }
}