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