Merge "aaudio test: test both input and output stream limits" into oc-dr1-dev
diff --git a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
index 892792d..f1593fe 100644
--- a/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
+++ b/media/libaaudio/examples/input_monitor/src/input_monitor.cpp
@@ -48,8 +48,8 @@
     //aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_NONE;
     const aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_LOW_LATENCY;
     //aaudio_performance_mode_t requestedPerformanceMode = AAUDIO_PERFORMANCE_MODE_POWER_SAVING;
-    const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
-    //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
+    //const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_SHARED;
+    const aaudio_sharing_mode_t requestedSharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE;
     aaudio_sharing_mode_t actualSharingMode;
 
     AAudioStream *aaudioStream = nullptr;
@@ -70,7 +70,7 @@
 
     printf("%s - Monitor input level using AAudio\n", argv[0]);
 
-//    AAudio_setMMapPolicy(AAUDIO_POLICY_ALWAYS);
+    AAudio_setMMapPolicy(AAUDIO_POLICY_ALWAYS);
 
     recorder.setPerformanceMode(requestedPerformanceMode);
     recorder.setSharingMode(requestedSharingMode);
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index d4cb407..b9269e6 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -27,15 +27,15 @@
 #define SAMPLE_RATE           48000
 #define NUM_SECONDS           4
 
-#define MMAP_POLICY              AAUDIO_UNSPECIFIED
+//#define MMAP_POLICY              AAUDIO_UNSPECIFIED
 //#define MMAP_POLICY              AAUDIO_POLICY_NEVER
 //#define MMAP_POLICY              AAUDIO_POLICY_AUTO
-//#define MMAP_POLICY              AAUDIO_POLICY_ALWAYS
+#define MMAP_POLICY              AAUDIO_POLICY_ALWAYS
 
 #define REQUESTED_FORMAT         AAUDIO_FORMAT_PCM_I16
 
-#define REQUESTED_SHARING_MODE   AAUDIO_SHARING_MODE_SHARED
-//#define REQUESTED_SHARING_MODE   AAUDIO_SHARING_MODE_EXCLUSIVE
+//#define REQUESTED_SHARING_MODE   AAUDIO_SHARING_MODE_SHARED
+#define REQUESTED_SHARING_MODE   AAUDIO_SHARING_MODE_EXCLUSIVE
 
 
 int main(int argc, char **argv)
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index e40f4f9..ff13fc2 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -115,6 +115,7 @@
         setSampleRate(configuration.getSampleRate());
         setSamplesPerFrame(configuration.getSamplesPerFrame());
         setDeviceId(configuration.getDeviceId());
+        setSharingMode(configuration.getSharingMode());
 
         // Save device format so we can do format conversion and volume scaling together.
         mDeviceFormat = configuration.getAudioFormat();
@@ -286,56 +287,6 @@
     }
 }
 
