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

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

aaudio: fix device switch detection in legacy path

Implement device switch detection on legacy path (AudioTrack and
AudioRecord) based on audio routing callbacks forcing the stream state
to disconnected.

Bug: 33355262
Bug: 62090113
Test: tested with write_sine and input_monitor command line tools.
Change-Id: I9e0421fee233964b1bf318acb640569196a00f13
parent d08f04f3
Loading
Loading
Loading
Loading
+57 −28
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ using namespace android;
using namespace aaudio;

AudioStreamLegacy::AudioStreamLegacy()
        : AudioStream() {
        : AudioStream(), mDeviceCallback(new StreamDeviceCallback(this)) {
}

AudioStreamLegacy::~AudioStreamLegacy() {
@@ -60,9 +60,16 @@ int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes

void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
    aaudio_data_callback_result_t callbackResult;

    if (!mCallbackEnabled.load()) {
        return;
    }

    switch (opcode) {
        case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
            // Note that this code assumes an AudioTrack::Buffer is the same as AudioRecord::Buffer
            if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
                // Note that this code assumes an AudioTrack::Buffer is the same as
                // AudioRecord::Buffer
                // TODO define our own AudioBuffer and pass it from the subclasses.
                AudioTrack::Buffer *audioBuffer = static_cast<AudioTrack::Buffer *>(info);
                if (audioBuffer->frameCount == 0) return;
@@ -70,8 +77,8 @@ void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode
                // If the caller specified an exact size then use a block size adapter.
                if (mBlockAdapter != nullptr) {
                    int32_t byteCount = audioBuffer->frameCount * getBytesPerFrame();
                callbackResult = mBlockAdapter->processVariableBlock((uint8_t *) audioBuffer->raw,
                                                                     byteCount);
                    callbackResult = mBlockAdapter->processVariableBlock(
                            (uint8_t *) audioBuffer->raw, byteCount);
                } else {
                    // Call using the AAudio callback interface.
                    callbackResult = (*getDataCallbackProc())(
@@ -87,17 +94,20 @@ void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode
                } else {
                    audioBuffer->size = 0;
                }
        }
                break;
            }
        }
        /// FALL THROUGH

            // Stream got rerouted so we disconnect.
        case AAUDIO_CALLBACK_OPERATION_DISCONNECTED: {
            ALOGD("AudioStreamAAudio(): callbackLoop() stream disconnected");
            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
            ALOGD("processCallbackCommon() stream disconnected");
            if (getErrorCallbackProc() != nullptr) {
                (*getErrorCallbackProc())(
                        (AAudioStream *) this,
                        getErrorCallbackUserData(),
                        AAUDIO_OK
                        AAUDIO_ERROR_DISCONNECTED
                        );
            }
            mCallbackEnabled.store(false);
@@ -129,3 +139,22 @@ aaudio_result_t AudioStreamLegacy::getBestTimestamp(clockid_t clockId,
    status_t status = extendedTimestamp->getBestTimestamp(framePosition, timeNanoseconds, timebase);
    return AAudioConvert_androidToAAudioResult(status);
}

void AudioStreamLegacy::onAudioDeviceUpdate(audio_port_handle_t deviceId)
{
    ALOGD("onAudioDeviceUpdate() deviceId %d", (int)deviceId);
    if (getDeviceId() != AAUDIO_UNSPECIFIED && getDeviceId() != deviceId &&
            getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
        // if we have a data callback and the stream is active, send the error callback from
        // data callback thread when it sees the DISCONNECTED state
        if (!isDataCallbackActive() && getErrorCallbackProc() != nullptr) {
            (*getErrorCallbackProc())(
                    (AAudioStream *) this,
                    getErrorCallbackUserData(),
                    AAUDIO_ERROR_DISCONNECTED
                    );
        }
    }
    setDeviceId(deviceId);
}
+24 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#define LEGACY_AUDIO_STREAM_LEGACY_H

#include <media/AudioTimestamp.h>
#include <media/AudioSystem.h>

#include <aaudio/AAudio.h>

@@ -75,14 +76,37 @@ public:

protected:

    class StreamDeviceCallback : public android::AudioSystem::AudioDeviceCallback
    {
    public:

        StreamDeviceCallback(AudioStreamLegacy *parent) : mParent(parent) {}
        virtual ~StreamDeviceCallback() {}

        virtual void onAudioDeviceUpdate(audio_io_handle_t audioIo __unused,
                                         audio_port_handle_t deviceId) {
            if (mParent != nullptr) {
                mParent->onAudioDeviceUpdate(deviceId);
            }
        }

        AudioStreamLegacy *mParent;
    };

    aaudio_result_t getBestTimestamp(clockid_t clockId,
                                     int64_t *framePosition,
                                     int64_t *timeNanoseconds,
                                     android::ExtendedTimestamp *extendedTimestamp);

    void onAudioDeviceUpdate(audio_port_handle_t deviceId);

    void onStart() { mCallbackEnabled.store(true); }
    void onStop() { mCallbackEnabled.store(false); }

    FixedBlockAdapter         *mBlockAdapter = nullptr;
    aaudio_wrapping_frames_t   mPositionWhenStarting = 0;
    int32_t                    mCallbackBufferSize = 0;
    const android::sp<StreamDeviceCallback>   mDeviceCallback;
};

} /* namespace aaudio */
+21 −1
Original line number Diff line number Diff line
@@ -99,15 +99,21 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder)
    ALOGD("AudioStreamRecord::open(), request notificationFrames = %u, frameCount = %u",
          notificationFrames, (uint)frameCount);
    mAudioRecord = new AudioRecord(
            mOpPackageName // const String16& opPackageName TODO does not compile
            );
    if (getDeviceId() != AAUDIO_UNSPECIFIED) {
        mAudioRecord->setInputDevice(getDeviceId());
    }
    mAudioRecord->set(
            AUDIO_SOURCE_VOICE_RECOGNITION,
            getSampleRate(),
            format,
            channelMask,
            mOpPackageName, // const String16& opPackageName TODO does not compile
            frameCount,
            callback,
            callbackData,
            notificationFrames,
            false /*threadCanCallJava*/,
            AUDIO_SESSION_ALLOCATE,
            streamTransferType,
            flags
@@ -162,6 +168,8 @@ aaudio_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder)
             perfMode, actualPerformanceMode);

    setState(AAUDIO_STREAM_STATE_OPEN);
    setDeviceId(mAudioRecord->getRoutedDeviceId());
    mAudioRecord->addAudioDeviceCallback(mDeviceCallback);

    return AAUDIO_OK;
}
@@ -209,6 +217,7 @@ aaudio_result_t AudioStreamRecord::requestStart()
    if (err != OK) {
        return AAudioConvert_androidToAAudioResult(err);
    } else {
        onStart();
        setState(AAUDIO_STREAM_STATE_STARTING);
    }
    return AAUDIO_OK;
