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

Commit 6f9868a7 authored by Phil Burk's avatar Phil Burk Committed by Android (Google) Code Review
Browse files

Merge "AudioTrack and AudioRecord: add stopAndJoinCallbacks()" into sc-dev

parents 9a37b243 7a9577cb
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -308,11 +308,19 @@ aaudio_result_t AudioStreamRecord::release_l() {
}

void AudioStreamRecord::close_l() {
    // The callbacks are normally joined in the AudioRecord destructor.
    // But if another object has a reference to the AudioRecord then
    // it will not get deleted here.
    // So we should join callbacks explicitly before returning.
    // Unlock around the join to avoid deadlocks if the callback tries to lock.
    // This can happen if the callback returns AAUDIO_CALLBACK_RESULT_STOP
    mStreamLock.unlock();
    mAudioRecord->stopAndJoinCallbacks();
    mStreamLock.lock();

    mAudioRecord.clear();
    // Do not close mFixedBlockWriter because a data callback
    // thread might still be running if someone else has a reference
    // to mAudioRecord.
    // It has a unique_ptr to its buffer so it will clean up by itself.
    // Do not close mFixedBlockReader. It has a unique_ptr to its buffer
    // so it will clean up by itself.
    AudioStream::close_l();
}

+11 −5
Original line number Diff line number Diff line
@@ -259,12 +259,18 @@ aaudio_result_t AudioStreamTrack::release_l() {
}

void AudioStreamTrack::close_l() {
    // Stop callbacks before deleting mFixedBlockReader memory.
    // The callbacks are normally joined in the AudioTrack destructor.
    // But if another object has a reference to the AudioTrack then
    // it will not get deleted here.
    // So we should join callbacks explicitly before returning.
    // Unlock around the join to avoid deadlocks if the callback tries to lock.
    // This can happen if the callback returns AAUDIO_CALLBACK_RESULT_STOP
    mStreamLock.unlock();
    mAudioTrack->stopAndJoinCallbacks();
    mStreamLock.lock();
    mAudioTrack.clear();
    // Do not close mFixedBlockReader because a data callback
    // thread might still be running if someone else has a reference
    // to mAudioRecord.
    // It has a unique_ptr to its buffer so it will clean up by itself.
    // Do not close mFixedBlockReader. It has a unique_ptr to its buffer
    // so it will clean up by itself.
    AudioStream::close_l();
}

+23 −14
Original line number Diff line number Diff line
@@ -181,7 +181,25 @@ AudioRecord::~AudioRecord()
        .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
        .record();

    stopAndJoinCallbacks(); // checks mStatus

    if (mStatus == NO_ERROR) {
        IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
        mAudioRecord.clear();
        mCblkMemory.clear();
        mBufferMemory.clear();
        IPCThreadState::self()->flushCommands();
        ALOGV("%s(%d): releasing session id %d",
                __func__, mPortId, mSessionId);
        pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
        AudioSystem::releaseAudioSessionId(mSessionId, pid);
    }
}

void AudioRecord::stopAndJoinCallbacks() {
    // Prevent nullptr crash if it did not open properly.
    if (mStatus != NO_ERROR) return;

    // Make sure that callback function exits in the case where
    // it is looping on buffer empty condition in obtainBuffer().
    // Otherwise the callback thread will never exit.
@@ -194,20 +212,11 @@ AudioRecord::~AudioRecord()
    }
    // No lock here: worst case we remove a NULL callback which will be a nop
    if (mDeviceCallback != 0 && mInput != AUDIO_IO_HANDLE_NONE) {
        // This may not stop all of these device callbacks!
        // TODO: Add some sort of protection.
        AudioSystem::removeAudioDeviceCallback(this, mInput, mPortId);
    }
        IInterface::asBinder(mAudioRecord)->unlinkToDeath(mDeathNotifier, this);
        mAudioRecord.clear();
        mCblkMemory.clear();
        mBufferMemory.clear();
        IPCThreadState::self()->flushCommands();
        ALOGV("%s(%d): releasing session id %d",
                __func__, mPortId, mSessionId);
        pid_t pid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
        AudioSystem::releaseAudioSessionId(mSessionId, pid);
    }
}

status_t AudioRecord::set(
        audio_source_t inputSource,
        uint32_t sampleRate,
+25 −14
Original line number Diff line number Diff line
@@ -327,12 +327,31 @@ AudioTrack::~AudioTrack()
        .set(AMEDIAMETRICS_PROP_STATUS, (int32_t)mStatus)
        .record();

    stopAndJoinCallbacks(); // checks mStatus

    if (mStatus == NO_ERROR) {
        IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
        mAudioTrack.clear();
        mCblkMemory.clear();
        mSharedBuffer.clear();
        IPCThreadState::self()->flushCommands();
        pid_t clientPid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
        ALOGV("%s(%d), releasing session id %d from %d on behalf of %d",
                __func__, mPortId,
                mSessionId, IPCThreadState::self()->getCallingPid(), clientPid);
        AudioSystem::releaseAudioSessionId(mSessionId, clientPid);
    }
}

void AudioTrack::stopAndJoinCallbacks() {
    // Prevent nullptr crash if it did not open properly.
    if (mStatus != NO_ERROR) return;

    // Make sure that callback function exits in the case where
    // it is looping on buffer full condition in obtainBuffer().
    // Otherwise the callback thread will never exit.
    stop();
        if (mAudioTrackThread != 0) {
    if (mAudioTrackThread != 0) { // not thread safe
        mProxy->interrupt();
        mAudioTrackThread->requestExit();   // see comment in AudioTrack.h
        mAudioTrackThread->requestExitAndWait();
@@ -340,18 +359,10 @@ AudioTrack::~AudioTrack()
    }
    // No lock here: worst case we remove a NULL callback which will be a nop
    if (mDeviceCallback != 0 && mOutput != AUDIO_IO_HANDLE_NONE) {
        // This may not stop all of these device callbacks!
        // TODO: Add some sort of protection.
        AudioSystem::removeAudioDeviceCallback(this, mOutput, mPortId);
        }
        IInterface::asBinder(mAudioTrack)->unlinkToDeath(mDeathNotifier, this);
        mAudioTrack.clear();
        mCblkMemory.clear();
        mSharedBuffer.clear();
        IPCThreadState::self()->flushCommands();
        pid_t clientPid = VALUE_OR_FATAL(aidl2legacy_int32_t_pid_t(mClientIdentity.pid));
        ALOGV("%s(%d), releasing session id %d from %d on behalf of %d",
                __func__, mPortId,
                mSessionId, IPCThreadState::self()->getCallingPid(), clientPid);
        AudioSystem::releaseAudioSessionId(mSessionId, clientPid);
        mDeviceCallback.clear();
    }
}

+13 −0
Original line number Diff line number Diff line
@@ -303,6 +303,19 @@ public:
            void        stop();
            bool        stopped() const;

    /* Calls stop() and then wait for all of the callbacks to return.
     * It is safe to call this if stop() or pause() has already been called.
     *
     * This function is called from the destructor. But since AudioRecord
     * is ref counted, the destructor may be called later than desired.
     * This can be called explicitly as part of closing an AudioRecord
     * if you want to be certain that callbacks have completely finished.
     *
     * This is not thread safe and should only be called from one thread,
     * ideally as the AudioRecord is being closed.
     */
            void        stopAndJoinCallbacks();

    /* Return the sink sample rate for this record track in Hz.
     * If specified as zero in constructor or set(), this will be the source sample rate.
     * Unlike AudioTrack, the sample rate is const after initialization, so doesn't need a lock.
Loading