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

Commit 717bc28f authored by Eric Laurent's avatar Eric Laurent
Browse files

audio flinger: handle race condition in AudioRecord creation

A rare race condition can cause a failure to create an AudioRecord if
two creation requests are processed concurrently and thread preemption happens
at a specific time within the sequence between audioflinger and
audio policy manager.

This CL implements a retry mechanism on client side when this particular failure
is detected.

Bug: 161656190
Test: Regression tests for audio capture use cases.
Test: CTS tests for AudiRecord
Change-Id: I155ee4899baeec21e10a1141cb9e323cbc8aa23b
(cherry picked from commit d52a28ca)
parent 0b3f2540
Loading
Loading
Loading
Loading
+19 −8
Original line number Diff line number Diff line
@@ -742,6 +742,8 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
    void *iMemPointer;
    audio_track_cblk_t* cblk;
    status_t status;
    static const int32_t kMaxCreateAttempts = 3;
    int32_t remainingAttempts = kMaxCreateAttempts;

    if (audioFlinger == 0) {
        ALOGE("%s(%d): Could not get audioflinger", __func__, mPortId);
@@ -803,15 +805,24 @@ status_t AudioRecord::createRecord_l(const Modulo<uint32_t> &epoch, const String
    input.sessionId = mSessionId;
    originalSessionId = mSessionId;

    record = audioFlinger->createRecord(input,
                                                              output,
                                                              &status);

    if (status != NO_ERROR) {
    do {
        record = audioFlinger->createRecord(input, output, &status);
        if (status == NO_ERROR) {
            break;
        }
        if (status != FAILED_TRANSACTION || --remainingAttempts <= 0) {
            ALOGE("%s(%d): AudioFlinger could not create record track, status: %d",
                  __func__, mPortId, status);
            goto exit;
        }
        // FAILED_TRANSACTION happens under very specific conditions causing a state mismatch
        // between audio policy manager and audio flinger during the input stream open sequence
        // and can be recovered by retrying.
        // Leave time for race condition to clear before retrying and randomize delay
        // to reduce the probability of concurrent retries in locked steps.
        usleep((20 + rand() % 30) * 10000);
    } while (1);

    ALOG_ASSERT(record != 0);

    // AudioFlinger now owns the reference to the I/O handle,
+2 −2
Original line number Diff line number Diff line
@@ -2068,8 +2068,8 @@ sp<media::IAudioRecord> AudioFlinger::createRecord(const CreateRecordInput& inpu
        Mutex::Autolock _l(mLock);
        RecordThread *thread = checkRecordThread_l(output.inputId);
        if (thread == NULL) {
            ALOGE("createRecord() checkRecordThread_l failed, input handle %d", output.inputId);
            lStatus = BAD_VALUE;
            ALOGW("createRecord() checkRecordThread_l failed, input handle %d", output.inputId);
            lStatus = FAILED_TRANSACTION;
            goto Exit;
        }

+4 −2
Original line number Diff line number Diff line
@@ -7869,7 +7869,8 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
        AutoMutex lock(mLock);
        if (recordTrack->isInvalid()) {
            recordTrack->clearSyncStartEvent();
            return INVALID_OPERATION;
            ALOGW("%s track %d: invalidated before startInput", __func__, recordTrack->portId());
            return DEAD_OBJECT;
        }
        if (mActiveTracks.indexOf(recordTrack) >= 0) {
            if (recordTrack->mState == TrackBase::PAUSING) {
@@ -7899,7 +7900,8 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac
                    recordTrack->mState = TrackBase::STARTING_2;
                    // STARTING_2 forces destroy to call stopInput.
                }
                return INVALID_OPERATION;
                ALOGW("%s track %d: invalidated after startInput", __func__, recordTrack->portId());
                return DEAD_OBJECT;
            }
            if (recordTrack->mState != TrackBase::STARTING_1) {
                ALOGW("%s(%d): unsynchronized mState:%d change",
+2 −1
Original line number Diff line number Diff line
@@ -2207,7 +2207,8 @@ status_t AudioFlinger::RecordThread::RecordTrack::start(AudioSystem::sync_event_
        RecordThread *recordThread = (RecordThread *)thread.get();
        return recordThread->start(this, event, triggerSession);
    } else {
        return BAD_VALUE;
        ALOGW("%s track %d: thread was destroyed", __func__, portId());
        return DEAD_OBJECT;
    }
}

+1 −1
Original line number Diff line number Diff line
@@ -2256,7 +2256,7 @@ status_t AudioPolicyManager::startInput(audio_port_handle_t portId)
    sp<AudioInputDescriptor> inputDesc = mInputs.getInputForClient(portId);
    if (inputDesc == 0) {
        ALOGW("%s no input for client %d", __FUNCTION__, portId);
        return BAD_VALUE;
        return DEAD_OBJECT;
    }
    audio_io_handle_t input = inputDesc->mIoHandle;
    sp<RecordClientDescriptor> client = inputDesc->getClient(portId);