aaudio: fix some state transitions
Now requestStop() and requestPause() do the appropriate thing
based on state.
Flush is allowed when OPEN, PAUSED or STOPPED because there might
be pre-roll data.
Bug: 69810494
Test: test_various.cpp
Change-Id: Ie1b306b17734a58fa71b1742bb186482893656b7
diff --git a/media/libaaudio/src/binding/AAudioServiceMessage.h b/media/libaaudio/src/binding/AAudioServiceMessage.h
index 9779f24..3981454 100644
--- a/media/libaaudio/src/binding/AAudioServiceMessage.h
+++ b/media/libaaudio/src/binding/AAudioServiceMessage.h
@@ -36,7 +36,6 @@
AAUDIO_SERVICE_EVENT_PAUSED,
AAUDIO_SERVICE_EVENT_STOPPED,
AAUDIO_SERVICE_EVENT_FLUSHED,
- AAUDIO_SERVICE_EVENT_CLOSED,
AAUDIO_SERVICE_EVENT_DISCONNECTED,
AAUDIO_SERVICE_EVENT_VOLUME,
AAUDIO_SERVICE_EVENT_XRUN
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 4980e97..b611160 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -340,8 +340,13 @@
}
}
-aaudio_result_t AudioStreamInternal::requestStopInternal()
+aaudio_result_t AudioStreamInternal::requestStop()
{
+ aaudio_result_t result = stopCallback();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("requestStopInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
@@ -355,16 +360,6 @@
return mServiceInterface.stopStream(mServiceStreamHandle);
}
-aaudio_result_t AudioStreamInternal::requestStop()
-{
- aaudio_result_t result = stopCallback();
- if (result != AAUDIO_OK) {
- return result;
- }
- result = requestStopInternal();
- return result;
-}
-
aaudio_result_t AudioStreamInternal::registerThread() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("registerThread() mServiceStreamHandle invalid");
@@ -483,10 +478,6 @@
onFlushFromServer();
}
break;
- case AAUDIO_SERVICE_EVENT_CLOSED:
- ALOGD("%s - got AAUDIO_SERVICE_EVENT_CLOSED", __func__);
- setState(AAUDIO_STREAM_STATE_CLOSED);
- break;
case AAUDIO_SERVICE_EVENT_DISCONNECTED:
// Prevent hardware from looping on old data and making buzzing sounds.
if (getDirection() == AAUDIO_DIRECTION_OUTPUT) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 117756d..0f54f8c 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -121,8 +121,6 @@
aaudio_result_t processCommands();
- aaudio_result_t requestStopInternal();
-
aaudio_result_t stopCallback();
virtual void advanceClientToMatchServerPosition() = 0;
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 5de6a11..5660c1b 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -38,9 +38,12 @@
AudioStreamInternalPlay::~AudioStreamInternalPlay() {}
-
-aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
+aaudio_result_t AudioStreamInternalPlay::requestPause()
{
+ aaudio_result_t result = stopCallback();
+ if (result != AAUDIO_OK) {
+ return result;
+ }
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
mServiceStreamHandle);
@@ -53,16 +56,6 @@
return mServiceInterface.pauseStream(mServiceStreamHandle);
}
-aaudio_result_t AudioStreamInternalPlay::requestPause()
-{
- aaudio_result_t result = stopCallback();
- if (result != AAUDIO_OK) {
- return result;
- }
- result = requestPauseInternal();
- return result;
-}
-
aaudio_result_t AudioStreamInternalPlay::requestFlush() {
if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index d5c1b1e..04e4a62 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -37,6 +37,16 @@
aaudio_result_t requestFlush() override;
+ bool isFlushSupported() const override {
+ // Only implement FLUSH for OUTPUT streams.
+ return true;
+ }
+
+ bool isPauseSupported() const override {
+ // Only implement PAUSE for OUTPUT streams.
+ return true;
+ }
+
aaudio_result_t write(const void *buffer,
int32_t numFrames,
int64_t timeoutNanoseconds) override;
@@ -52,8 +62,6 @@
protected:
- aaudio_result_t requestPauseInternal();
-
void advanceClientToMatchServerPosition() override;
void onFlushFromServer() override;
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 9a2405a..c4465fd 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -130,29 +130,106 @@
}
aaudio_result_t AudioStream::safePause() {
+ if (!isPauseSupported()) {
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
ALOGE("%s cannot be called from a callback!", __func__);
return AAUDIO_ERROR_INVALID_STATE;
}
+
+ switch (getState()) {
+ // Proceed with pausing.
+ case AAUDIO_STREAM_STATE_STARTING:
+ case AAUDIO_STREAM_STATE_STARTED:
+ case AAUDIO_STREAM_STATE_DISCONNECTED:
+ break;
+
+ // Transition from one inactive state to another.
+ case AAUDIO_STREAM_STATE_OPEN:
+ case AAUDIO_STREAM_STATE_STOPPED:
+ case AAUDIO_STREAM_STATE_FLUSHED:
+ setState(AAUDIO_STREAM_STATE_PAUSED);
+ return AAUDIO_OK;
+
+ // Redundant?
+ case AAUDIO_STREAM_STATE_PAUSING:
+ case AAUDIO_STREAM_STATE_PAUSED:
+ return AAUDIO_OK;
+
+ // Don't interfere with transitional states or when closed.
+ case AAUDIO_STREAM_STATE_STOPPING:
+ case AAUDIO_STREAM_STATE_FLUSHING:
+ case AAUDIO_STREAM_STATE_CLOSING:
+ case AAUDIO_STREAM_STATE_CLOSED:
+ default:
+ ALOGW("safePause() stream not running, state = %s",
+ AAudio_convertStreamStateToText(getState()));
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
return requestPause();
}
aaudio_result_t AudioStream::safeFlush() {
+ if (!isFlushSupported()) {
+ ALOGE("flush not supported for this stream");
+ return AAUDIO_ERROR_UNIMPLEMENTED;
+ }
+
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
- ALOGE("%s cannot be called from a callback!", __func__);
+ ALOGE("stream cannot be flushed from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
+
+ aaudio_result_t result = AAudio_isFlushAllowed(getState());
+ if (result != AAUDIO_OK) {
+ return result;
+ }
+
return requestFlush();
}
aaudio_result_t AudioStream::safeStop() {
std::lock_guard<std::mutex> lock(mStreamLock);
if (collidesWithCallback()) {
- ALOGE("%s cannot be called from a callback!", __func__);
+ ALOGE("stream cannot be stopped from a callback!");
return AAUDIO_ERROR_INVALID_STATE;
}
+
+ switch (getState()) {
+ // Proceed with stopping.
+ case AAUDIO_STREAM_STATE_STARTING:
+ case AAUDIO_STREAM_STATE_STARTED:
+ case AAUDIO_STREAM_STATE_DISCONNECTED:
+ break;
+
+ // Transition from one inactive state to another.
+ case AAUDIO_STREAM_STATE_OPEN:
+ case AAUDIO_STREAM_STATE_PAUSED:
+ case AAUDIO_STREAM_STATE_FLUSHED:
+ setState(AAUDIO_STREAM_STATE_STOPPED);
+ return AAUDIO_OK;
+
+ // Redundant?
+ case AAUDIO_STREAM_STATE_STOPPING:
+ case AAUDIO_STREAM_STATE_STOPPED:
+ return AAUDIO_OK;
+
+ // Don't interfere with transitional states or when closed.
+ case AAUDIO_STREAM_STATE_PAUSING:
+ case AAUDIO_STREAM_STATE_FLUSHING:
+ case AAUDIO_STREAM_STATE_CLOSING:
+ case AAUDIO_STREAM_STATE_CLOSED:
+ default:
+ ALOGW("requestStop() stream not running, state = %s",
+ AAudio_convertStreamStateToText(getState()));
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+
return requestStop();
}
@@ -238,6 +315,7 @@
if (err != 0) {
return AAudioConvert_androidToAAudioResult(-errno);
} else {
+ // TODO Use AAudioThread or maybe AndroidThread
// Name the thread with an increasing index, "AAudio_#", for debugging.
static std::atomic<uint32_t> nextThreadIndex{1};
char name[16]; // max length for a pthread_name
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index c59ee6c..c0db0f9 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -73,6 +73,24 @@
*/
virtual aaudio_result_t requestStart() = 0;
+ /**
+ * Check the state to see if Pause if currently legal.
+ *
+ * @param result pointer to return code
+ * @return true if OK to continue, if false then return result
+ */
+ bool checkPauseStateTransition(aaudio_result_t *result);
+
+ virtual bool isFlushSupported() const {
+ // Only implement FLUSH for OUTPUT streams.
+ return false;
+ }
+
+ virtual bool isPauseSupported() const {
+ // Only implement PAUSE for OUTPUT streams.
+ return false;
+ }
+
virtual aaudio_result_t requestPause()
{
// Only implement this for OUTPUT streams.
@@ -341,11 +359,13 @@
return mPlayerBase->getResult();
}
+ // Pass pause request through PlayerBase for tracking.
aaudio_result_t systemPause() {
mPlayerBase->pause();
return mPlayerBase->getResult();
}
+ // Pass stop request through PlayerBase for tracking.
aaudio_result_t systemStop() {
mPlayerBase->stop();
return mPlayerBase->getResult();
@@ -452,7 +472,14 @@
}
void setState(aaudio_stream_state_t state) {
- mState = state;
+ if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+ ; // CLOSED is a final state
+ } else if (mState == AAUDIO_STREAM_STATE_DISCONNECTED
+ && state != AAUDIO_STREAM_STATE_CLOSED) {
+ ; // Once DISCONNECTED, we can only move to CLOSED state.
+ } else {
+ mState = state;
+ }
}
void setDeviceId(int32_t deviceId) {
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 4d1b187..ee069ee 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -287,13 +287,8 @@
if (mAudioTrack.get() == nullptr) {
ALOGE("requestPause() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
- } else if (getState() != AAUDIO_STREAM_STATE_STARTING
- && getState() != AAUDIO_STREAM_STATE_STARTED) {
- // TODO What about DISCONNECTED?
- ALOGE("requestPause(), called when state is %s",
- AAudio_convertStreamStateToText(getState()));
- return AAUDIO_ERROR_INVALID_STATE;
}
+
setState(AAUDIO_STREAM_STATE_PAUSING);
mAudioTrack->pause();
mCallbackEnabled.store(false);
@@ -308,10 +303,8 @@
if (mAudioTrack.get() == nullptr) {
ALOGE("requestFlush() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
- } else if (getState() != AAUDIO_STREAM_STATE_PAUSED) {
- ALOGE("requestFlush() not paused");
- return AAUDIO_ERROR_INVALID_STATE;
}
+
setState(AAUDIO_STREAM_STATE_FLUSHING);
incrementFramesRead(getFramesWritten() - getFramesRead());
mAudioTrack->flush();
@@ -325,6 +318,7 @@
ALOGE("requestStop() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
}
+
setState(AAUDIO_STREAM_STATE_STOPPING);
incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
mTimestampPosition.set(getFramesWritten());
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.h b/media/libaaudio/src/legacy/AudioStreamTrack.h
index a871db4..68608de 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.h
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.h
@@ -48,6 +48,16 @@
aaudio_result_t requestFlush() override;
aaudio_result_t requestStop() override;
+ bool isFlushSupported() const override {
+ // Only implement FLUSH for OUTPUT streams.
+ return true;
+ }
+
+ bool isPauseSupported() const override {
+ // Only implement PAUSE for OUTPUT streams.
+ return true;
+ }
+
aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) override;
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index 2a34016..adc4904 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -441,3 +441,31 @@
}
return prop;
}
+
+aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state) {
+ aaudio_result_t result = AAUDIO_OK;
+ switch (state) {
+// Proceed with flushing.
+ case AAUDIO_STREAM_STATE_OPEN:
+ case AAUDIO_STREAM_STATE_PAUSED:
+ case AAUDIO_STREAM_STATE_STOPPED:
+ case AAUDIO_STREAM_STATE_FLUSHED:
+ break;
+
+// Transition from one inactive state to another.
+ case AAUDIO_STREAM_STATE_STARTING:
+ case AAUDIO_STREAM_STATE_STARTED:
+ case AAUDIO_STREAM_STATE_STOPPING:
+ case AAUDIO_STREAM_STATE_PAUSING:
+ case AAUDIO_STREAM_STATE_FLUSHING:
+ case AAUDIO_STREAM_STATE_CLOSING:
+ case AAUDIO_STREAM_STATE_CLOSED:
+ case AAUDIO_STREAM_STATE_DISCONNECTED:
+ default:
+ ALOGE("can only flush stream when PAUSED, OPEN or STOPPED, state = %s",
+ AAudio_convertStreamStateToText(state));
+ result = AAUDIO_ERROR_INVALID_STATE;
+ break;
+ }
+ return result;
+}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index d3a2ae9..3673c34 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -268,6 +268,14 @@
*/
int32_t AAudioProperty_getHardwareBurstMinMicros();
+
+/**
+ * Is flush allowed for the given state?
+ * @param state
+ * @return AAUDIO_OK if allowed or an error
+ */
+aaudio_result_t AAudio_isFlushAllowed(aaudio_stream_state_t state);
+
/**
* Try a function f until it returns true.
*