Merge changes I7f8339d8,Idff0c394 into rvc-dev

* changes:
  aaudio: steal exclusive streams
  aaudio test: test stealing an exclusive stream
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 3db0099..79fa5ed 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -354,6 +354,12 @@
     drainTimestampsFromService();
 
     aaudio_result_t result = mServiceInterface.startStream(mServiceStreamHandle);
+    if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+        ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+        // Stealing was added in R. Coerce result to improve backward compatibility.
+        result = AAUDIO_ERROR_DISCONNECTED;
+        setState(AAUDIO_STREAM_STATE_DISCONNECTED);
+    }
 
     startTime = AudioClock::getNanoseconds();
     mClockModel.start(startTime);
@@ -397,7 +403,12 @@
     if (isDataCallbackSet()
             && (isActive() || getState() == AAUDIO_STREAM_STATE_DISCONNECTED)) {
         mCallbackEnabled.store(false);
-        return joinThread(NULL); // may temporarily unlock mStreamLock
+        aaudio_result_t result = joinThread(NULL); // may temporarily unlock mStreamLock
+        if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+            ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+            result = AAUDIO_OK;
+        }
+        return result;
     } else {
         return AAUDIO_OK;
     }
@@ -427,7 +438,12 @@
     setState(AAUDIO_STREAM_STATE_STOPPING);
     mAtomicInternalTimestamp.clear();
 
-    return mServiceInterface.stopStream(mServiceStreamHandle);
+    result = mServiceInterface.stopStream(mServiceStreamHandle);
+    if (result == AAUDIO_ERROR_INVALID_HANDLE) {
+        ALOGD("%s() INVALID_HANDLE, stream was probably stolen", __func__);
+        result = AAUDIO_OK;
+    }
+    return result;
 }
 
 aaudio_result_t AudioStreamInternal::registerThread() {
diff --git a/media/libaaudio/tests/Android.bp b/media/libaaudio/tests/Android.bp
index 73fd896..a6e5f70 100644
--- a/media/libaaudio/tests/Android.bp
+++ b/media/libaaudio/tests/Android.bp
@@ -226,3 +226,15 @@
         "libutils",
     ],
 }
