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/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()