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

Commit cb212cd8 authored by jiabin's avatar jiabin
Browse files

Separate mDisconnected from mState.

The DISCONNECTED state was preventing the pause and stop
from finishing. The DISCONNECTED status is really orthogonal
to the other states like STARTED and STOPPED and needs to
be tracked separately.

Bug: 214607638
Test: atest AAudioTests
Test: TEST DISCONNECT from OboeTester
Change-Id: Ie49c1d2ee95db2bbd4285b8d003aae24e1b6e48c
parent bb2443ef
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -277,6 +277,7 @@ enum
    AAUDIO_STREAM_STATE_CLOSED,
    AAUDIO_STREAM_STATE_CLOSED,
    /**
    /**
     * The stream is disconnected from audio device.
     * The stream is disconnected from audio device.
     * @deprecated
     */
     */
    AAUDIO_STREAM_STATE_DISCONNECTED
    AAUDIO_STREAM_STATE_DISCONNECTED
};
};
+7 −10
Original line number Original line Diff line number Diff line
@@ -315,11 +315,10 @@ aaudio_result_t AudioStreamInternal::release_l() {
    aaudio_result_t result = AAUDIO_OK;
    aaudio_result_t result = AAUDIO_OK;
    ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
    ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
    if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
    if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
        aaudio_stream_state_t currentState = getState();
        // Don't release a stream while it is running. Stop it first.
        // Don't release a stream while it is running. Stop it first.
        // If DISCONNECTED then we should still try to stop in case the
        // If DISCONNECTED then we should still try to stop in case the
        // error callback is still running.
        // error callback is still running.
        if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) {
        if (isActive() || isDisconnected()) {
            requestStop_l();
            requestStop_l();
        }
        }


@@ -432,11 +431,11 @@ aaudio_result_t AudioStreamInternal::requestStart_l()
        return AAUDIO_ERROR_INVALID_STATE;
        return AAUDIO_ERROR_INVALID_STATE;
    }
    }


    aaudio_stream_state_t originalState = getState();
    if (isDisconnected()) {
    if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
        ALOGD("requestStart() but DISCONNECTED");
        ALOGD("requestStart() but DISCONNECTED");
        return AAUDIO_ERROR_DISCONNECTED;
        return AAUDIO_ERROR_DISCONNECTED;
    }
    }
    aaudio_stream_state_t originalState = getState();
    setState(AAUDIO_STREAM_STATE_STARTING);
    setState(AAUDIO_STREAM_STATE_STARTING);


    // Clear any stale timestamps from the previous run.
    // Clear any stale timestamps from the previous run.
@@ -456,7 +455,7 @@ aaudio_result_t AudioStreamInternal::requestStart_l()
        ALOGD("%s() error = %d, stream was probably stolen", __func__, result);
        ALOGD("%s() error = %d, stream was probably stolen", __func__, result);
        // Stealing was added in R. Coerce result to improve backward compatibility.
        // Stealing was added in R. Coerce result to improve backward compatibility.
        result = AAUDIO_ERROR_DISCONNECTED;
        result = AAUDIO_ERROR_DISCONNECTED;
        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
        setDisconnected();
    }
    }


    startTime = AudioClock::getNanoseconds();
    startTime = AudioClock::getNanoseconds();
@@ -473,7 +472,6 @@ aaudio_result_t AudioStreamInternal::requestStart_l()
        result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
        result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
    }
    }
    if (result != AAUDIO_OK) {
    if (result != AAUDIO_OK) {
        // TODO(b/214607638): Do we want to roll back to original state or keep as disconnected?
        setState(originalState);
        setState(originalState);
    }
    }
    return result;
    return result;
@@ -499,8 +497,7 @@ int64_t AudioStreamInternal::calculateReasonableTimeout() {
// This must be called under mStreamLock.
// This must be called under mStreamLock.
aaudio_result_t AudioStreamInternal::stopCallback_l()
aaudio_result_t AudioStreamInternal::stopCallback_l()
{
{
    if (isDataCallbackSet()
    if (isDataCallbackSet() && (isActive() || isDisconnected())) {
            && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
        mCallbackEnabled.store(false);
        mCallbackEnabled.store(false);
        aaudio_result_t result = joinThread_l(nullptr); // may temporarily unlock mStreamLock
        aaudio_result_t result = joinThread_l(nullptr); // may temporarily unlock mStreamLock
        if (result == AAUDIO_ERROR_INVALID_HANDLE) {
        if (result == AAUDIO_ERROR_INVALID_HANDLE) {
@@ -525,7 +522,7 @@ aaudio_result_t AudioStreamInternal::requestStop_l() {
    // and the callback may have stopped the stream.
    // and the callback may have stopped the stream.
    // Check to make sure the stream still needs to be stopped.
    // Check to make sure the stream still needs to be stopped.
    // See also AudioStream::safeStop_l().
    // See also AudioStream::safeStop_l().
    if (!(isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
    if (!(isActive() || isDisconnected())) {
        ALOGD("%s() returning early, not active or disconnected", __func__);
        ALOGD("%s() returning early, not active or disconnected", __func__);
        return AAUDIO_OK;
        return AAUDIO_OK;
    }
    }
@@ -673,7 +670,7 @@ aaudio_result_t AudioStreamInternal::onEventFromServer(AAudioServiceMessage *mes
                mAudioEndpoint->eraseDataMemory();
                mAudioEndpoint->eraseDataMemory();
            }
            }
            result = AAUDIO_ERROR_DISCONNECTED;
            result = AAUDIO_ERROR_DISCONNECTED;
            setState(AAUDIO_STREAM_STATE_DISCONNECTED);
            setDisconnected();
            ALOGW("%s - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared", __func__);
            ALOGW("%s - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared", __func__);
            break;
            break;
        case AAUDIO_SERVICE_EVENT_VOLUME:
        case AAUDIO_SERVICE_EVENT_VOLUME:
+38 −18
Original line number Original line Diff line number Diff line
@@ -61,10 +61,9 @@ AudioStream::~AudioStream() {
    // If the stream is deleted when OPEN or in use then audio resources will leak.
    // If the stream is deleted when OPEN or in use then audio resources will leak.
    // This would indicate an internal error. So we want to find this ASAP.
    // This would indicate an internal error. So we want to find this ASAP.
    LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
    LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
                          || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
                          || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED),
                          || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
                        "~AudioStream() - still in use, state = %s disconnected = %d",
                        "~AudioStream() - still in use, state = %s",
                        AudioGlobal_convertStreamStateToText(getState()), isDisconnected());
                        AudioGlobal_convertStreamStateToText(getState()));
}
}


aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
@@ -158,6 +157,11 @@ aaudio_result_t AudioStream::systemStart() {


    std::lock_guard<std::mutex> lock(mStreamLock);
    std::lock_guard<std::mutex> lock(mStreamLock);


    if (isDisconnected()) {
        ALOGW("%s() stream is disconnected", __func__);
        return AAUDIO_ERROR_INVALID_STATE;
    }

    switch (getState()) {
    switch (getState()) {
        // Is this a good time to start?
        // Is this a good time to start?
        case AAUDIO_STREAM_STATE_OPEN:
        case AAUDIO_STREAM_STATE_OPEN:
@@ -176,8 +180,13 @@ aaudio_result_t AudioStream::systemStart() {
                  AudioGlobal_convertStreamStateToText(getState()));
                  AudioGlobal_convertStreamStateToText(getState()));
            return AAUDIO_ERROR_INVALID_STATE;
            return AAUDIO_ERROR_INVALID_STATE;


        // Don't start when the stream is dead!
        case AAUDIO_STREAM_STATE_DISCONNECTED:
        case AAUDIO_STREAM_STATE_DISCONNECTED:
            // This must not happen after deprecating AAUDIO_STREAM_STATE_DISCONNECTED, trying to
            // start will finally return ERROR_DISCONNECTED.
            ALOGE("%s, unexpected state = AAUDIO_STREAM_STATE_DISCONNECTED", __func__);
            return AAUDIO_ERROR_INTERNAL;

        // Don't start when the stream is dead!
        case AAUDIO_STREAM_STATE_CLOSING:
        case AAUDIO_STREAM_STATE_CLOSING:
        case AAUDIO_STREAM_STATE_CLOSED:
        case AAUDIO_STREAM_STATE_CLOSED:
        default:
        default:
@@ -210,7 +219,11 @@ aaudio_result_t AudioStream::systemPause() {
        // Proceed with pausing.
        // Proceed with pausing.
        case AAUDIO_STREAM_STATE_STARTING:
        case AAUDIO_STREAM_STATE_STARTING:
        case AAUDIO_STREAM_STATE_STARTED:
        case AAUDIO_STREAM_STATE_STARTED:
            break;

        case AAUDIO_STREAM_STATE_DISCONNECTED:
        case AAUDIO_STREAM_STATE_DISCONNECTED:
            // This must not happen after deprecating AAUDIO_STREAM_STATE_DISCONNECTED
            ALOGE("%s, unexpected state = AAUDIO_STREAM_STATE_DISCONNECTED", __func__);
            break;
            break;


            // Transition from one inactive state to another.
            // Transition from one inactive state to another.
@@ -289,7 +302,10 @@ aaudio_result_t AudioStream::safeStop_l() {
        // Proceed with stopping.
        // Proceed with stopping.
        case AAUDIO_STREAM_STATE_STARTING:
        case AAUDIO_STREAM_STATE_STARTING:
        case AAUDIO_STREAM_STATE_STARTED:
        case AAUDIO_STREAM_STATE_STARTED:
            break;
        case AAUDIO_STREAM_STATE_DISCONNECTED:
        case AAUDIO_STREAM_STATE_DISCONNECTED:
            // This must not happen after deprecating AAUDIO_STREAM_STATE_DISCONNECTED
            ALOGE("%s, unexpected state = AAUDIO_STREAM_STATE_DISCONNECTED", __func__);
            break;
            break;


        // Transition from one inactive state to another.
        // Transition from one inactive state to another.
@@ -369,13 +385,8 @@ void AudioStream::setState(aaudio_stream_state_t state) {
    if (state == oldState) {
    if (state == oldState) {
        return; // no change
        return; // no change
    }
    }
    // Track transition to DISCONNECTED state.
    LOG_ALWAYS_FATAL_IF(state == AAUDIO_STREAM_STATE_DISCONNECTED,
    if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
                        "Disconnected state must be separated from mState");
        android::mediametrics::LogItem(mMetricsId)
                .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
                .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(oldState))
                .record();
    }
    // CLOSED is a final state
    // CLOSED is a final state
    if (oldState == AAUDIO_STREAM_STATE_CLOSED) {
    if (oldState == AAUDIO_STREAM_STATE_CLOSED) {
        ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
        ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
@@ -385,12 +396,6 @@ void AudioStream::setState(aaudio_stream_state_t state) {
               && state != AAUDIO_STREAM_STATE_CLOSED) {
               && state != AAUDIO_STREAM_STATE_CLOSED) {
        ALOGW("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);
        ALOGW("%s(%d) tried to set to %d but already CLOSING", __func__, getId(), state);


    // Once DISCONNECTED, we can only move to CLOSING or CLOSED state.
    } else if (oldState == AAUDIO_STREAM_STATE_DISCONNECTED
               && !(state == AAUDIO_STREAM_STATE_CLOSING
                   || state == AAUDIO_STREAM_STATE_CLOSED)) {
        ALOGW("%s(%d) tried to set to %d but already DISCONNECTED", __func__, getId(), state);

    } else {
    } else {
        mState.store(state);
        mState.store(state);
        // Wake up a wakeForStateChange thread if it exists.
        // Wake up a wakeForStateChange thread if it exists.
@@ -398,6 +403,21 @@ void AudioStream::setState(aaudio_stream_state_t state) {
    }
    }
}
}


void AudioStream::setDisconnected() {
    const bool old = isDisconnected();
    ALOGD("%s setting disconnected, current disconnected: %d, current state: %d",
          __func__, old, getState());
    if (old) {
        return; // no change, the stream is already disconnected
    }
    mDisconnected.store(true);
    // Track transition to DISCONNECTED state.
    android::mediametrics::LogItem(mMetricsId)
            .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
            .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(getState()))
            .record();
}

aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
                                                aaudio_stream_state_t *nextState,
                                                aaudio_stream_state_t *nextState,
                                                int64_t timeoutNanoseconds)
                                                int64_t timeoutNanoseconds)
+7 −0
Original line number Original line Diff line number Diff line
@@ -558,6 +558,11 @@ protected:


    void setState(aaudio_stream_state_t state);
    void setState(aaudio_stream_state_t state);


    bool isDisconnected() const {
        return mDisconnected.load();
    }
    void setDisconnected();

    void setDeviceId(int32_t deviceId) {
    void setDeviceId(int32_t deviceId) {
        mDeviceId = deviceId;
        mDeviceId = deviceId;
    }
    }
@@ -683,6 +688,8 @@ private:


    std::atomic<aaudio_stream_state_t>          mState{AAUDIO_STREAM_STATE_UNINITIALIZED};
    std::atomic<aaudio_stream_state_t>          mState{AAUDIO_STREAM_STATE_UNINITIALIZED};


    std::atomic_bool            mDisconnected{false};

    // These do not change after open().
    // These do not change after open().
    int32_t                     mSamplesPerFrame = AAUDIO_UNSPECIFIED;
    int32_t                     mSamplesPerFrame = AAUDIO_UNSPECIFIED;
    aaudio_channel_mask_t       mChannelMask = AAUDIO_UNSPECIFIED;
    aaudio_channel_mask_t       mChannelMask = AAUDIO_UNSPECIFIED;
+5 −5
Original line number Original line Diff line number Diff line
@@ -85,7 +85,7 @@ size_t AudioStreamLegacy::onMoreData(const android::AudioTrack::Buffer& buffer)
    // AudioRecord::Buffer
    // AudioRecord::Buffer
    // TODO define our own AudioBuffer and pass it from the subclasses.
    // TODO define our own AudioBuffer and pass it from the subclasses.
    size_t written = buffer.size();
    size_t written = buffer.size();
    if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
    if (isDisconnected()) {
        ALOGW("%s() data, stream disconnected", __func__);
        ALOGW("%s() data, stream disconnected", __func__);
        // This will kill the stream and prevent it from being restarted.
        // This will kill the stream and prevent it from being restarted.
        // That is OK because the stream is disconnected.
        // That is OK because the stream is disconnected.
@@ -150,7 +150,7 @@ size_t AudioStreamLegacy::onMoreData(const android::AudioRecord::Buffer& buffer)
    // AudioRecord::Buffer
    // AudioRecord::Buffer
    // TODO define our own AudioBuffer and pass it from the subclasses.
    // TODO define our own AudioBuffer and pass it from the subclasses.
    size_t written = buffer.size();
    size_t written = buffer.size();
    if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
    if (isDisconnected()) {
        ALOGW("%s() data, stream disconnected", __func__);
        ALOGW("%s() data, stream disconnected", __func__);
        // This will kill the stream and prevent it from being restarted.
        // This will kill the stream and prevent it from being restarted.
        // That is OK because the stream is disconnected.
        // That is OK because the stream is disconnected.
@@ -214,11 +214,11 @@ aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackE


void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
    // There is no need to disconnect if already in these states.
    // There is no need to disconnect if already in these states.
    if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED
    if (!isDisconnected()
            && getState() != AAUDIO_STREAM_STATE_CLOSING
            && getState() != AAUDIO_STREAM_STATE_CLOSING
            && getState() != AAUDIO_STREAM_STATE_CLOSED
            && getState() != AAUDIO_STREAM_STATE_CLOSED
            ) {
            ) {
        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
        setDisconnected();
        if (errorCallbackEnabled) {
        if (errorCallbackEnabled) {
            maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
            maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
        }
        }
@@ -268,7 +268,7 @@ void AudioStreamLegacy::onAudioDeviceUpdate(audio_io_handle_t /* audioIo */,
    ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
    ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
    if (getDeviceId() != AAUDIO_UNSPECIFIED
    if (getDeviceId() != AAUDIO_UNSPECIFIED
            && getDeviceId() != deviceId
            && getDeviceId() != deviceId
            && getState() != AAUDIO_STREAM_STATE_DISCONNECTED
            && !isDisconnected()
            ) {
            ) {
        // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
        // Note that isDataCallbackActive() is affected by state so call it before DISCONNECTING.
        // If we have a data callback and the stream is active, then ask the data callback
        // If we have a data callback and the stream is active, then ask the data callback
Loading