+
+cc_test {
+    name: "test_steal_exclusive",
+    defaults: ["libaaudio_tests_defaults"],
+    srcs: ["test_steal_exclusive.cpp"],
+    shared_libs: [
+        "libaaudio",
+        "libbinder",
+        "libcutils",
+        "libutils",
+    ],
+}
diff --git a/media/libaaudio/tests/test_steal_exclusive.cpp b/media/libaaudio/tests/test_steal_exclusive.cpp
new file mode 100644
index 0000000..2a05910
--- /dev/null
+++ b/media/libaaudio/tests/test_steal_exclusive.cpp
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * This test starts an exclusive stream.
+ * Then a few seconds later it starts a second exclusive stream.
+ * The first stream should get stolen and they should both end up
+ * as SHARED streams.
+ * The test will print PASS or FAIL.
+ *
+ * If you plug in a headset during the test then you can get them to both
+ * open at almost the same time. This can result in a race condition.
+ * Both streams may try to automatically reopen their streams in EXCLUSIVE mode.
+ * The first stream will have its EXCLUSIVE stream stolen by the second stream.
+ * It will usually get disconnected between its Open and Start calls.
+ * This can also occur in normal use. But is unlikely because the window is very narrow.
+ * In this case, where two streams are responding to the same disconnect event,
+ * it will usually happen.
+ *
+ * Because the stream has not started, this condition will not trigger an onError callback.
+ * But the stream will get an error returned from AAudioStream_requestStart().
+ * The test uses this result to trigger a retry in the onError callback.
+ * That is the best practice for any app restarting a stream.
+ *
+ * You should see that both streams are advancing after the disconnect.
+ *
+ * The headset can connect using a 3.5 mm jack, or USB-C or Bluetooth.
+ *
+ * This test can be used with INPUT by using the -i command line option.
+ * Before running the test you will need to enter "adb root" so that
+ * you can have permission to record.
+ * Also the headset needs to have a microphone.
+ * Then the test should behave essentially the same.
+ */
+
+#include <atomic>
+#include <stdio.h>
+#include <thread>
+#include <unistd.h>
+
+#include <aaudio/AAudio.h>
+
+#define DEFAULT_TIMEOUT_NANOS  ((int64_t)1000000000)
+#define SOLO_DURATION_MSEC    2000
+#define DUET_DURATION_MSEC    8000
+#define SLEEP_DURATION_MSEC    500
+
+static const char * s_sharingModeToText(aaudio_sharing_mode_t mode) {
+    return (mode == AAUDIO_SHARING_MODE_EXCLUSIVE) ? "EXCLUSIVE"
+        : ((mode == AAUDIO_SHARING_MODE_SHARED)  ? "SHARED"
+            : AAudio_convertResultToText(mode));
+}
+
+static void s_myErrorCallbackProc(
+        AAudioStream *stream,
+        void *userData,
+        aaudio_result_t error);
+
+struct AudioEngine {
+    AAudioStream        *stream = nullptr;
+    std::thread         *thread = nullptr;
+    aaudio_direction_t   direction = AAUDIO_DIRECTION_OUTPUT;
+
+    // These counters are read and written by the callback and the main thread.
+    std::atomic<int32_t> framesRead{};
+    std::atomic<int32_t> framesCalled{};
+    std::atomic<int32_t> callbackCount{};
+
+    void reset() {
+        framesRead.store(0);
+        framesCalled.store(0);
+        callbackCount.store(0);
+    }
+};
+
+// Callback function that fills the audio output buffer.
+static aaudio_data_callback_result_t s_myDataCallbackProc(
+        AAudioStream *stream,
+        void *userData,
+        void *audioData,
+        int32_t numFrames
+) {
+    (void) audioData;
+    (void) numFrames;
+    AudioEngine *engine = (struct AudioEngine *)userData;
+    engine->callbackCount++;
+
+    engine->framesRead = (int32_t)AAudioStream_getFramesRead(stream);
+    engine->framesCalled += numFrames;
+    return AAUDIO_CALLBACK_RESULT_CONTINUE;
+}
+
+static aaudio_result_t s_OpenAudioStream(struct AudioEngine *engine,
+                                         aaudio_direction_t direction) {
+    AAudioStreamBuilder *builder = nullptr;
+    engine->direction = direction;
+
+    // Use an AAudioStreamBuilder to contain requested parameters.
+    aaudio_result_t result = AAudio_createStreamBuilder(&builder);
+    if (result != AAUDIO_OK) {
+        printf("AAudio_createStreamBuilder returned %s",
+               AAudio_convertResultToText(result));
+        return result;
+    }
+
+    // Request stream properties.
+    AAudioStreamBuilder_setFormat(builder, AAUDIO_FORMAT_PCM_FLOAT);
+    AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+    AAudioStreamBuilder_setSharingMode(builder, AAUDIO_SHARING_MODE_EXCLUSIVE);
+    AAudioStreamBuilder_setDirection(builder, direction);
+    AAudioStreamBuilder_setDataCallback(builder, s_myDataCallbackProc, engine);
+    AAudioStreamBuilder_setErrorCallback(builder, s_myErrorCallbackProc, engine);
+
+    // Create an AAudioStream using the Builder.
+    result = AAudioStreamBuilder_openStream(builder, &engine->stream);
+    AAudioStreamBuilder_delete(builder);
+    builder = nullptr;
+    if (result != AAUDIO_OK) {
+        printf("AAudioStreamBuilder_openStream returned %s",
+               AAudio_convertResultToText(result));
+    }
+
+    // See see what kind of stream we actually opened.
+    int32_t deviceId = AAudioStream_getDeviceId(engine->stream);
+    aaudio_sharing_mode_t actualSharingMode = AAudioStream_getSharingMode(engine->stream);
+    printf("-------- opened: deviceId = %3d, actualSharingMode = %s\n",
+           deviceId,
+           s_sharingModeToText(actualSharingMode));
+
+    return result;
+}
+
+static aaudio_result_t s_CloseAudioStream(struct AudioEngine *engine) {
+    aaudio_result_t result = AAUDIO_OK;
+    if (engine->stream != nullptr) {
+        result = AAudioStream_close(engine->stream);
+        if (result != AAUDIO_OK) {
+            printf("AAudioStream_close returned %s\n",
+                   AAudio_convertResultToText(result));
+        }
+        engine->stream = nullptr;
+    }
+    return result;
+}
+
+static void s_myRestartStreamProc(void *userData) {
+    printf("%s() - restart in separate thread\n", __func__);
+    AudioEngine *engine = (AudioEngine *) userData;
+    int retriesLeft = 1;
+    aaudio_result_t result;
+    do {
+        s_CloseAudioStream(engine);
+        s_OpenAudioStream(engine, engine->direction);
+        // It is possible for the stream to be disconnected, or stolen between the time
+        // it is opened and when it is started. If that happens then try again.
+        // If it was stolen then it should succeed the second time because there will already be
+        // a SHARED stream, which will not get stolen.
+        result = AAudioStream_requestStart(engine->stream);
+        printf("%s() - AAudioStream_requestStart() returns %s\n", __func__,
+                AAudio_convertResultToText(result));
+    } while (retriesLeft-- > 0 && result != AAUDIO_OK);
+}
+
+static void s_myErrorCallbackProc(
+        AAudioStream * /* stream */,
+        void *userData,
+        aaudio_result_t error) {
+    printf("%s() - error = %s\n", __func__, AAudio_convertResultToText(error));
+    // Handle error on a separate thread.
+    std::thread t(s_myRestartStreamProc, userData);
+    t.detach();
+}
+
+static void s_usage() {
+    printf("test_steal_exclusive [-i]\n");
+    printf("     -i direction INPUT, otherwise OUTPUT\n");
+}
+
+/**
+ * @return 0 is OK, -1 for error
+ */
+static int s_checkEnginePositions(AudioEngine *engine) {
+    if (engine->stream == nullptr) return 0; // race condition with onError procs!
+
+    const int64_t framesRead = AAudioStream_getFramesRead(engine->stream);
+    const int64_t framesWritten = AAudioStream_getFramesWritten(engine->stream);
+    const int32_t delta = (int32_t)(framesWritten - framesRead);
+    printf("playing framesRead = %7d, framesWritten = %7d"
+           ", delta = %4d, framesCalled = %6d, callbackCount = %4d\n",
+           (int32_t) framesRead,
+           (int32_t) framesWritten,
+           delta,
+           engine->framesCalled.load(),
+           engine->callbackCount.load()
+    );
+    if (delta > AAudioStream_getBufferCapacityInFrames(engine->stream)) {
+        printf("ERROR - delta > capacity\n");
+        return -1;
+    }
+    return 0;
+}
+
+int main(int argc, char **argv) {
+    (void) argc;
+    (void) argv;
+    struct AudioEngine victim;
+    struct AudioEngine thief;
+    aaudio_direction_t direction = AAUDIO_DIRECTION_OUTPUT;
+    aaudio_result_t result = AAUDIO_OK;
+    int errorCount = 0;
+
+    // Make printf print immediately so that debug info is not stuck
+    // in a buffer if we hang or crash.
+    setvbuf(stdout, nullptr, _IONBF, (size_t) 0);
+
+    printf("Test Stealing an EXCLUSIVE stream V1.0\n");
+    printf("\n");
+
+    for (int i = 1; i < argc; i++) {
+        const char *arg = argv[i];
+        if (arg[0] == '-') {
+            char option = arg[1];
+            switch (option) {
+                case 'i':
+                    direction = AAUDIO_DIRECTION_INPUT;
+                    break;
+                default:
+                    s_usage();
+                    exit(EXIT_FAILURE);
+                    break;
+            }
+        } else {
+            s_usage();
+            exit(EXIT_FAILURE);
+            break;
+        }
+    }
+
+    result = s_OpenAudioStream(&victim, direction);
+    if (result != AAUDIO_OK) {
+        printf("s_OpenAudioStream victim returned %s\n",
+               AAudio_convertResultToText(result));
+        errorCount++;
+    }
+    victim.reset();
+
+    // Start stream.
+    result = AAudioStream_requestStart(victim.stream);
+    printf("AAudioStream_requestStart(VICTIM) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+    if (result != AAUDIO_OK) {
+        errorCount++;
+    }
+
+    if (result == AAUDIO_OK) {
+        const int watchLoops = SOLO_DURATION_MSEC / SLEEP_DURATION_MSEC;
+        for (int i = watchLoops; i > 0; i--) {
+            errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+            usleep(SLEEP_DURATION_MSEC * 1000);
+        }
+    }
+
+    printf("Try to start the THIEF stream that may steal the VICTIM MMAP resource -----\n");
+    result = s_OpenAudioStream(&thief, direction);
+    if (result != AAUDIO_OK) {
+        printf("s_OpenAudioStream victim returned %s\n",
+               AAudio_convertResultToText(result));
+        errorCount++;
+    }
+    thief.reset();
+
+    // Start stream.
+    result = AAudioStream_requestStart(thief.stream);
+    printf("AAudioStream_requestStart(THIEF) returned %d >>>>>>>>>>>>>>>>>>>>>>\n", result);
+    if (result != AAUDIO_OK) {
+        errorCount++;
+    }
+    printf("You might enjoy plugging in a headset now to see what happens...\n");
+
+    if (result == AAUDIO_OK) {
+        const int watchLoops = DUET_DURATION_MSEC / SLEEP_DURATION_MSEC;
+        for (int i = watchLoops; i > 0; i--) {
+            printf("victim: ");
+            errorCount += s_checkEnginePositions(&victim) ? 1 : 0;
+            printf(" thief: ");
+            errorCount += s_checkEnginePositions(&thief) ? 1 : 0;
+            usleep(SLEEP_DURATION_MSEC * 1000);
+        }
+    }
+
+    // Check for PASS/FAIL
+    aaudio_sharing_mode_t victimSharingMode = AAudioStream_getSharingMode(victim.stream);
+    aaudio_sharing_mode_t thiefSharingMode = AAudioStream_getSharingMode(thief.stream);
+    printf("victimSharingMode = %s, thiefSharingMode = %s, - ",
+           s_sharingModeToText(victimSharingMode),
+           s_sharingModeToText(thiefSharingMode));
+    if ((victimSharingMode == AAUDIO_SHARING_MODE_SHARED)
+            && (thiefSharingMode == AAUDIO_SHARING_MODE_SHARED)) {
+        printf("Both modes are SHARED => PASS\n");
+    } else {
+        errorCount++;
+        printf("Both modes should be SHARED => FAIL!!\n");
+    }
+
+    const int64_t victimFramesRead = AAudioStream_getFramesRead(victim.stream);
+    const int64_t thiefFramesRead = AAudioStream_getFramesRead(thief.stream);
+    printf("victimFramesRead = %d, thiefFramesRead = %d, - ",
+           (int)victimFramesRead, (int)thiefFramesRead);
+    if (victimFramesRead > 0 && thiefFramesRead > 0) {
+        printf("Both streams are running => PASS\n");
+    } else {
+        errorCount++;
+        printf("Both streams should be running => FAIL!!\n");
+    }
+
+    result = AAudioStream_requestStop(victim.stream);
+    printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
+    if (result != AAUDIO_OK) {
+        errorCount++;
+    }
+    result = AAudioStream_requestStop(thief.stream);
+    printf("AAudioStream_requestStop() returned %d <<<<<<<<<<<<<<<<<<<<<\n", result);
+    if (result != AAUDIO_OK) {
+        errorCount++;
+    }
+
+    s_CloseAudioStream(&victim);
+    s_CloseAudioStream(&thief);
+
+    printf("aaudio result = %d = %s\n", result, AAudio_convertResultToText(result));
+    printf("test %s\n", errorCount ? "FAILED" : "PASSED");
+
+    return errorCount ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index 82cc90e..c9bf72f 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -76,6 +76,7 @@
         result << "  ExclusiveFoundCount:   " << mExclusiveFoundCount << "\n";
         result << "  ExclusiveOpenCount:    " << mExclusiveOpenCount << "\n";
         result << "  ExclusiveCloseCount:   " << mExclusiveCloseCount << "\n";
+        result << "  ExclusiveStolenCount:  " << mExclusiveStolenCount << "\n";
         result << "\n";
 
         if (isExclusiveLocked) {
@@ -142,7 +143,13 @@
 sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
                                         const aaudio::AAudioStreamRequest &request) {
     if (request.getConstantConfiguration().getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
-        return openExclusiveEndpoint(audioService, request);
+        sp<AAudioServiceEndpoint> endpointToSteal;
+        sp<AAudioServiceEndpoint> foundEndpoint =
+                openExclusiveEndpoint(audioService, request, endpointToSteal);
+        if (endpointToSteal.get()) {
+            endpointToSteal->releaseRegisteredStreams(); // free the MMAP resource
+        }
+        return foundEndpoint;
     } else {
         return openSharedEndpoint(audioService, request);
     }
@@ -150,7 +157,8 @@
 
 sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
         AAudioService &aaudioService,
-        const aaudio::AAudioStreamRequest &request) {
+        const aaudio::AAudioStreamRequest &request,
+        sp<AAudioServiceEndpoint> &endpointToSteal) {
 
     std::lock_guard<std::mutex> lock(mExclusiveLock);
 
@@ -161,18 +169,22 @@
 
     // If we find an existing one then this one cannot be exclusive.
     if (endpoint.get() != nullptr) {
-        ALOGW("openExclusiveEndpoint() already in use");
-        // Already open so do not allow a second stream.
+        if (kStealingEnabled
+                && !endpoint->isForSharing() // not currently SHARED
+                && !request.isSharingModeMatchRequired()) { // app did not request a shared stream
+            ALOGD("%s() endpoint in EXCLUSIVE use. Steal it!", __func__);
+            mExclusiveStolenCount++;
+            endpointToSteal = endpoint;
+        }
         return nullptr;
     } else {
         sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP(aaudioService);
-        ALOGV("openExclusiveEndpoint(), no match so try to open MMAP %p for dev %d",
-              endpointMMap.get(), configuration.getDeviceId());
+        ALOGV("%s(), no match so try to open MMAP %p for dev %d",
+              __func__, endpointMMap.get(), configuration.getDeviceId());
         endpoint = endpointMMap;
 
         aaudio_result_t result = endpoint->open(request);
         if (result != AAUDIO_OK) {
-            ALOGV("openExclusiveEndpoint(), open failed");
             endpoint.clear();
         } else {
             mExclusiveStreams.push_back(endpointMMap);
@@ -183,7 +195,9 @@
     if (endpoint.get() != nullptr) {
         // Increment the reference count under this lock.
         endpoint->setOpenCount(endpoint->getOpenCount() + 1);
+        endpoint->setForSharing(request.isSharingModeMatchRequired());
     }
+
     return endpoint;
 }
 
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index ba17853..ae776b1 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -19,6 +19,7 @@
 
 #include <map>
 #include <mutex>
+#include <sys/types.h>
 #include <utils/Singleton.h>
 
 #include "binding/AAudioServiceMessage.h"
@@ -62,7 +63,8 @@
 
 private:
     android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
-                                                 const aaudio::AAudioStreamRequest &request);
+                                                 const aaudio::AAudioStreamRequest &request,
+                                                 sp<AAudioServiceEndpoint> &endpointToSteal);
 
     android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
                                               const aaudio::AAudioStreamRequest &request);