-aaudio_result_t AudioStreamInternal::requestPauseInternal()
-{
-    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
-              mServiceStreamHandle);
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-
-    mClockModel.stop(AudioClock::getNanoseconds());
-    setState(AAUDIO_STREAM_STATE_PAUSING);
-    return AAudioConvert_androidToAAudioResult(pauseWithStatus());
-}
-
-aaudio_result_t AudioStreamInternal::requestPause()
-{
-    aaudio_result_t result = stopCallback();
-    if (result != AAUDIO_OK) {
-        return result;
-    }
-    result = requestPauseInternal();
-    return result;
-}
-
-aaudio_result_t AudioStreamInternal::requestFlush() {
-    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
-        ALOGE("AudioStreamInternal::requestFlush() mServiceStreamHandle invalid = 0x%08X",
-              mServiceStreamHandle);
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-
-    setState(AAUDIO_STREAM_STATE_FLUSHING);
-    return mServiceInterface.flushStream(mServiceStreamHandle);
-}
-
-// TODO for Play only
-void AudioStreamInternal::onFlushFromServer() {
-    int64_t readCounter = mAudioEndpoint.getDataReadCounter();
-    int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
-
-    // Bump offset so caller does not see the retrograde motion in getFramesRead().
-    int64_t framesFlushed = writeCounter - readCounter;
-    mFramesOffsetFromService += framesFlushed;
-    ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
-          (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
-
-    // Flush written frames by forcing writeCounter to readCounter.
-    // This is because we cannot move the read counter in the hardware.
-    mAudioEndpoint.setDataWriteCounter(readCounter);
-}
-
 aaudio_result_t AudioStreamInternal::requestStopInternal()
 {
     if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index c2c6419..257a702 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -41,13 +41,8 @@
     AudioStreamInternal(AAudioServiceInterface  &serviceInterface, bool inService);
     virtual ~AudioStreamInternal();
 
-    // =========== Begin ABSTRACT methods ===========================
     aaudio_result_t requestStart() override;
 
-    aaudio_result_t requestPause() override;
-
-    aaudio_result_t requestFlush() override;
-
     aaudio_result_t requestStop() override;
 
     aaudio_result_t getTimestamp(clockid_t clockId,
@@ -55,7 +50,6 @@
                                        int64_t *timeNanoseconds) override;
 
     virtual aaudio_result_t updateStateWhileWaiting() override;
-    // =========== End ABSTRACT methods ===========================
 
     aaudio_result_t open(const AudioStreamBuilder &builder) override;
 
@@ -113,13 +107,12 @@
 
     aaudio_result_t processCommands();
 
-    aaudio_result_t requestPauseInternal();
     aaudio_result_t requestStopInternal();
 
     aaudio_result_t stopCallback();
 
 
-    void onFlushFromServer();
+    virtual void onFlushFromServer() {}
 
     aaudio_result_t onEventFromServer(AAudioServiceMessage *message);
 
@@ -160,6 +153,8 @@
     // The service uses this for SHARED mode.
     bool                     mInService = false;  // Is this running in the client or the service?
 
+    AAudioServiceInterface  &mServiceInterface;   // abstract interface to the service
+
 private:
     /*
      * Asynchronous write with data conversion.
@@ -175,7 +170,6 @@
 
     AudioEndpointParcelable  mEndPointParcelable; // description of the buffers filled by service
     EndpointDescriptor       mEndpointDescriptor; // buffer description with resolved addresses
-    AAudioServiceInterface  &mServiceInterface;   // abstract interface to the service
 
 };
 
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
index 76ecbf9..1b18577 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.cpp
@@ -34,6 +34,55 @@
 AudioStreamInternalPlay::~AudioStreamInternalPlay() {}
 
 
+aaudio_result_t AudioStreamInternalPlay::requestPauseInternal()
+{
+    if (mServiceStreamHandle == AAUDIO_HANDLE_INVALID) {
+        ALOGE("AudioStreamInternal::requestPauseInternal() mServiceStreamHandle invalid = 0x%08X",
+              mServiceStreamHandle);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    mClockModel.stop(AudioClock::getNanoseconds());
+    setState(AAUDIO_STREAM_STATE_PAUSING);
+    return AAudioConvert_androidToAAudioResult(pauseWithStatus());
+}
+
+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",
+              mServiceStreamHandle);
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
+    setState(AAUDIO_STREAM_STATE_FLUSHING);
+    return mServiceInterface.flushStream(mServiceStreamHandle);
+}
+
+void AudioStreamInternalPlay::onFlushFromServer() {
+    int64_t readCounter = mAudioEndpoint.getDataReadCounter();
+    int64_t writeCounter = mAudioEndpoint.getDataWriteCounter();
+
+    // Bump offset so caller does not see the retrograde motion in getFramesRead().
+    int64_t framesFlushed = writeCounter - readCounter;
+    mFramesOffsetFromService += framesFlushed;
+    ALOGD("AudioStreamInternal::onFlushFromServer() readN = %lld, writeN = %lld, offset = %lld",
+          (long long)readCounter, (long long)writeCounter, (long long)mFramesOffsetFromService);
+
+    // Flush written frames by forcing writeCounter to readCounter.
+    // This is because we cannot move the read counter in the hardware.
+    mAudioEndpoint.setDataWriteCounter(readCounter);
+}
+
 // Write the data, block if needed and timeoutMillis > 0
 aaudio_result_t AudioStreamInternalPlay::write(const void *buffer, int32_t numFrames,
                                            int64_t timeoutNanoseconds)
diff --git a/media/libaaudio/src/client/AudioStreamInternalPlay.h b/media/libaaudio/src/client/AudioStreamInternalPlay.h
index b043f67..e59d02c 100644
--- a/media/libaaudio/src/client/AudioStreamInternalPlay.h
+++ b/media/libaaudio/src/client/AudioStreamInternalPlay.h
@@ -33,6 +33,10 @@
     AudioStreamInternalPlay(AAudioServiceInterface  &serviceInterface, bool inService = false);
     virtual ~AudioStreamInternalPlay();
 
+    aaudio_result_t requestPause() override;
+
+    aaudio_result_t requestFlush() override;
+
     aaudio_result_t write(const void *buffer,
                           int32_t numFrames,
                           int64_t timeoutNanoseconds) override;
@@ -47,6 +51,11 @@
     }
 
 protected:
+
+    aaudio_result_t requestPauseInternal();
+
+    void onFlushFromServer() override;
+
 /**
  * Low level write that will not block. It will just write as much as it can.
  *
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 15c85f9..e5fdcc6 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -48,8 +48,18 @@
      * Use waitForStateChange() to wait for completion.
      */
     virtual aaudio_result_t requestStart() = 0;
-    virtual aaudio_result_t requestPause() = 0;
-    virtual aaudio_result_t requestFlush() = 0;
+
+    virtual aaudio_result_t requestPause()
+    {
+        // Only implement this for OUTPUT streams.
+        return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual aaudio_result_t requestFlush() {
+        // Only implement this for OUTPUT streams.
+        return AAUDIO_ERROR_UNIMPLEMENTED;
+    }
+
     virtual aaudio_result_t requestStop() = 0;
 
     virtual aaudio_result_t getTimestamp(clockid_t clockId,
diff --git a/media/libaaudio/src/core/AudioStreamBuilder.cpp b/media/libaaudio/src/core/AudioStreamBuilder.cpp
index 8a481c0..6c4aa59 100644
--- a/media/libaaudio/src/core/AudioStreamBuilder.cpp
+++ b/media/libaaudio/src/core/AudioStreamBuilder.cpp
@@ -138,6 +138,12 @@
     bool allowMMap = mmapPolicy != AAUDIO_POLICY_NEVER;
     bool allowLegacy = mmapPolicy != AAUDIO_POLICY_ALWAYS;
 
+    // TODO Support other performance settings in MMAP mode.
+    // Disable MMAP if low latency not requested.
+    if (getPerformanceMode() != AAUDIO_PERFORMANCE_MODE_LOW_LATENCY) {
+        allowMMap = false;
+    }
+
     result = builder_createStream(getDirection(), sharingMode, allowMMap, &audioStream);
     if (result == AAUDIO_OK) {
         // Open the stream using the parameters from the builder.
@@ -247,4 +253,4 @@
     }
 
     return AAUDIO_OK;
-}
\ No newline at end of file
+}
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 156e83d..8e8070c 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -55,7 +55,7 @@
 
     // Try to create an AudioRecord
 
-    // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+    // TODO Support UNSPECIFIED in AudioRecord. For now, use stereo if unspecified.
     int32_t samplesPerFrame = (getSamplesPerFrame() == AAUDIO_UNSPECIFIED)
                               ? 2 : getSamplesPerFrame();
     audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame);
@@ -130,8 +130,8 @@
         return AAudioConvert_androidToAAudioResult(status);
     }
 
