aaudio: prevent stop/close from a callback
Detect whether AAudioStream_requestStop() is being called
from a data or error callback and return error.
Also for start(), pause(), flush() and close().
Honor AAUDIO_CALLBACK_RESULT_STOP in Legacy callback.
Bug: 65560631
Bug: 62943389
Bug: 63096718
Test: test_various.cpp callback_blocker_none and callback_blocker_lowlat
Change-Id: I29a685cd42910f70d16e23db0f9ec00b72451cfc
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index b7b4b5c..6d5a64f 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -171,7 +171,7 @@
mClockModel.setSampleRate(getSampleRate());
mClockModel.setFramesPerBurst(mFramesPerBurst);
- if (getDataCallbackProc()) {
+ if (isDataCallbackSet()) {
mCallbackFrames = builder.getFramesPerDataCallback();
if (mCallbackFrames > getBufferCapacity() / 2) {
ALOGE("%s - framesPerCallback too big = %d, capacity = %d",
@@ -290,7 +290,7 @@
mNeedCatchUp.request(); // Ask data processing code to catch up when first timestamp received.
// Start data callback thread.
- if (result == AAUDIO_OK && getDataCallbackProc() != nullptr) {
+ if (result == AAUDIO_OK && isDataCallbackSet()) {
// Launch the callback loop thread.
int64_t periodNanos = mCallbackFrames
* AAUDIO_NANOS_PER_SECOND
diff --git a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
index 3e82a88..62f0fc8 100644
--- a/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalCapture.cpp
@@ -232,8 +232,7 @@
void *AudioStreamInternalCapture::callbackLoop() {
aaudio_result_t result = AAUDIO_OK;
aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
- AAudioStream_dataCallback appCallback = getDataCallbackProc();
- if (appCallback == nullptr) return NULL;
+ if (!isDataCallbackSet()) return NULL;
// result might be a frame count
while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
@@ -249,22 +248,12 @@
// Only read some of the frames requested. Must have timed out.
result = AAUDIO_ERROR_TIMEOUT;
}
- AAudioStream_errorCallback errorCallback = getErrorCallbackProc();
- if (errorCallback != nullptr) {
- (*errorCallback)(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- result);
- }
+ maybeCallErrorCallback(result);
break;
}
// Call application using the AAudio callback interface.
- callbackResult = (*appCallback)(
- (AAudioStream *) this,
- getDataCallbackUserData(),
- mCallbackBuffer,
- mCallbackFrames);
+ callbackResult = maybeCallDataCallback(mCallbackBuffer, mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
ALOGD("callback returned AAUDIO_CALLBACK_RESULT_STOP");
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index b49e08c..5de6a11 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -323,18 +323,13 @@
void *AudioStreamInternalPlay::callbackLoop() {
aaudio_result_t result = AAUDIO_OK;
aaudio_data_callback_result_t callbackResult = AAUDIO_CALLBACK_RESULT_CONTINUE;
- AAudioStream_dataCallback appCallback = getDataCallbackProc();
- if (appCallback == nullptr) return NULL;
+ if (!isDataCallbackSet()) return NULL;
int64_t timeoutNanos = calculateReasonableTimeout(mCallbackFrames);
// result might be a frame count
while (mCallbackEnabled.load() && isActive() && (result >= 0)) {
// Call application using the AAudio callback interface.
- callbackResult = (*appCallback)(
- (AAudioStream *) this,
- getDataCallbackUserData(),
- mCallbackBuffer,
- mCallbackFrames);
+ callbackResult = maybeCallDataCallback(mCallbackBuffer, mCallbackFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
// Write audio data to stream. This is a BLOCKING WRITE!
@@ -345,13 +340,7 @@
// Only wrote some of the frames requested. Must have timed out.
result = AAUDIO_ERROR_TIMEOUT;
}
- AAudioStream_errorCallback errorCallback = getErrorCallbackProc();
- if (errorCallback != nullptr) {
- (*errorCallback)(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- result);
- }
+ maybeCallErrorCallback(result);
break;
}
} else if (callbackResult == AAUDIO_CALLBACK_RESULT_STOP) {
diff --git a/media/libaaudio/src/core/AAudioAudio.cpp b/media/libaaudio/src/core/AAudioAudio.cpp
index 1eaee81..b5634b8 100644
--- a/media/libaaudio/src/core/AAudioAudio.cpp
+++ b/media/libaaudio/src/core/AAudioAudio.cpp
@@ -18,6 +18,7 @@
//#define LOG_NDEBUG 0
#include <utils/Log.h>
+#include <mutex>
#include <time.h>
#include <pthread.h>
@@ -238,15 +239,22 @@
AAUDIO_API aaudio_result_t AAudioStream_close(AAudioStream* stream)
{
+ aaudio_result_t result = AAUDIO_ERROR_NULL;
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
- ALOGD("AAudioStream_close(%p)", stream);
+ ALOGD("AAudioStream_close(%p) called ---------------", stream);
if (audioStream != nullptr) {
- audioStream->close();
- audioStream->unregisterPlayerBase();
- delete audioStream;
- return AAUDIO_OK;
+ result = audioStream->safeClose();
+ // Close will only fail if called illegally, for example, from a callback.
+ // That would result in deleting an active stream, which would cause a crash.
+ if (result == AAUDIO_OK) {
+ audioStream->unregisterPlayerBase();
+ delete audioStream;
+ } else {
+ ALOGW("%s attempt to close failed. Close from another thread.", __func__);
+ }
}
- return AAUDIO_ERROR_NULL;
+ ALOGD("AAudioStream_close(%p) returned %d ---------", stream, result);
+ return result;
}
AAUDIO_API aaudio_result_t AAudioStream_requestStart(AAudioStream* stream)
@@ -269,7 +277,7 @@
{
AudioStream *audioStream = convertAAudioStreamToAudioStream(stream);
ALOGD("AAudioStream_requestFlush(%p)", stream);
- return audioStream->requestFlush();
+ return audioStream->safeFlush();
}
AAUDIO_API aaudio_result_t AAudioStream_requestStop(AAudioStream* stream)
@@ -324,7 +332,7 @@
}
// Don't allow writes when playing with a callback.
- if (audioStream->getDataCallbackProc() != nullptr && audioStream->isActive()) {
+ if (audioStream->isDataCallbackActive()) {
ALOGE("Cannot write to a callback stream when running.");
return AAUDIO_ERROR_INVALID_STATE;
}
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 27c36e1..8f5f5d3 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -43,7 +43,7 @@
LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED
|| getState() == AAUDIO_STREAM_STATE_UNINITIALIZED
|| getState() == AAUDIO_STREAM_STATE_DISCONNECTED),
- "aaudio stream still in use, state = %s",
+ "~AudioStream() - still in use, state = %s",
AAudio_convertStreamStateToText(getState()));
mPlayerBase->clearParentReference(); // remove reference to this AudioStream
@@ -97,12 +97,56 @@
(getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "OUTPUT" : "INPUT");
ALOGI("open() device = %d, perfMode = %d, callback: %s with frames = %d",
mDeviceId, mPerformanceMode,
- (mDataCallbackProc == nullptr ? "OFF" : "ON"),
+ (isDataCallbackSet() ? "ON" : "OFF"),
mFramesPerDataCallback);
return AAUDIO_OK;
}
+aaudio_result_t AudioStream::safeStart() {
+ std::lock_guard<std::mutex> lock(mStreamLock);
+ if (collidesWithCallback()) {
+ ALOGE("%s cannot be called from a callback!", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return requestStart();
+}
+
+aaudio_result_t AudioStream::safePause() {
+ std::lock_guard<std::mutex> lock(mStreamLock);
+ if (collidesWithCallback()) {
+ ALOGE("%s cannot be called from a callback!", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return requestPause();
+}
+
+aaudio_result_t AudioStream::safeFlush() {
+ std::lock_guard<std::mutex> lock(mStreamLock);
+ if (collidesWithCallback()) {
+ ALOGE("%s cannot be called from a callback!", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ 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__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return requestStop();
+}
+
+aaudio_result_t AudioStream::safeClose() {
+ std::lock_guard<std::mutex> lock(mStreamLock);
+ if (collidesWithCallback()) {
+ ALOGE("%s cannot be called from a callback!", __func__);
+ return AAUDIO_ERROR_INVALID_STATE;
+ }
+ return close();
+}
aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState,
aaudio_stream_state_t *nextState,
@@ -200,6 +244,58 @@
}
+aaudio_data_callback_result_t AudioStream::maybeCallDataCallback(void *audioData,
+ int32_t numFrames) {
+ aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_STOP;
+ AAudioStream_dataCallback dataCallback = getDataCallbackProc();
+ if (dataCallback != nullptr) {
+ // Store thread ID of caller to detect stop() and close() calls from callback.
+ pid_t expected = CALLBACK_THREAD_NONE;
+ if (mDataCallbackThread.compare_exchange_strong(expected, gettid())) {
+ result = (*dataCallback)(
+ (AAudioStream *) this,
+ getDataCallbackUserData(),
+ audioData,
+ numFrames);
+ mDataCallbackThread.store(CALLBACK_THREAD_NONE);
+ } else {
+ ALOGW("%s() data callback already running!", __func__);
+ }
+ }
+ return result;
+}
+
+void AudioStream::maybeCallErrorCallback(aaudio_result_t result) {
+ AAudioStream_errorCallback errorCallback = getErrorCallbackProc();
+ if (errorCallback != nullptr) {
+ // Store thread ID of caller to detect stop() and close() calls from callback.
+ pid_t expected = CALLBACK_THREAD_NONE;
+ if (mErrorCallbackThread.compare_exchange_strong(expected, gettid())) {
+ (*errorCallback)(
+ (AAudioStream *) this,
+ getErrorCallbackUserData(),
+ result);
+ mErrorCallbackThread.store(CALLBACK_THREAD_NONE);
+ } else {
+ ALOGW("%s() error callback already running!", __func__);
+ }
+ }
+}
+
+// Is this running on the same thread as a callback?
+// Note: This cannot be implemented using a thread_local because that would
+// require using a thread_local variable that is shared between streams.
+// So a thread_local variable would prevent stopping or closing stream A from
+// a callback on stream B, which is currently legal and not so terrible.
+bool AudioStream::collidesWithCallback() const {
+ pid_t thisThread = gettid();
+ // Compare the current thread ID with the thread ID of the callback
+ // threads to see it they match. If so then this code is being
+ // called from one of the stream callback functions.
+ return ((mErrorCallbackThread.load() == thisThread)
+ || (mDataCallbackThread.load() == thisThread));
+}
+
#if AAUDIO_USE_VOLUME_SHAPER
android::media::VolumeShaper::Status AudioStream::applyVolumeShaper(
const android::media::VolumeShaper::Configuration& configuration __unused,
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 34202d2..b5d7fd5 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -39,6 +39,8 @@
class AudioStreamBuilder;
+constexpr pid_t CALLBACK_THREAD_NONE = 0;
+
/**
* AAudio audio stream.
*/
@@ -49,8 +51,22 @@
virtual ~AudioStream();
+ /**
+ * Lock a mutex and make sure we are not calling from a callback function.
+ * @return result of requestStart();
+ */
+ aaudio_result_t safeStart();
+
+ aaudio_result_t safePause();
+
+ aaudio_result_t safeFlush();
+
+ aaudio_result_t safeStop();
+
+ aaudio_result_t safeClose();
// =========== Begin ABSTRACT methods ===========================
+protected:
/* Asynchronous requests.
* Use waitForStateChange() to wait for completion.
@@ -70,6 +86,7 @@
virtual aaudio_result_t requestStop() = 0;
+public:
virtual aaudio_result_t getTimestamp(clockid_t clockId,
int64_t *framePosition,
int64_t *timeNanoseconds) = 0;
@@ -81,7 +98,6 @@
*/
virtual aaudio_result_t updateStateMachine() = 0;
-
// =========== End ABSTRACT methods ===========================
virtual aaudio_result_t waitForStateChange(aaudio_stream_state_t currentState,
@@ -209,13 +225,19 @@
AAudioStream_dataCallback getDataCallbackProc() const {
return mDataCallbackProc;
}
+
AAudioStream_errorCallback getErrorCallbackProc() const {
return mErrorCallbackProc;
}
+ aaudio_data_callback_result_t maybeCallDataCallback(void *audioData, int32_t numFrames);
+
+ void maybeCallErrorCallback(aaudio_result_t result);
+
void *getDataCallbackUserData() const {
return mDataCallbackUserData;
}
+
void *getErrorCallbackUserData() const {
return mErrorCallbackUserData;
}
@@ -224,10 +246,25 @@
return mFramesPerDataCallback;
}
- bool isDataCallbackActive() {
- return (mDataCallbackProc != nullptr) && isActive();
+ /**
+ * @return true if data callback has been specified
+ */
+ bool isDataCallbackSet() const {
+ return mDataCallbackProc != nullptr;
}
+ /**
+ * @return true if data callback has been specified and stream is running
+ */
+ bool isDataCallbackActive() const {
+ return isDataCallbackSet() && isActive();
+ }
+
+ /**
+ * @return true if called from the same thread as the callback
+ */
+ bool collidesWithCallback() const;
+
// ============== I/O ===========================
// A Stream will only implement read() or write() depending on its direction.
virtual aaudio_result_t write(const void *buffer __unused,
@@ -248,7 +285,7 @@
doSetVolume(); // apply this change
}
- float getDuckAndMuteVolume() {
+ float getDuckAndMuteVolume() const {
return mDuckAndMuteVolume;
}
@@ -331,17 +368,17 @@
android::status_t playerStart() override {
// mParent should NOT be null. So go ahead and crash if it is.
- mResult = mParent->requestStart();
+ mResult = mParent->safeStart();
return AAudioConvert_aaudioToAndroidStatus(mResult);
}
android::status_t playerPause() override {
- mResult = mParent->requestPause();
+ mResult = mParent->safePause();
return AAudioConvert_aaudioToAndroidStatus(mResult);
}
android::status_t playerStop() override {
- mResult = mParent->requestStop();
+ mResult = mParent->safeStop();
return AAudioConvert_aaudioToAndroidStatus(mResult);
}
@@ -405,8 +442,6 @@
mDeviceId = deviceId;
}
- std::mutex mStreamMutex;
-
std::atomic<bool> mCallbackEnabled{false};
float mDuckAndMuteVolume = 1.0f;
@@ -422,39 +457,42 @@
}
private:
+
+ std::mutex mStreamLock;
+
const android::sp<MyPlayerBase> mPlayerBase;
// These do not change after open().
- int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
- int32_t mSampleRate = AAUDIO_UNSPECIFIED;
- int32_t mDeviceId = AAUDIO_UNSPECIFIED;
- aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
- bool mSharingModeMatchRequired = false; // must match sharing mode requested
- aaudio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
- aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
-
- aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
+ int32_t mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+ int32_t mSampleRate = AAUDIO_UNSPECIFIED;
+ int32_t mDeviceId = AAUDIO_UNSPECIFIED;
+ aaudio_sharing_mode_t mSharingMode = AAUDIO_SHARING_MODE_SHARED;
+ bool mSharingModeMatchRequired = false; // must match sharing mode requested
+ aaudio_format_t mFormat = AAUDIO_FORMAT_UNSPECIFIED;
+ aaudio_stream_state_t mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
+ aaudio_performance_mode_t mPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
// callback ----------------------------------
AAudioStream_dataCallback mDataCallbackProc = nullptr; // external callback functions
void *mDataCallbackUserData = nullptr;
int32_t mFramesPerDataCallback = AAUDIO_UNSPECIFIED; // frames
+ std::atomic<pid_t> mDataCallbackThread{CALLBACK_THREAD_NONE};
AAudioStream_errorCallback mErrorCallbackProc = nullptr;
void *mErrorCallbackUserData = nullptr;
+ std::atomic<pid_t> mErrorCallbackThread{CALLBACK_THREAD_NONE};
// background thread ----------------------------------
- bool mHasThread = false;
- pthread_t mThread; // initialized in constructor
+ bool mHasThread = false;
+ pthread_t mThread; // initialized in constructor
// These are set by the application thread and then read by the audio pthread.
- std::atomic<int64_t> mPeriodNanoseconds; // for tuning SCHED_FIFO threads
+ std::atomic<int64_t> mPeriodNanoseconds; // for tuning SCHED_FIFO threads
// TODO make atomic?
- aaudio_audio_thread_proc_t mThreadProc = nullptr;
- void* mThreadArg = nullptr;
- aaudio_result_t mThreadRegistrationResult = AAUDIO_OK;
-
+ aaudio_audio_thread_proc_t mThreadProc = nullptr;
+ void *mThreadArg = nullptr;
+ aaudio_result_t mThreadRegistrationResult = AAUDIO_OK;
};
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index ee2504d..c5dfb7c 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -48,19 +48,14 @@
return AudioStreamLegacy_callback;
}
-int32_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) {
+aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) {
if (getDirection() == AAUDIO_DIRECTION_INPUT) {
// Increment before because we already got the data from the device.
incrementFramesRead(numFrames);
}
// Call using the AAudio callback interface.
- AAudioStream_dataCallback appCallback = getDataCallbackProc();
- aaudio_data_callback_result_t callbackResult = (*appCallback)(
- (AAudioStream *) this,
- getDataCallbackUserData(),
- buffer,
- numFrames);
+ aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(buffer, numFrames);
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
&& getDirection() == AAUDIO_DIRECTION_OUTPUT) {
@@ -73,22 +68,26 @@
// Implement FixedBlockProcessor
int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
int32_t numFrames = numBytes / getBytesPerFrame();
- return callDataCallbackFrames(buffer, numFrames);
+ return (int32_t) callDataCallbackFrames(buffer, numFrames);
}
void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
aaudio_data_callback_result_t callbackResult;
+ // This illegal size can be used to AudioFlinger to stop calling us.
+ // This takes advantage of AudioFlinger killing the stream.
+ // TODO need API change in AudioRecord and AudioTrack
+ const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
switch (opcode) {
case AAUDIO_CALLBACK_OPERATION_PROCESS_DATA: {
- checkForDisconnectRequest();
+ (void) checkForDisconnectRequest(true);
// 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 (getState() == AAUDIO_STREAM_STATE_DISCONNECTED || !mCallbackEnabled.load()) {
- audioBuffer->size = 0; // silence the buffer
+ audioBuffer->size = SIZE_STOP_CALLBACKS;
} else {
if (audioBuffer->frameCount == 0) {
return;
@@ -106,8 +105,11 @@
}
if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
- } else {
- audioBuffer->size = 0;
+ } else { // STOP or invalid result
+ ALOGW("%s() stop stream by faking an error", __func__);
+ audioBuffer->size = SIZE_STOP_CALLBACKS;
+ // Disable the callback just in case AudioFlinger keeps trying to call us.
+ mCallbackEnabled.store(false);
}
if (updateStateMachine() != AAUDIO_OK) {
@@ -130,26 +132,23 @@
}
}
-
-
-void AudioStreamLegacy::checkForDisconnectRequest() {
+aaudio_result_t AudioStreamLegacy::checkForDisconnectRequest(bool errorCallbackEnabled) {
if (mRequestDisconnect.isRequested()) {
ALOGD("checkForDisconnectRequest() mRequestDisconnect acknowledged");
- forceDisconnect();
+ forceDisconnect(errorCallbackEnabled);
mRequestDisconnect.acknowledge();
mCallbackEnabled.store(false);
+ return AAUDIO_ERROR_DISCONNECTED;
+ } else {
+ return AAUDIO_OK;
}
}
-void AudioStreamLegacy::forceDisconnect() {
+void AudioStreamLegacy::forceDisconnect(bool errorCallbackEnabled) {
if (getState() != AAUDIO_STREAM_STATE_DISCONNECTED) {
setState(AAUDIO_STREAM_STATE_DISCONNECTED);
- if (getErrorCallbackProc() != nullptr) {
- (*getErrorCallbackProc())(
- (AAudioStream *) this,
- getErrorCallbackUserData(),
- AAUDIO_ERROR_DISCONNECTED
- );
+ if (errorCallbackEnabled) {
+ maybeCallErrorCallback(AAUDIO_ERROR_DISCONNECTED);
}
}
}
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.h b/media/libaaudio/src/legacy/AudioStreamLegacy.h
index 7e28579..6a506b3 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.h
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.h
@@ -112,9 +112,14 @@
void onAudioDeviceUpdate(audio_port_handle_t deviceId);
- void checkForDisconnectRequest();
+ /*
+ * Check to see whether a callback thread has requested a disconnected.
+ * @param errorCallbackEnabled set true to call errorCallback on disconnect
+ * @return AAUDIO_OK or AAUDIO_ERROR_DISCONNECTED
+ */
+ aaudio_result_t checkForDisconnectRequest(bool errorCallbackEnabled);
- void forceDisconnect();
+ void forceDisconnect(bool errorCallbackEnabled = true);
void onStart() { mCallbackEnabled.store(true); }
void onStop() { mCallbackEnabled.store(false); }
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 6d98ed3..3bcb8e7 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -238,8 +238,9 @@
mAudioRecord->stop();
mFramesRead.reset32();
mTimestampPosition.reset32();
- checkForDisconnectRequest();
- return AAUDIO_OK;
+ // Pass false to prevent errorCallback from being called after disconnect
+ // when app has already requested a stop().
+ return checkForDisconnectRequest(false);
}
aaudio_result_t AudioStreamRecord::updateStateMachine()
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index c2ce9a2..9b691e0 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -224,8 +224,6 @@
}
aaudio_result_t AudioStreamTrack::requestStart() {
- std::lock_guard<std::mutex> lock(mStreamMutex);
-
if (mAudioTrack.get() == nullptr) {
ALOGE("requestStart() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
@@ -247,13 +245,12 @@
}
aaudio_result_t AudioStreamTrack::requestPause() {
- std::lock_guard<std::mutex> lock(mStreamMutex);
-
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;
@@ -261,17 +258,14 @@
onStop();
setState(AAUDIO_STREAM_STATE_PAUSING);
mAudioTrack->pause();
- checkForDisconnectRequest();
status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
if (err != OK) {
return AAudioConvert_androidToAAudioResult(err);
}
- return AAUDIO_OK;
+ return checkForDisconnectRequest(false);
}
aaudio_result_t AudioStreamTrack::requestFlush() {
- std::lock_guard<std::mutex> lock(mStreamMutex);
-
if (mAudioTrack.get() == nullptr) {
ALOGE("requestFlush() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
@@ -288,8 +282,6 @@
}
aaudio_result_t AudioStreamTrack::requestStop() {
- std::lock_guard<std::mutex> lock(mStreamMutex);
-
if (mAudioTrack.get() == nullptr) {
ALOGE("requestStop() no AudioTrack");
return AAUDIO_ERROR_INVALID_STATE;
@@ -301,8 +293,7 @@
mFramesWritten.reset32();
mTimestampPosition.reset32();
mAudioTrack->stop();
- checkForDisconnectRequest();
- return AAUDIO_OK;
+ return checkForDisconnectRequest(false);;
}
aaudio_result_t AudioStreamTrack::updateStateMachine()