@@ -91,11 +93,16 @@
     int32_t mExclusiveFoundCount  = 0; // number of times we FOUND an exclusive endpoint
     int32_t mExclusiveOpenCount   = 0; // number of times we OPENED an exclusive endpoint
     int32_t mExclusiveCloseCount  = 0; // number of times we CLOSED an exclusive endpoint
+    int32_t mExclusiveStolenCount = 0; // number of times we STOLE an exclusive endpoint
+
     // Same as above but for SHARED endpoints.
     int32_t mSharedSearchCount    = 0;
     int32_t mSharedFoundCount     = 0;
     int32_t mSharedOpenCount      = 0;
     int32_t mSharedCloseCount     = 0;
+
+    // For easily disabling the stealing of exclusive streams.
+    static constexpr bool kStealingEnabled = true;
 };
 } /* namespace aaudio */
 
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index cac7453..ecbcb7e 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -85,6 +85,17 @@
 
 aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
                                           aaudio::AAudioStreamConfiguration &configurationOutput) {
+    // A lock in is used to order the opening of endpoints when an
+    // EXCLUSIVE endpoint is stolen. We want the order to be:
+    // 1) Thread A opens exclusive MMAP endpoint
+    // 2) Thread B wants to open an exclusive MMAP endpoint so it steals the one from A
+    //    under this lock.
+    // 3) Thread B opens a shared MMAP endpoint.
+    // 4) Thread A can then get the lock and also open a shared stream.
+    // Without the lock. Thread A might sneak in and reallocate an exclusive stream
+    // before B can open the shared stream.
+    std::unique_lock<std::recursive_mutex> lock(mOpenLock);
+
     aaudio_result_t result = AAUDIO_OK;
     sp<AAudioServiceStreamBase> serviceStream;
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
@@ -139,7 +150,6 @@
         return result;
     } else {
         aaudio_handle_t handle = mStreamTracker.addStreamForHandle(serviceStream.get());
-        ALOGV("openStream(): handle = 0x%08X", handle);
         serviceStream->setHandle(handle);
         pid_t pid = request.getProcessId();
         AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
@@ -147,6 +157,7 @@
         // Log open in MediaMetrics after we have the handle because we need the handle to
         // create the metrics ID.
         serviceStream->logOpen(handle);
+        ALOGV("%s(): return handle = 0x%08X", __func__, handle);
         return handle;
     }
 }