@@ -230,6 +239,7 @@ aaudio_result_t AudioStreamRecord::requestStop() {
    if (mAudioRecord.get() == nullptr) {
        return AAUDIO_ERROR_INVALID_STATE;
    }
    onStop();
    setState(AAUDIO_STREAM_STATE_STOPPING);
    incrementFramesWritten(getFramesRead() - getFramesWritten()); // TODO review
    mAudioRecord->stop();
@@ -274,12 +284,22 @@ aaudio_result_t AudioStreamRecord::read(void *buffer,
        return result;
    }

    if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
        return AAUDIO_ERROR_DISCONNECTED;
    }

    // TODO add timeout to AudioRecord
    bool blocking = (timeoutNanoseconds > 0);
    ssize_t bytesRead = mAudioRecord->read(buffer, numBytes, blocking);
    if (bytesRead == WOULD_BLOCK) {
        return 0;
    } else if (bytesRead < 0) {
        // in this context, a DEAD_OBJECT is more likely to be a disconnect notification due to
        // AudioRecord invalidation
        if (bytesRead == DEAD_OBJECT) {
            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
            return AAUDIO_ERROR_DISCONNECTED;
        }
        return AAudioConvert_androidToAAudioResult(bytesRead);
    }
    int32_t framesRead = (int32_t)(bytesRead / bytesPerFrame);