-    // Get the actual rate.
-    setSampleRate(mAudioRecord->getSampleRate());
+    // Get the actual values from the AudioRecord.
+    setSamplesPerFrame(mAudioRecord->channelCount());
     setFormat(AAudioConvert_androidToAAudioDataFormat(mAudioRecord->format()));
 
     int32_t actualSampleRate = mAudioRecord->getSampleRate();
@@ -223,18 +223,6 @@
     return AAUDIO_OK;
 }
 
-aaudio_result_t AudioStreamRecord::requestPause()
-{
-    // This does not make sense for an input stream.
-    // There is no real difference between pause() and stop().
-    return AAUDIO_ERROR_UNIMPLEMENTED;
-}
-
-aaudio_result_t AudioStreamRecord::requestFlush() {
-    // This does not make sense for an input stream.
-    return AAUDIO_ERROR_UNIMPLEMENTED;
-}
-
 aaudio_result_t AudioStreamRecord::requestStop() {
     if (mAudioRecord.get() == nullptr) {
         return AAUDIO_ERROR_INVALID_STATE;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index 90000fc..2c6a7eb 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -41,8 +41,6 @@
     aaudio_result_t close() override;
 
     aaudio_result_t requestStart() override;
-    aaudio_result_t requestPause() override;
-    aaudio_result_t requestFlush() override;
     aaudio_result_t requestStop() override;
 
     virtual aaudio_result_t getTimestamp(clockid_t clockId,
diff --git a/media/libaaudio/src/utility/HandleTracker.cpp b/media/libaaudio/src/utility/HandleTracker.cpp
index d4202db..35ce95a 100644
--- a/media/libaaudio/src/utility/HandleTracker.cpp
+++ b/media/libaaudio/src/utility/HandleTracker.cpp
@@ -113,7 +113,8 @@
         result << "HandleTracker may be deadlocked\n";
     }
 
-    result << "Handles:\n";
+    result << "HandleTracker:\n";
+    result << "  HandleHeaders:\n";
     // atLineStart() can be changed to support an arbitrary line breaking algorithm;
     // it should return true when a new line starts.
     // For simplicity, we will use a constant 16 items per line.
@@ -125,7 +126,7 @@
 
     for (int i = 0; i < mMaxHandleCount; ++i) {
         if (atLineStart(i)) {
-            result << "  ";
+            result << "    ";
         }
         result << std::hex << std::setw(4) << std::setfill('0') << mHandleHeaders[i]
                << (atLineEnd(i) ? "\n" : " ");
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index cfee943..d808e5b 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -44,6 +44,9 @@
 const int32_t kDefaultVideoEncoderDataSpace = HAL_DATASPACE_V0_BT709;
 
 const int kStopTimeoutUs = 300000; // allow 1 sec for shutting down encoder
+// allow maximum 1 sec for stop time offset. This limits the the delay in the
+// input source.
+const int kMaxStopTimeOffsetUs = 1000000;
 
 struct MediaCodecSource::Puller : public AHandler {
     explicit Puller(const sp<MediaSource> &source);
@@ -1017,7 +1020,15 @@
             if (mEncoder->getInputFormat(&inputFormat) == OK &&
                     inputFormat->findInt64("android._stop-time-offset-us", &stopTimeOffsetUs) &&
                     stopTimeOffsetUs > 0) {
+                if (stopTimeOffsetUs > kMaxStopTimeOffsetUs) {
+                    ALOGW("Source stopTimeOffsetUs %lld too large, limit at %lld us",
+                        (long long)stopTimeOffsetUs, (long long)kMaxStopTimeOffsetUs);
+                    stopTimeOffsetUs = kMaxStopTimeOffsetUs;
+                }
                 timeoutUs += stopTimeOffsetUs;
+            } else {
+                // Use kMaxStopTimeOffsetUs if stop time offset is not provided by input source
+                timeoutUs = kMaxStopTimeOffsetUs;
             }
         } else {
             mPuller->stop();
diff --git a/media/libstagefright/omx/1.0/Omx.cpp b/media/libstagefright/omx/1.0/Omx.cpp
index 64b2c08..789379a 100644
--- a/media/libstagefright/omx/1.0/Omx.cpp
+++ b/media/libstagefright/omx/1.0/Omx.cpp
@@ -90,46 +90,49 @@
     using ::android::IOMXNode;
     using ::android::IOMXObserver;
 
-    Mutex::Autolock autoLock(mLock);
-    if (mLiveNodes.size() == kMaxNodeInstances) {
-        _hidl_cb(toStatus(NO_MEMORY), nullptr);
-        return Void();
-    }
-
-    sp<OMXNodeInstance> instance = new OMXNodeInstance(
-            this, new LWOmxObserver(observer), name.c_str());
-
-    OMX_COMPONENTTYPE *handle;
-    OMX_ERRORTYPE err = mMaster->makeComponentInstance(
-            name.c_str(), &OMXNodeInstance::kCallbacks,
-            instance.get(), &handle);
-
-    if (err != OMX_ErrorNone) {
-        LOG(ERROR) << "Failed to allocate omx component "
-                "'" << name.c_str() << "' "
-                " err=" << asString(err) <<
-                "(0x" << std::hex << unsigned(err) << ")";
-        _hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);
-        return Void();
-    }
-    instance->setHandle(handle);
-    std::vector<AString> quirkVector;
-    if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
-        uint32_t quirks = 0;
-        for (const AString quirk : quirkVector) {
-            if (quirk == "requires-allocate-on-input-ports") {
-                quirks |= kRequiresAllocateBufferOnInputPorts;
-            }
-            if (quirk == "requires-allocate-on-output-ports") {
-                quirks |= kRequiresAllocateBufferOnOutputPorts;
-            }
+    sp<OMXNodeInstance> instance;
+    {
+        Mutex::Autolock autoLock(mLock);
+        if (mLiveNodes.size() == kMaxNodeInstances) {
+            _hidl_cb(toStatus(NO_MEMORY), nullptr);
+            return Void();
         }
-        instance->setQuirks(quirks);
-    }
 
-    mLiveNodes.add(observer.get(), instance);
+        instance = new OMXNodeInstance(
+                this, new LWOmxObserver(observer), name.c_str());
+
+        OMX_COMPONENTTYPE *handle;
+        OMX_ERRORTYPE err = mMaster->makeComponentInstance(
+                name.c_str(), &OMXNodeInstance::kCallbacks,
+                instance.get(), &handle);
+
+        if (err != OMX_ErrorNone) {
+            LOG(ERROR) << "Failed to allocate omx component "
+                    "'" << name.c_str() << "' "
+                    " err=" << asString(err) <<
+                    "(0x" << std::hex << unsigned(err) << ")";
+            _hidl_cb(toStatus(StatusFromOMXError(err)), nullptr);
+            return Void();
+        }
+        instance->setHandle(handle);
+        std::vector<AString> quirkVector;
+        if (mParser.getQuirks(name.c_str(), &quirkVector) == OK) {
+            uint32_t quirks = 0;
+            for (const AString quirk : quirkVector) {
+                if (quirk == "requires-allocate-on-input-ports") {
+                    quirks |= kRequiresAllocateBufferOnInputPorts;
+                }
+                if (quirk == "requires-allocate-on-output-ports") {
+                    quirks |= kRequiresAllocateBufferOnOutputPorts;
+                }
+            }
+            instance->setQuirks(quirks);
+        }
+
+        mLiveNodes.add(observer.get(), instance);
+        mNode2Observer.add(instance.get(), observer.get());
+    }
     observer->linkToDeath(this, 0);
-    mNode2Observer.add(instance.get(), observer.get());
 
     _hidl_cb(toStatus(OK), new TWOmxNode(instance));
     return Void();
diff --git a/media/libstagefright/omx/GraphicBufferSource.cpp b/media/libstagefright/omx/GraphicBufferSource.cpp
index 2f6fec8..ef4d745 100644
--- a/media/libstagefright/omx/GraphicBufferSource.cpp
+++ b/media/libstagefright/omx/GraphicBufferSource.cpp
@@ -1229,7 +1229,8 @@
         ALOGW("Fail to return stopTimeOffsetUs as stop time is not set");
         return INVALID_OPERATION;
     }
-    *stopTimeOffsetUs = mStopTimeUs - mLastFrameTimestampUs;
+    *stopTimeOffsetUs =
+        mLastFrameTimestampUs == -1 ? 0 : mStopTimeUs - mLastFrameTimestampUs;
     return OK;
 }
 
diff --git a/services/oboeservice/AAudioClientTracker.cpp b/services/oboeservice/AAudioClientTracker.cpp
index c0dfc54..c0bea13 100644
--- a/services/oboeservice/AAudioClientTracker.cpp
+++ b/services/oboeservice/AAudioClientTracker.cpp
@@ -39,6 +39,28 @@
         : Singleton<AAudioClientTracker>() {
 }
 
+
+std::string AAudioClientTracker::dump() const {
+    std::stringstream result;
+    const bool isLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mLock.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isLocked) {
+        result << "AAudioClientTracker may be deadlocked\n";
+    }
+
+    result << "AAudioClientTracker:\n";
+    for (const auto&  it : mNotificationClients) {
+        result << it.second->dump();
+    }
+
+    if (isLocked) {
+        mLock.unlock();
+    }
+    return result.str();
+}
+
 // Create a tracker for the client.
 aaudio_result_t AAudioClientTracker::registerClient(pid_t pid,
                                          const sp<IAAudioClient>& client) {
@@ -81,12 +103,12 @@
 aaudio_result_t
 AAudioClientTracker::registerClientStream(pid_t pid, sp<AAudioServiceStreamBase> serviceStream) {
     aaudio_result_t result = AAUDIO_OK;
-    ALOGV("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
+    ALOGD("AAudioClientTracker::registerClientStream(%d, %p)\n", pid, serviceStream.get());
     std::lock_guard<std::mutex> lock(mLock);
     sp<NotificationClient> notificationClient = mNotificationClients[pid];
     if (notificationClient == 0) {
         // This will get called the first time the audio server registers an internal stream.
-        ALOGV("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
+        ALOGD("AAudioClientTracker::registerClientStream(%d,) unrecognized pid\n", pid);
         notificationClient = new NotificationClient(pid);
         mNotificationClients[pid] = notificationClient;
     }
@@ -98,11 +120,16 @@
 aaudio_result_t
 AAudioClientTracker::unregisterClientStream(pid_t pid,
                                             sp<AAudioServiceStreamBase> serviceStream) {
-    ALOGV("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
+    ALOGD("AAudioClientTracker::unregisterClientStream(%d, %p)\n", pid, serviceStream.get());
     std::lock_guard<std::mutex> lock(mLock);
     auto it = mNotificationClients.find(pid);
     if (it != mNotificationClients.end()) {
+        ALOGD("AAudioClientTracker::unregisterClientStream(%d, %p) found NotificationClient\n",
+              pid, serviceStream.get());
         it->second->unregisterClientStream(serviceStream);
+    } else {
+        ALOGE("AAudioClientTracker::unregisterClientStream(%d, %p) missing NotificationClient\n",
+              pid, serviceStream.get());
     }
     return AAUDIO_OK;
 }
@@ -131,7 +158,11 @@
 aaudio_result_t AAudioClientTracker::NotificationClient::unregisterClientStream(
         sp<AAudioServiceStreamBase> serviceStream) {
     std::lock_guard<std::mutex> lock(mLock);
+    ALOGD("AAudioClientTracker::NotificationClient() before erase() count = %d\n",
+          (int)mStreams.size());
     mStreams.erase(serviceStream);
+    ALOGD("AAudioClientTracker::NotificationClient() after erase() count = %d\n",
+          (int)mStreams.size());
     return AAUDIO_OK;
 }
 
@@ -141,7 +172,7 @@
     if (aaudioService != nullptr) {
         // Copy the current list of streams to another vector because closing them below
         // will cause unregisterClientStream() calls back to this object.
-        std::set<android::sp<AAudioServiceStreamBase>>  streamsToClose;
+        std::set<sp<AAudioServiceStreamBase>>  streamsToClose;
 
         {
             std::lock_guard<std::mutex> lock(mLock);
@@ -162,3 +193,25 @@
     sp<NotificationClient> keep(this);
     AAudioClientTracker::getInstance().unregisterClient(mProcessId);
 }
+
+
+std::string AAudioClientTracker::NotificationClient::dump() const {
+    std::stringstream result;
+    const bool isLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mLock.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isLocked) {
+        result << "AAudioClientTracker::NotificationClient may be deadlocked\n";
+    }
+
+    result << "  client: pid = " << mProcessId << " has " << mStreams.size() << " streams\n";
+    for (auto serviceStream : mStreams) {
+        result << "     stream: 0x" << std::hex << serviceStream->getHandle() << std::dec << "\n";
+    }
+
+    if (isLocked) {
+        mLock.unlock();
+    }
+    return result.str();
+}
diff --git a/services/oboeservice/AAudioClientTracker.h b/services/oboeservice/AAudioClientTracker.h
index e74c8bf..accf1a7 100644
--- a/services/oboeservice/AAudioClientTracker.h
+++ b/services/oboeservice/AAudioClientTracker.h
@@ -34,6 +34,18 @@
     AAudioClientTracker();
     ~AAudioClientTracker() = default;
 
+    /**
+     * Returns information about the state of the this class.
+     *
+     * Will attempt to get the object lock, but will proceed
+     * even if it cannot.
+     *
+     * Each line of information ends with a newline.
+     *
+     * @return a string with useful information
+     */
+    std::string dump() const;
+
     aaudio_result_t registerClient(pid_t pid, const android::sp<android::IAAudioClient>& client);
 
     void unregisterClient(pid_t pid);
@@ -66,6 +78,8 @@
 
         int32_t getStreamCount();
 
+        std::string dump() const;
+
         aaudio_result_t registerClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
 
         aaudio_result_t unregisterClientStream(android::sp<AAudioServiceStreamBase> serviceStream);
@@ -74,12 +88,12 @@
         virtual     void    binderDied(const android::wp<IBinder>& who);
 
     protected:
-        std::mutex                                      mLock;
+        mutable std::mutex                              mLock;
         const pid_t                                     mProcessId;
         std::set<android::sp<AAudioServiceStreamBase>>  mStreams;
     };
 
-    std::mutex                                       mLock;
+    mutable std::mutex                               mLock;
     std::map<pid_t, android::sp<NotificationClient>> mNotificationClients;
     android::AAudioService                          *mAAudioService = nullptr;
 };
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 36e678a..02d4a19 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -48,16 +48,17 @@
         result << "EndpointManager may be deadlocked\n";
     }
 
+    result << "AAudioEndpointManager:" << "\n";
     size_t inputs = mInputs.size();
-    result << "Inputs: " << inputs << "\n";
+    result << "Input Endpoints: " << inputs << "\n";
     for (const auto &input : mInputs) {
-        result << "  Input(" << input << ")\n";
+        result << "  Input: " << input->dump() << "\n";
     }
 
     size_t outputs = mOutputs.size();
-    result << "Outputs: " << outputs << "\n";
+    result << "Output Endpoints: " << outputs << "\n";
     for (const auto &output : mOutputs) {
-        result << "  Output(" << output << ")\n";
+        result << "  Output: " << output->dump() << "\n";
     }
 
     if (isLocked) {
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 6d3b52b..2511b2f 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -34,14 +34,14 @@
     ~AAudioEndpointManager() = default;
 
     /**
-     * Returns EndpointManager information.
+     * Returns information about the state of the this class.
      *
      * Will attempt to get the object lock, but will proceed
      * even if it cannot.
      *
      * Each line of information ends with a newline.
      *
-     * @return a string representing the EndpointManager info
+     * @return a string with useful information
      */
     std::string dump() const;
 
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 91f7885..4ccd2f6 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -69,7 +69,9 @@
         result = ss.str();
         ALOGW("%s", result.c_str());
     } else {
-        result = mHandleTracker.dump() + AAudioEndpointManager::getInstance().dump();
+        result = mHandleTracker.dump()
+                 + AAudioClientTracker::getInstance().dump()
+                 + AAudioEndpointManager::getInstance().dump();
     }
     (void)write(fd, result.c_str(), result.size());
     return NO_ERROR;
@@ -105,7 +107,7 @@
     }
 
     if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
-        serviceStream = new AAudioServiceStreamMMAP();
+        serviceStream = new AAudioServiceStreamMMAP(mCachedUserId);
         result = serviceStream->open(request, configurationOutput);
         if (result != AAUDIO_OK) {
             // fall back to using a shared stream
@@ -148,17 +150,18 @@
 }
 
 aaudio_result_t AAudioService::closeStream(aaudio_handle_t streamHandle) {
-    // Check permission first.
-    AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    // Check permission and ownership first.
+    sp<AAudioServiceStreamBase> serviceStream = convertHandleToServiceStream(streamHandle);
     if (serviceStream == nullptr) {
         ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
 
+    ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);
+    // Remove handle from tracker so that we cannot look up the raw address any more.
     serviceStream = (AAudioServiceStreamBase *)
             mHandleTracker.remove(AAUDIO_HANDLE_TYPE_STREAM,
                                   streamHandle);
-    ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);
     if (serviceStream != nullptr) {
         serviceStream->close();
         pid_t pid = serviceStream->getOwnerProcessId();
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 1df2365..b519829 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -18,16 +18,17 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
+#include <algorithm>
 #include <assert.h>
 #include <map>
 #include <mutex>
+#include <sstream>
+#include <vector>
+
 #include <utils/Singleton.h>
 
 #include "AAudioEndpointManager.h"
 #include "AAudioServiceEndpoint.h"
-#include <algorithm>
-#include <mutex>
-#include <vector>
 
 #include "core/AudioStreamBuilder.h"
 #include "AAudioServiceEndpoint.h"
@@ -44,6 +45,35 @@
 // This is the maximum size in frames. The effective size can be tuned smaller at runtime.
 #define DEFAULT_BUFFER_CAPACITY   (48 * 8)
 
+std::string AAudioServiceEndpoint::dump() const {
+    std::stringstream result;
+
+    const bool isLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mLockStreams.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isLocked) {
+        result << "EndpointManager may be deadlocked\n";
+    }
+
+    AudioStreamInternal     *stream = mStreamInternal;
+    if (stream == nullptr) {
+        result << "null stream!" << "\n";
+    } else {
+        result << "mmap stream: rate = " << stream->getSampleRate() << "\n";
+    }
+
+    result << "    Registered Streams:" << "\n";
+    for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
+        result << sharedStream->dump();
+    }
+
+    if (isLocked) {
+        mLockStreams.unlock();
+    }
+    return result.str();
+}
+
 // Set up an EXCLUSIVE MMAP stream that will be shared.
 aaudio_result_t AAudioServiceEndpoint::open(const AAudioStreamConfiguration& configuration) {
     mRequestedDeviceId = configuration.getDeviceId();
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 72015b1..a78d3fa 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -36,6 +36,8 @@
 public:
     virtual ~AAudioServiceEndpoint() = default;
 
+    std::string dump() const;
+
     virtual aaudio_result_t open(const AAudioStreamConfiguration& configuration);
 
     int32_t getSampleRate() const { return mStreamInternal->getSampleRate(); }
@@ -73,7 +75,7 @@
 
     std::atomic<bool>        mCallbackEnabled;
 
-    std::mutex               mLockStreams;
+    mutable std::mutex       mLockStreams;
 
     std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
 
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index c4591b0..2e20287 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -41,8 +41,25 @@
 }
 
 AAudioServiceStreamBase::~AAudioServiceStreamBase() {
-    close();
-    ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroyed %p", this);
+    ALOGD("AAudioServiceStreamBase::~AAudioServiceStreamBase() destroying %p", this);
+    // If the stream is deleted without closing then audio resources will leak.
+    // Not being closed here would indicate an internal error. So we want to find this ASAP.
+    LOG_ALWAYS_FATAL_IF(mState != AAUDIO_STREAM_STATE_CLOSED,
+                        "service stream not closed, state = %d", mState);
+}
+
+std::string AAudioServiceStreamBase::dump() const {
+    std::stringstream result;
+
+    result << "      -------- handle = 0x" << std::hex << mHandle << std::dec << "\n";
+    result << "      state          = " << AAudio_convertStreamStateToText(mState) << "\n";
+    result << "      format         = " << mAudioFormat << "\n";
+    result << "      framesPerBurst = " << mFramesPerBurst << "\n";
+    result << "      channelCount   = " << mSamplesPerFrame << "\n";
+    result << "      capacityFrames = " << mCapacityInFrames << "\n";
+    result << "      owner uid      = " << mOwnerUserId << "\n";
+
+    return result.str();
 }
 
 aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
@@ -57,11 +74,13 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::close() {
-    stop();
-    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
-    delete mUpMessageQueue;
-    mUpMessageQueue = nullptr;
-
+    if (mState != AAUDIO_STREAM_STATE_CLOSED) {
+        stopTimestampThread();
+        std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+        delete mUpMessageQueue;
+        mUpMessageQueue = nullptr;
+        mState = AAUDIO_STREAM_STATE_CLOSED;
+    }
     return AAUDIO_OK;
 }
 
@@ -92,9 +111,8 @@
     aaudio_result_t result = AAUDIO_OK;
     if (isRunning()) {
         // TODO wait for data to be played out
-        sendCurrentTimestamp();
-        mThreadEnabled.store(false);
-        result = mAAudioThread.stop();
+        sendCurrentTimestamp(); // warning - this calls a virtual function
+        result = stopTimestampThread();
         if (result != AAUDIO_OK) {
             disconnect();
             return result;
@@ -105,6 +123,15 @@
     return result;
 }
 
+aaudio_result_t AAudioServiceStreamBase::stopTimestampThread() {
+    aaudio_result_t result = AAUDIO_OK;
+    // clear flag that tells thread to loop
+    if (mThreadEnabled.exchange(false)) {
+        result = mAAudioThread.stop();
+    }
+    return result;
+}
+
 aaudio_result_t AAudioServiceStreamBase::flush() {
     sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
     mState = AAUDIO_STREAM_STATE_FLUSHED;
@@ -127,6 +154,7 @@
             nextTime = timestampScheduler.nextAbsoluteTime();
         } else  {
             // Sleep until it is time to send the next timestamp.
+            // TODO Wait for a signal with a timeout so that we can stop more quickly.
             AudioClock::sleepUntilNanoTime(nextTime);
         }
     }
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 653b456..eed1a03 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -52,6 +52,8 @@
         ILLEGAL_THREAD_ID = 0
     };
 
+    std::string dump() const;
+
     // -------------------------------------------------------------------
     /**
      * Open the device.
@@ -76,6 +78,8 @@
      */
     virtual aaudio_result_t stop();
 
+    aaudio_result_t stopTimestampThread();
+
     /**
      *  Discard any data held by the underlying HAL or Service.
      */
@@ -141,6 +145,7 @@
     }
 
 protected:
+
     aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
 
     aaudio_result_t sendCurrentTimestamp();
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 40093eb..da18f44 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -41,23 +41,26 @@
  * Service Stream that uses an MMAP buffer.
  */
 
-AAudioServiceStreamMMAP::AAudioServiceStreamMMAP()
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(uid_t serviceUid)
         : AAudioServiceStreamBase()
         , mMmapStreamCallback(new MyMmapStreamCallback(*this))
         , mPreviousFrameCounter(0)
-        , mMmapStream(nullptr) {
-}
-
-AAudioServiceStreamMMAP::~AAudioServiceStreamMMAP() {
-    close();
+        , mMmapStream(nullptr)
+        , mCachedUserId(serviceUid) {
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::close() {
-    mMmapStream.clear(); // TODO review. Is that all we have to do?
-    // Apparently the above close is asynchronous. An attempt to open a new device
-    // right after a close can fail. Also some callbacks may still be in flight!
-    // FIXME Make closing synchronous.
-    AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
+    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+        return AAUDIO_OK;
+    }
+
+    if (mMmapStream != 0) {
+        mMmapStream.clear(); // TODO review. Is that all we have to do?
+        // Apparently the above close is asynchronous. An attempt to open a new device
+        // right after a close can fail. Also some callbacks may still be in flight!
+        // FIXME Make closing synchronous.
+        AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
+    }
 
     if (mAudioDataFileDescriptor != -1) {
         ::close(mAudioDataFileDescriptor);
@@ -153,10 +156,29 @@
               status);
         return AAUDIO_ERROR_UNAVAILABLE;
     } else {
-        ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d",
+        ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d"
+                "Sharable FD: %s",
               status, mMmapBufferinfo.shared_memory_address,
-              mMmapBufferinfo.buffer_size_frames,
-              mMmapBufferinfo.burst_size_frames);
+              abs(mMmapBufferinfo.buffer_size_frames),
+              mMmapBufferinfo.burst_size_frames,
+              mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
+    }
+
+    mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
+    // FIXME: the audio HAL indicates if the shared memory fd can be shared outside of audioserver
+    // by returning a negative buffer size
+    if (mCapacityInFrames < 0) {
+        // Exclusive mode is possible from any client
+        mCapacityInFrames = -mCapacityInFrames;
+    } else {
+        // exclusive mode is only possible if the final fd destination is inside audioserver
+        if ((mMmapClient.clientUid != mCachedUserId) &&
+                configurationInput.getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+            // Fallback is handled by caller but indicate what is possible in case
+            // this is used in the future
+            configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+            return AAUDIO_ERROR_UNAVAILABLE;
+        }
     }
 
     // Get information about the stream and pass it back to the caller.
