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

Commit 05ce0941 authored by Eric Laurent's avatar Eric Laurent
Browse files

226483: A2DP connected, but music out to speaker

When the A2DP headset is connected, there is a possible
race condition when the audio tracks are moved from
the mixer thread attached to the speaker output to the thread
attached to A2DP output.
As the request to clear the stream type to output mapping cache in
the client process is asynchronous, it is possible that the flag
indicating to the client audio track to re-create the IAudioTrack
on the new thread is processed before the cache is invalidated.
In this case, the track will be attached to the old thread and
music will continue playing over the device speaker instead of being
redirected to A2DP headset.

Change-Id: Ib2ce1eb5320eaff83287b93779061bf4e7a330df
parent 2c5423da
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -185,6 +185,10 @@ public:
    static status_t unregisterEffect(int id);
    static status_t setEffectEnabled(int id, bool enabled);

    // clear stream to output mapping cache (gStreamOutputMap)
    // and output configuration cache (gOutputs)
    static void clearAudioConfigCache();

    static const sp<IAudioPolicyService>& get_audio_policy_service();

    // ----------------------------------------------------------------------------
@@ -236,7 +240,8 @@ private:

    // mapping between stream types and outputs
    static DefaultKeyedVector<int, audio_io_handle_t> gStreamOutputMap;
    // list of output descritor containing cached parameters (sampling rate, framecount, channel count...)
    // list of output descriptors containing cached parameters
    // (sampling rate, framecount, channel count...)
    static DefaultKeyedVector<audio_io_handle_t, OutputDescriptor *> gOutputs;
};

+8 −0
Original line number Diff line number Diff line
@@ -727,6 +727,14 @@ status_t AudioSystem::isStreamActive(int stream, bool* state, uint32_t inPastMs)
}


void AudioSystem::clearAudioConfigCache()
{
    Mutex::Autolock _l(gLock);
    LOGV("clearAudioConfigCache()");
    gStreamOutputMap.clear();
    gOutputs.clear();
}

// ---------------------------------------------------------------------------

void AudioSystem::AudioPolicyServiceClient::binderDied(const wp<IBinder>& who) {
+4 −0
Original line number Diff line number Diff line
@@ -1164,6 +1164,10 @@ status_t AudioTrack::restoreTrack_l(audio_track_cblk_t*& cblk, bool fromStart)
        cblk->cv.broadcast();
        cblk->lock.unlock();

        // refresh the audio configuration cache in this process to make sure we get new
        // output parameters in getOutput_l() and createTrack_l()
        AudioSystem::clearAudioConfigCache();

        // if the new IAudioTrack is created, createTrack_l() will modify the
        // following member variables: mAudioTrack, mCblkMemory and mCblk.
        // It will also delete the strong references on previous IAudioTrack and IMemory
+20 −0
Original line number Diff line number Diff line
@@ -1362,6 +1362,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge
    for (int stream = 0; stream < AUDIO_STREAM_CNT; stream++) {
        mStreamTypes[stream].volume = mAudioFlinger->streamVolumeInternal(stream);
        mStreamTypes[stream].mute = mAudioFlinger->streamMute(stream);
        mStreamTypes[stream].valid = true;
    }
}

@@ -1530,6 +1531,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra
            chain->setStrategy(AudioSystem::getStrategyForStream((audio_stream_type_t)track->type()));
            chain->incTrackCnt();
        }

        // invalidate track immediately if the stream type was moved to another thread since
        // createTrack() was called by the client process.
        if (!mStreamTypes[streamType].valid) {
            LOGW("createTrack_l() on thread %p: invalidating track on stream %d",
                 this, streamType);
            android_atomic_or(CBLK_INVALID_ON, &track->mCblk->flags);
        }
    }
    lStatus = NO_ERROR;

@@ -2219,6 +2228,14 @@ void AudioFlinger::MixerThread::invalidateTracks(int streamType)
    }
}

void AudioFlinger::PlaybackThread::setStreamValid(int streamType, bool valid)
{
    LOGV ("PlaybackThread::setStreamValid() thread %p, streamType %d, valid %d",
            this,  streamType, valid);
    Mutex::Autolock _l(mLock);

    mStreamTypes[streamType].valid = valid;
}

// getTrackName_l() must be called with ThreadBase::mLock held
int AudioFlinger::MixerThread::getTrackName_l()
@@ -5074,11 +5091,14 @@ status_t AudioFlinger::setStreamOutput(uint32_t stream, int output)
    LOGV("setStreamOutput() stream %d to output %d", stream, output);
    audioConfigChanged_l(AudioSystem::STREAM_CONFIG_CHANGED, output, &stream);

    dstThread->setStreamValid(stream, true);

    for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
        PlaybackThread *thread = mPlaybackThreads.valueAt(i).get();
        if (thread != dstThread &&
            thread->type() != ThreadBase::DIRECT) {
            MixerThread *srcThread = (MixerThread *)thread;
            srcThread->setStreamValid(stream, false);
            srcThread->invalidateTracks(stream);
        }
    }
+5 −1
Original line number Diff line number Diff line
@@ -751,14 +751,18 @@ private:
                    virtual uint32_t hasAudioSession(int sessionId);
                    virtual uint32_t getStrategyForSession_l(int sessionId);

                            void setStreamValid(int streamType, bool valid);

        struct  stream_type_t {
            stream_type_t()
                :   volume(1.0f),
                    mute(false)
                    mute(false),
                    valid(true)
            {
            }
            float       volume;
            bool        mute;
            bool        valid;
        };

    protected: