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
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 2de878d..eed902f 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -315,11 +315,10 @@
aaudio_result_t result = AAUDIO_OK;
ALOGD("%s(): mServiceStreamHandle = 0x%08X", __func__, mServiceStreamHandle);
if (mServiceStreamHandle != AAUDIO_HANDLE_INVALID) {
- aaudio_stream_state_t currentState = getState();
// Don't release a stream while it is running. Stop it first.
// If DISCONNECTED then we should still try to stop in case the
// error callback is still running.
- if (isActive() || currentState == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (isActive() || isDisconnected()) {
requestStop_l();
}
@@ -432,11 +431,11 @@
return AAUDIO_ERROR_INVALID_STATE;
}
- aaudio_stream_state_t originalState = getState();
- if (originalState == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (isDisconnected()) {
ALOGD("requestStart() but DISCONNECTED");
return AAUDIO_ERROR_DISCONNECTED;
}
+ aaudio_stream_state_t originalState = getState();
setState(AAUDIO_STREAM_STATE_STARTING);
// Clear any stale timestamps from the previous run.
@@ -456,7 +455,7 @@
ALOGD("%s() error = %d, stream was probably stolen", __func__, result);
// Stealing was added in R. Coerce result to improve backward compatibility.
result = AAUDIO_ERROR_DISCONNECTED;
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ setDisconnected();
}
startTime = AudioClock::getNanoseconds();
@@ -473,7 +472,6 @@
result = createThread_l(periodNanos, aaudio_callback_thread_proc, this);
}
if (result != AAUDIO_OK) {
- // TODO(b/214607638): Do we want to roll back to original state or keep as disconnected?
setState(originalState);
}
return result;
@@ -499,8 +497,7 @@
// This must be called under mStreamLock.
aaudio_result_t AudioStreamInternal::stopCallback_l()
{
- if (isDataCallbackSet()
- && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
+ if (isDataCallbackSet() && (isActive() || isDisconnected())) {
mCallbackEnabled.store(false);
aaudio_result_t result = joinThread_l(nullptr); // may temporarily unlock mStreamLock
if (result == AAUDIO_ERROR_INVALID_HANDLE) {
@@ -525,7 +522,7 @@
// and the callback may have stopped the stream.
// Check to make sure the stream still needs to be stopped.
// See also AudioStream::safeStop_l().
- if (!(isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
+ if (!(isActive() || isDisconnected())) {
ALOGD("%s() returning early, not active or disconnected", __func__);
return AAUDIO_OK;
}
@@ -673,7 +670,7 @@
mAudioEndpoint->eraseDataMemory();
}
result = AAUDIO_ERROR_DISCONNECTED;
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ setDisconnected();
ALOGW("%s - AAUDIO_SERVICE_EVENT_DISCONNECTED - FIFO cleared", __func__);
break;
case AAUDIO_SERVICE_EVENT_VOLUME:
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 8a5186a..c9351e0 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -61,10 +61,9 @@
// 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.
LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
- || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
- || getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
- "~AudioStream() - still in use, state = %s",
- AudioGlobal_convertStreamStateToText(getState()));
+ || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED),
+ "~AudioStream() - still in use, state = %s disconnected = %d",
+ AudioGlobal_convertStreamStateToText(getState()), isDisconnected());
}
aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder)
@@ -158,6 +157,11 @@
std::lock_guard<std::mutex> lock(mStreamLock);
+ if (isDisconnected()) {
+ ALOGW("%s() stream is disconnected", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
switch (getState()) {
// Is this a good time to start?
case AAUDIO_STREAM_STATE_OPEN:
@@ -176,8 +180,13 @@
AudioGlobal_convertStreamStateToText(getState()));
return AAUDIO_ERROR_INVALID_STATE;
- // Don't start when the stream is dead!
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_CLOSED:
default:
@@ -210,7 +219,11 @@
// Proceed with pausing.
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
+ break;
+
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;
// Transition from one inactive state to another.
@@ -289,7 +302,10 @@
// Proceed with stopping.
case AAUDIO_STREAM_STATE_STARTING:
case AAUDIO_STREAM_STATE_STARTED:
+ break;
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;
// Transition from one inactive state to another.
@@ -369,13 +385,8 @@
if (state == oldState) {
return; // no change
}
- // Track transition to DISCONNECTED state.
- if (state == AAUDIO_STREAM_STATE_DISCONNECTED) {
- android::mediametrics::LogItem(mMetricsId)
- .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_DISCONNECT)
- .set(AMEDIAMETRICS_PROP_STATE, AudioGlobal_convertStreamStateToText(oldState))
- .record();
- }
+ LOG_ALWAYS_FATAL_IF(state == AAUDIO_STREAM_STATE_DISCONNECTED,
+ "Disconnected state must be separated from mState");
// CLOSED is a final state
if (oldState == AAUDIO_STREAM_STATE_CLOSED) {
ALOGW("%s(%d) tried to set to %d but already CLOSED", __func__, getId(), state);
@@ -385,12 +396,6 @@
&& state != AAUDIO_STREAM_STATE_CLOSED) {
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 {
mState.store(state);
// Wake up a wakeForStateChange thread if it exists.
@@ -398,6 +403,21 @@
}
}
+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_stream_state_t *nextState,
int64_t timeoutNanoseconds)
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index e36928d..50f6aa0 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -558,6 +558,11 @@
void setState(aaudio_stream_state_t state);
+ bool isDisconnected() const {
+ return mDisconnected.load();
+ }
+ void setDisconnected();
+
void setDeviceId(int32_t deviceId) {
mDeviceId = deviceId;
}
@@ -683,6 +688,8 @@
std::atomic<aaudio_stream_state_t> mState{AAUDIO_STREAM_STATE_UNINITIALIZED};
+ std::atomic_bool mDisconnected{false};
+
// These do not change after open().
int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
aaudio_channel_mask_t mChannelMask = AAUDIO_UNSPECIFIED;
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index f32ef65..8595308 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -85,7 +85,7 @@
// AudioRecord::Buffer
// TODO define our own AudioBuffer and pass it from the subclasses.
size_t written = buffer.size();
- if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (isDisconnected()) {
ALOGW("%s() data, stream disconnected", __func__);
// This will kill the stream and prevent it from being restarted.
// That is OK because the stream is disconnected.
@@ -150,7 +150,7 @@
// AudioRecord::Buffer
// TODO define our own AudioBuffer and pass it from the subclasses.
size_t written = buffer.size();
- if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (isDisconnected()) {
ALOGW("%s() data, stream disconnected", __func__);
// This will kill the stream and prevent it from being restarted.
// That is OK because the stream is disconnected.
@@ -214,11 +214,11 @@
void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
// 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_CLOSED
) {
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ setDisconnected();
if (errorCallbackEnabled) {
maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
}
@@ -268,7 +268,7 @@
ALOGD("%s(deviceId = %d)", __func__, (int)deviceId);
if (getDeviceId() != AAUDIO_UNSPECIFIED
&& getDeviceId() != deviceId
- && getState() != AAUDIO_STREAM_STATE_DISCONNECTED
+ && !isDisconnected()
) {
// 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
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 9a136a7..da152b0 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -403,7 +403,7 @@
return result;
}
- if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (isDisconnected()) {
return AAUDIO_ERROR_DISCONNECTED;
}
@@ -446,7 +446,7 @@
// In this context, a DEAD_OBJECT is more likely to be a disconnect notification due to
// AudioRecord invalidation.
if (bytesActuallyRead == DEAD_OBJECT) {
- setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+ setDisconnected();
return AAUDIO_ERROR_DISCONNECTED;
}
return AAudioConvert_androidToAAudioResult(bytesActuallyRead);
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index fb3fcc1..10bd5f7 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -248,7 +248,7 @@
if (getState() != AAUDIO_STREAM_STATE_UNINITIALIZED) {
ALOGE("%s - Open canceled since state = %d", __func__, getState());
- if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED)
+ if (isDisconnected())
{
ALOGE("%s - Opening while state is disconnected", __func__);
safeReleaseClose();
@@ -432,7 +432,7 @@
return result;
}
- if (getState() == AAUDIO_STREAM_STATE_DISCONNECTED) {
+ if (isDisconnected()) {
return AAUDIO_ERROR_DISCONNECTED;
}
@@ -446,7 +446,7 @@
// 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);
+ setDisconnected();
return AAUDIO_ERROR_DISCONNECTED;
}
return AAudioConvert_androidToAAudioResult(bytesWritten);