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