+21 −1
Original line number Diff line number Diff line
@@ -115,7 +115,11 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)

    ALOGD("AudioStreamTrack::open(), request notificationFrames = %d, frameCount = %u",
          notificationFrames, (uint)frameCount);
    mAudioTrack = new AudioTrack(
    mAudioTrack = new AudioTrack();
    if (getDeviceId() != AAUDIO_UNSPECIFIED) {
        mAudioTrack->setOutputDevice(getDeviceId());
    }
    mAudioTrack->set(
            (audio_stream_type_t) AUDIO_STREAM_MUSIC,
            getSampleRate(),
            format,
@@ -125,6 +129,8 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)
            callback,
            callbackData,
            notificationFrames,
            0 /*sharedBuffer*/,
            false /*threadCanCallJava*/,
            AUDIO_SESSION_ALLOCATE,
            streamTransferType
            );
@@ -160,6 +166,7 @@ aaudio_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)

    setState(AAUDIO_STREAM_STATE_OPEN);
    setDeviceId(mAudioTrack->getRoutedDeviceId());
    mAudioTrack->addAudioDeviceCallback(mDeviceCallback);

    // Update performance mode based on the actual stream.
    // For example, if the sample rate is not allowed then you won't get a FAST track.
@@ -229,6 +236,7 @@ aaudio_result_t AudioStreamTrack::requestStart()
    if (err != OK) {
        return AAudioConvert_androidToAAudioResult(err);
    } else {
        onStart();
        setState(AAUDIO_STREAM_STATE_STARTING);
    }
    return AAUDIO_OK;
@@ -246,6 +254,7 @@ aaudio_result_t AudioStreamTrack::requestPause()
              AAudio_convertStreamStateToText(getState()));
        return AAUDIO_ERROR_INVALID_STATE;
    }
    onStop();
    setState(AAUDIO_STREAM_STATE_PAUSING);
    mAudioTrack->pause();
    status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
@@ -276,6 +285,7 @@ aaudio_result_t AudioStreamTrack::requestStop() {
    if (mAudioTrack.get() == nullptr) {
        return AAUDIO_ERROR_INVALID_STATE;
    }
    onStop();
    setState(AAUDIO_STREAM_STATE_STOPPING);
    incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
    mAudioTrack->stop();
@@ -339,6 +349,10 @@ aaudio_result_t AudioStreamTrack::write(const void *buffer,
        return result;
    }

    if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
        return AAUDIO_ERROR_DISCONNECTED;
    }

    // TODO add timeout to AudioTrack
    bool blocking = timeoutNanoseconds > 0;
    ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
@@ -346,6 +360,12 @@ aaudio_result_t AudioStreamTrack::write(const void *buffer,
        return 0;
    } else if (bytesWritten < 0) {
        ALOGE("invalid write, returned %d", (int)bytesWritten);
        // in this context, a DEAD_OBJECT is more likely to be a disconnect notification due to
        // AudioTrack invalidation
        if (bytesWritten == DEAD_OBJECT) {
            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
            return AAUDIO_ERROR_DISCONNECTED;
        }
        return AAudioConvert_androidToAAudioResult(bytesWritten);
    }
    int32_t framesWritten = (int32_t)(bytesWritten / bytesPerFrame);
+7 −5
Original line number Diff line number Diff line
@@ -481,6 +481,7 @@ status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) {
    AutoMutex lock(mLock);
    if (mSelectedDeviceId != deviceId) {
        mSelectedDeviceId = deviceId;
        if (mStatus == NO_ERROR) {
            // stop capture so that audio policy manager does not reject the new instance start request
            // as only one capture can be active at a time.
            if (mAudioRecord != 0 && mActive) {
@@ -488,6 +489,7 @@ status_t AudioRecord::setInputDevice(audio_port_handle_t deviceId) {
            }
            android_atomic_or(CBLK_INVALID, &mCblk->mFlags);
        }
    }
    return NO_ERROR;
}

Loading