@@ -180,7 +191,10 @@
         ALOGE("closeStream(0x%0x), illegal stream handle", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
+    return closeStream(serviceStream);
+}
 
+aaudio_result_t AAudioService::closeStream(sp<AAudioServiceStreamBase> serviceStream) {
     pid_t pid = serviceStream->getOwnerProcessId();
     AAudioClientTracker::getInstance().unregisterClientStream(pid, serviceStream);
 
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 7167868..6a2ac1f 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -55,6 +55,10 @@
                                        aaudio::AAudioStreamConfiguration &configurationOutput)
                                        override;
 
+    /*
+     * This is called from Binder. It checks for permissions
+     * and converts the handle passed through Binder to a stream pointer.
+     */
     aaudio_result_t closeStream(aaudio::aaudio_handle_t streamHandle) override;
 
     aaudio_result_t getStreamDescription(
@@ -84,8 +88,18 @@
     aaudio_result_t stopClient(aaudio::aaudio_handle_t streamHandle,
                                        audio_port_handle_t clientHandle) override;
 
+ // ===============================================================================
+ // The following public methods are only called from the service and NOT by Binder.
+ // ===============================================================================
+
     aaudio_result_t disconnectStreamByPortHandle(audio_port_handle_t portHandle);
 
+    /*
+     * This is only called from within the Service.
+     * It bypasses the permission checks in closeStream(handle).
+     */
+    aaudio_result_t closeStream(sp<aaudio::AAudioServiceStreamBase> serviceStream);
+
 private:
 
     /** @return true if the client is the audioserver
@@ -100,8 +114,6 @@
     sp<aaudio::AAudioServiceStreamBase> convertHandleToServiceStream(
             aaudio::aaudio_handle_t streamHandle);
 
-
-
     bool releaseStream(const sp<aaudio::AAudioServiceStreamBase> &serviceStream);
 
     aaudio_result_t checkForPendingClose(const sp<aaudio::AAudioServiceStreamBase> &serviceStream,
@@ -111,6 +123,10 @@
 
     aaudio::AAudioStreamTracker     mStreamTracker;
 
+    // We use a lock to prevent thread A from reopening an exclusive stream
+    // after thread B steals thread A's exclusive MMAP resource stream.
+    std::recursive_mutex            mOpenLock;
+
     // TODO  Extract the priority constants from services/audioflinger/Threads.cpp
     // and share them with this code. Look for "kPriorityFastMixer".
     static constexpr int32_t        kRealTimeAudioPriorityClient = 2;
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 647dcf7..b09cbf4 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -27,13 +27,13 @@
 
 #include <utils/Singleton.h>
 
-#include "AAudioEndpointManager.h"
-#include "AAudioServiceEndpoint.h"
 
 #include "core/AudioStreamBuilder.h"
+
+#include "AAudioEndpointManager.h"
+#include "AAudioClientTracker.h"
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamShared.h"
-#include "AAudioServiceEndpointShared.h"
 
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
@@ -87,16 +87,31 @@
     return false;
 }
 
-void AAudioServiceEndpoint::disconnectRegisteredStreams() {
+std::vector<android::sp<AAudioServiceStreamBase>>
+        AAudioServiceEndpoint::disconnectRegisteredStreams() {
+    std::vector<android::sp<AAudioServiceStreamBase>> streamsDisconnected;
     std::lock_guard<std::mutex> lock(mLockStreams);
     mConnected.store(false);
-    for (const auto& stream : mRegisteredStreams) {
-        ALOGD("disconnectRegisteredStreams() stop and disconnect port %d",
-              stream->getPortHandle());
+    for (const auto &stream : mRegisteredStreams) {
+        ALOGD("%s() - stop and disconnect port %d", __func__, stream->getPortHandle());
         stream->stop();
         stream->disconnect();
     }
-    mRegisteredStreams.clear();
+    mRegisteredStreams.swap(streamsDisconnected);
+    return streamsDisconnected;
+}
+
+void AAudioServiceEndpoint::releaseRegisteredStreams() {
+    // List of streams to be closed after we disconnect everything.
+    std::vector<android::sp<AAudioServiceStreamBase>> streamsToClose
+            = disconnectRegisteredStreams();
+
+    // Close outside the lock to avoid recursive locks.
+    AAudioService *aaudioService = AAudioClientTracker::getInstance().getAAudioService();
+    for (const auto& serviceStream : streamsToClose) {
+        ALOGD("%s() - close stream 0x%08X", __func__, serviceStream->getHandle());
+        aaudioService->closeStream(serviceStream);
+    }
 }
 
 aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamBase>stream) {
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index ad30087..a171cb0 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -111,6 +111,21 @@
 
     static audio_attributes_t getAudioAttributesFrom(const AAudioStreamParameters *params);
 
+    // Stop, disconnect and release any streams registered on this endpoint.
+    void releaseRegisteredStreams();
+
+    bool isForSharing() const {
+        return mForSharing;
+    }
+
+    /**
+     *
+     * @param flag true if this endpoint is to be shared between multiple streams
+     */
+    void setForSharing(bool flag) {
+        mForSharing = flag;
+    }
+
 protected:
 
     /**
@@ -119,7 +134,7 @@
      */
     bool                     isStreamRegistered(audio_port_handle_t portHandle);
 
-    void                     disconnectRegisteredStreams();
+    std::vector<android::sp<AAudioServiceStreamBase>> disconnectRegisteredStreams();
 
     mutable std::mutex       mLockStreams;
     std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
@@ -132,7 +147,11 @@
     int32_t                  mOpenCount = 0;
     int32_t                  mRequestedDeviceId = 0;
 
+    // True if this will be shared by one or more other streams.
+    bool                     mForSharing = false;
+
     std::atomic<bool>        mConnected{true};
+
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
index af2710d..9dab770 100644
--- a/services/oboeservice/AAudioServiceEndpointMMAP.cpp
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -229,7 +229,7 @@
 }
 
 aaudio_result_t AAudioServiceEndpointMMAP::close() {
-    if (mMmapStream != 0) {
+    if (mMmapStream != nullptr) {
         // Needs to be explicitly cleared or CTS will fail but it is not clear why.
         mMmapStream.clear();
         // Apparently the above close is asynchronous. An attempt to open a new device