@@ -166,7 +188,6 @@
 
     mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
     mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
-    mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
     mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
     mSampleRate = config.sample_rate;
 
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index fe75a10..257bea9 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -43,9 +43,8 @@
     , public android::MmapStreamCallback {
 
 public:
-    AAudioServiceStreamMMAP();
-    virtual ~AAudioServiceStreamMMAP();
-
+    AAudioServiceStreamMMAP(uid_t serviceUid);
+    virtual ~AAudioServiceStreamMMAP() = default;
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
                                  aaudio::AAudioStreamConfiguration &configurationOutput) override;
@@ -134,6 +133,7 @@
     struct audio_mmap_buffer_info             mMmapBufferinfo;
     android::MmapStreamInterface::Client      mMmapClient;
     audio_port_handle_t                       mPortHandle = -1; // TODO review best default
+    uid_t                                     mCachedUserId = -1;
 };
 
 } // namespace aaudio
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 1978ddd..9cc5cbc 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -44,10 +44,6 @@
     {
 }
 
-AAudioServiceStreamShared::~AAudioServiceStreamShared() {
-    close();
-}
-
 int32_t AAudioServiceStreamShared::calculateBufferCapacity(int32_t requestedCapacityFrames,
                                                            int32_t framesPerBurst) {
 
@@ -260,21 +256,27 @@
 }
 
 aaudio_result_t AAudioServiceStreamShared::close()  {
-    pause();
-    // TODO wait for pause() to synchronize
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint != nullptr) {
-        sp<AAudioServiceStreamShared> keep(this);
-        endpoint->unregisterStream(keep);
-
-        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-        mEndpointManager.closeEndpoint(endpoint);
-        mServiceEndpoint = nullptr;
+    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+        return AAUDIO_OK;
     }
+
+    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
+    if (endpoint == nullptr) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    endpoint->stopStream(this);
+
+    endpoint->unregisterStream(this);
+
+    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+    mEndpointManager.closeEndpoint(endpoint);
+    mServiceEndpoint = nullptr;
+
     if (mAudioDataQueue != nullptr) {
         delete mAudioDataQueue;
         mAudioDataQueue = nullptr;
     }
+
     return AAudioServiceStreamBase::close();
 }
 
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 9de502b..742c5af 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -44,7 +44,7 @@
 
 public:
     AAudioServiceStreamShared(android::AAudioService &aAudioService);
-    virtual ~AAudioServiceStreamShared();
+    virtual ~AAudioServiceStreamShared() = default;
 
     aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
                          aaudio::AAudioStreamConfiguration &configurationOutput) override;