aaudio: fix EXCLUSIVE mode interfering with SHARED

The MMAP endpoints were not tracked by the EndpointManager
so it could not broker EXCLUSIVE access. To fix this the MMAP stream
was refactored to use a per-client stream and a per-device endpoint.

Cleanup close() of MMAP stream.
Add AAudioServiceEndpointShared.cpp.
Extract AAudioServiceEndpointMMAP from AAudioServiceStreamMMAP.
Track MMAP endpoints so we can manage EXCLUSIVE and SHARED access.

Bug: 64494572
Bug: 64310586
Test: see bug, use write_sine to play a shared stream and a excl stream
Change-Id: I5053193abfd9b8a69a2f7e1110739d65e2af5d64
Merged-In: I5053193abfd9b8a69a2f7e1110739d65e2af5d64
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
index ec2f5b9..f41219e 100644
--- a/services/oboeservice/AAudioEndpointManager.cpp
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -26,6 +26,10 @@
 #include <utility/AAudioUtilities.h>
 
 #include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceEndpointMMAP.h"
+#include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointPlay.h"
 
 using namespace android;
 using namespace aaudio;
@@ -34,159 +38,241 @@
 
 AAudioEndpointManager::AAudioEndpointManager()
         : Singleton<AAudioEndpointManager>()
-        , mInputs()
-        , mOutputs() {
+        , mSharedStreams()
+        , mExclusiveStreams() {
 }
 
 std::string AAudioEndpointManager::dump() const {
     std::stringstream result;
-    const bool isLocked = AAudio_tryUntilTrue(
-            [this]()->bool { return mLock.try_lock(); } /* f */,
-            50 /* times */,
-            20 /* sleepMs */);
-    if (!isLocked) {
-        result << "EndpointManager may be deadlocked\n";
-    }
+    int index = 0;
 
     result << "AAudioEndpointManager:" << "\n";
-    size_t inputs = mInputs.size();
-    result << "Input Endpoints: " << inputs << "\n";
-    for (const auto &input : mInputs) {
-        result << "  Input: " << input->dump() << "\n";
+
+    const bool isSharedLocked = AAudio_tryUntilTrue(
+            [this]()->bool { return mSharedLock.try_lock(); } /* f */,
+            50 /* times */,
+            20 /* sleepMs */);
+    if (!isSharedLocked) {
+        result << "AAudioEndpointManager Shared may be deadlocked\n";
     }
 
-    size_t outputs = mOutputs.size();
-    result << "Output Endpoints: " << outputs << "\n";
-    for (const auto &output : mOutputs) {
-        result << "  Output: " << output->dump() << "\n";
+    {
+        const bool isExclusiveLocked = AAudio_tryUntilTrue(
+                [this]() -> bool { return mExclusiveLock.try_lock(); } /* f */,
+                50 /* times */,
+                20 /* sleepMs */);
+        if (!isExclusiveLocked) {
+            result << "AAudioEndpointManager Exclusive may be deadlocked\n";
+        }
+
+        result << "Exclusive MMAP Endpoints: " << mExclusiveStreams.size() << "\n";
+        index = 0;
+        for (const auto &output : mExclusiveStreams) {
+            result << "  #" << index++ << ":";
+            result << output->dump() << "\n";
+        }
+
+        if (isExclusiveLocked) {
+            mExclusiveLock.unlock();
+        }
     }
 
-    if (isLocked) {
-        mLock.unlock();
+    result << "Shared Endpoints: " << mSharedStreams.size() << "\n";
+    index = 0;
+    for (const auto &input : mSharedStreams) {
+        result << "  #" << index++ << ":";
+        result << input->dump() << "\n";
+    }
+
+    if (isSharedLocked) {
+        mSharedLock.unlock();
     }
     return result.str();
 }
 
-AAudioServiceEndpoint *AAudioEndpointManager::openEndpoint(AAudioService &audioService,
-        const AAudioStreamConfiguration& configuration, aaudio_direction_t direction) {
-    AAudioServiceEndpoint *endpoint = nullptr;
-    AAudioServiceEndpointCapture *capture = nullptr;
-    AAudioServiceEndpointPlay *player = nullptr;
-    std::lock_guard<std::mutex> lock(mLock);
+
+// Try to find an existing endpoint.
+sp<AAudioServiceEndpoint> AAudioEndpointManager::findExclusiveEndpoint_l(
+        const AAudioStreamConfiguration &configuration) {
+    sp<AAudioServiceEndpoint> endpoint;
+    for (const auto ep : mExclusiveStreams) {
+        if (ep->matches(configuration)) {
+            endpoint = ep;
+            break;
+        }
+    }
+
+    ALOGD("AAudioEndpointManager.findExclusiveEndpoint_l(), found %p for device = %d",
+          endpoint.get(), configuration.getDeviceId());
+    return endpoint;
+}
+
+// Try to find an existing endpoint.
+sp<AAudioServiceEndpointShared> AAudioEndpointManager::findSharedEndpoint_l(
+        const AAudioStreamConfiguration &configuration) {
+    sp<AAudioServiceEndpointShared> endpoint;
+    for (const auto ep  : mSharedStreams) {
+        if (ep->matches(configuration)) {
+            endpoint = ep;
+            break;
+        }
+    }
+
+    ALOGD("AAudioEndpointManager.findSharedEndpoint_l(), found %p for device = %d",
+          endpoint.get(), configuration.getDeviceId());
+    return endpoint;
+}
+
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openEndpoint(AAudioService &audioService,
+                                        const aaudio::AAudioStreamRequest &request,
+                                        aaudio_sharing_mode_t sharingMode) {
+    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+        return openExclusiveEndpoint(audioService, request);
+    } else {
+        return openSharedEndpoint(audioService, request);
+    }
+}
+
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openExclusiveEndpoint(
+        AAudioService &aaudioService __unused,
+        const aaudio::AAudioStreamRequest &request) {
+
+    std::lock_guard<std::mutex> lock(mExclusiveLock);
+
+    const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
 
     // Try to find an existing endpoint.
+    sp<AAudioServiceEndpoint> endpoint = findExclusiveEndpoint_l(configuration);
 
+    // If we find an existing one then this one cannot be exclusive.
+    if (endpoint.get() != nullptr) {
+        ALOGE("AAudioEndpointManager.openExclusiveEndpoint() already in use");
+        // Already open so do not allow a second stream.
+        return nullptr;
+    } else {
+        sp<AAudioServiceEndpointMMAP> endpointMMap = new AAudioServiceEndpointMMAP();
+        ALOGE("AAudioEndpointManager.openEndpoint(),created MMAP %p", endpointMMap.get());
+        endpoint = endpointMMap;
 
-
-    switch (direction) {
-        case AAUDIO_DIRECTION_INPUT:
-            for (AAudioServiceEndpoint *ep : mInputs) {
-                if (ep->matches(configuration)) {
-                    endpoint = ep;
-                    break;
-                }
-            }
-            break;
-        case AAUDIO_DIRECTION_OUTPUT:
-            for (AAudioServiceEndpoint *ep : mOutputs) {
-                if (ep->matches(configuration)) {
-                    endpoint = ep;
-                    break;
-                }
-            }
-            break;
-        default:
-            assert(false); // There are only two possible directions.
-            break;
-    }
-    ALOGD("AAudioEndpointManager::openEndpoint(), found %p for device = %d, dir = %d",
-          endpoint, configuration.getDeviceId(), (int)direction);
-
-    // If we can't find an existing one then open a new one.
-    if (endpoint == nullptr) {
-        // we must call openStream with audioserver identity
-        int64_t token = IPCThreadState::self()->clearCallingIdentity();
-        switch(direction) {
-            case AAUDIO_DIRECTION_INPUT:
-                capture = new AAudioServiceEndpointCapture(audioService);
-                endpoint = capture;
-                break;
-            case AAUDIO_DIRECTION_OUTPUT:
-                player = new AAudioServiceEndpointPlay(audioService);
-                endpoint = player;
-                break;
-            default:
-                break;
+        aaudio_result_t result = endpoint->open(request);
+        if (result != AAUDIO_OK) {
+            ALOGE("AAudioEndpointManager.openEndpoint(), open failed");
+            endpoint.clear();
+        } else {
+            mExclusiveStreams.push_back(endpointMMap);
         }
 
-        if (endpoint != nullptr) {
-            aaudio_result_t result = endpoint->open(configuration);
-            if (result != AAUDIO_OK) {
-                ALOGE("AAudioEndpointManager::findEndpoint(), open failed");
-                delete endpoint;
-                endpoint = nullptr;
-            } else {
-                switch(direction) {
-                    case AAUDIO_DIRECTION_INPUT:
-                        mInputs.push_back(capture);
-                        break;
-                    case AAUDIO_DIRECTION_OUTPUT:
-                        mOutputs.push_back(player);
-                        break;
-                    default:
-                        break;
-                }
-            }
-        }
-        ALOGD("AAudioEndpointManager::openEndpoint(), created %p for device = %d, dir = %d",
-              endpoint, configuration.getDeviceId(), (int)direction);
-        IPCThreadState::self()->restoreCallingIdentity(token);
+        ALOGD("AAudioEndpointManager.openEndpoint(), created %p for device = %d",
+              endpoint.get(), configuration.getDeviceId());
     }
 
-    if (endpoint != nullptr) {
-        ALOGD("AAudioEndpointManager::openEndpoint(), sampleRate = %d, framesPerBurst = %d",
-              endpoint->getSampleRate(), endpoint->getFramesPerBurst());
+    if (endpoint.get() != nullptr) {
         // Increment the reference count under this lock.
-        endpoint->setReferenceCount(endpoint->getReferenceCount() + 1);
+        endpoint->setOpenCount(endpoint->getOpenCount() + 1);
     }
     return endpoint;
 }
 
-void AAudioEndpointManager::closeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
-    std::lock_guard<std::mutex> lock(mLock);
-    if (serviceEndpoint == nullptr) {
-        return;
-    }
+sp<AAudioServiceEndpoint> AAudioEndpointManager::openSharedEndpoint(
+        AAudioService &aaudioService,
+        const aaudio::AAudioStreamRequest &request) {
 
-    // Decrement the reference count under this lock.
-    int32_t newRefCount = serviceEndpoint->getReferenceCount() - 1;
-    serviceEndpoint->setReferenceCount(newRefCount);
-    ALOGD("AAudioEndpointManager::closeEndpoint(%p) newRefCount = %d",
-          serviceEndpoint, newRefCount);
+    std::lock_guard<std::mutex> lock(mSharedLock);
 
-    // If no longer in use then close and delete it.
-    if (newRefCount <= 0) {
-        aaudio_direction_t direction = serviceEndpoint->getDirection();
-        // Track endpoints based on requested deviceId because UNSPECIFIED
-        // can change to a specific device after opening.
-        int32_t deviceId = serviceEndpoint->getRequestedDeviceId();
+    const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
+    aaudio_direction_t direction = configuration.getDirection();
 
+    // Try to find an existing endpoint.
+    sp<AAudioServiceEndpointShared> endpoint = findSharedEndpoint_l(configuration);
+
+    // If we can't find an existing one then open a new one.
+    if (endpoint.get() == nullptr) {
+        // we must call openStream with audioserver identity
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
         switch (direction) {
             case AAUDIO_DIRECTION_INPUT:
-                mInputs.erase(
-                  std::remove(mInputs.begin(), mInputs.end(), serviceEndpoint), mInputs.end());
+                endpoint = new AAudioServiceEndpointCapture(aaudioService);
                 break;
             case AAUDIO_DIRECTION_OUTPUT:
-                mOutputs.erase(
-                  std::remove(mOutputs.begin(), mOutputs.end(), serviceEndpoint), mOutputs.end());
+                endpoint = new AAudioServiceEndpointPlay(aaudioService);
                 break;
             default:
                 break;
         }
 
+        if (endpoint.get() != nullptr) {
+            aaudio_result_t result = endpoint->open(request);
+            if (result != AAUDIO_OK) {
+                ALOGE("AAudioEndpointManager.openEndpoint(), open failed");
+                endpoint.clear();
+            } else {
+                mSharedStreams.push_back(endpoint);
+            }
+        }
+        ALOGD("AAudioEndpointManager.openSharedEndpoint(), created %p for device = %d, dir = %d",
+              endpoint.get(), configuration.getDeviceId(), (int)direction);
+        IPCThreadState::self()->restoreCallingIdentity(token);
+    }
+
+    if (endpoint.get() != nullptr) {
+        // Increment the reference count under this lock.
+        endpoint->setOpenCount(endpoint->getOpenCount() + 1);
+    }
+    return endpoint;
+}
+
+void AAudioEndpointManager::closeEndpoint(sp<AAudioServiceEndpoint>serviceEndpoint) {
+    if (serviceEndpoint->getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+        return closeExclusiveEndpoint(serviceEndpoint);
+    } else {
+        return closeSharedEndpoint(serviceEndpoint);
+    }
+}
+
+void AAudioEndpointManager::closeExclusiveEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
+    if (serviceEndpoint.get() == nullptr) {
+        return;
+    }
+
+    // Decrement the reference count under this lock.
+    std::lock_guard<std::mutex> lock(mExclusiveLock);
+    int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
+    serviceEndpoint->setOpenCount(newRefCount);
+    ALOGD("AAudioEndpointManager::closeExclusiveEndpoint(%p) newRefCount = %d",
+          serviceEndpoint.get(), newRefCount);
+
+    // If no longer in use then close and delete it.
+    if (newRefCount <= 0) {
+        mExclusiveStreams.erase(
+                std::remove(mExclusiveStreams.begin(), mExclusiveStreams.end(), serviceEndpoint),
+                mExclusiveStreams.end());
+
         serviceEndpoint->close();
-        ALOGD("AAudioEndpointManager::closeEndpoint() delete %p for device %d, dir = %d",
-              serviceEndpoint, deviceId, (int)direction);
-        delete serviceEndpoint;
+        ALOGD("AAudioEndpointManager::closeExclusiveEndpoint() %p for device %d",
+              serviceEndpoint.get(), serviceEndpoint->getDeviceId());
+    }
+}
+
+void AAudioEndpointManager::closeSharedEndpoint(sp<AAudioServiceEndpoint> serviceEndpoint) {
+    if (serviceEndpoint.get() == nullptr) {
+        return;
+    }
+
+    // Decrement the reference count under this lock.
+    std::lock_guard<std::mutex> lock(mSharedLock);
+    int32_t newRefCount = serviceEndpoint->getOpenCount() - 1;
+    serviceEndpoint->setOpenCount(newRefCount);
+    ALOGD("AAudioEndpointManager::closeSharedEndpoint(%p) newRefCount = %d",
+          serviceEndpoint.get(), newRefCount);
+
+    // If no longer in use then close and delete it.
+    if (newRefCount <= 0) {
+        mSharedStreams.erase(
+                std::remove(mSharedStreams.begin(), mSharedStreams.end(), serviceEndpoint),
+                mSharedStreams.end());
+
+        serviceEndpoint->close();
+        ALOGD("AAudioEndpointManager::closeSharedEndpoint() %p for device %d",
+              serviceEndpoint.get(), serviceEndpoint->getDeviceId());
     }
 }
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
index 2511b2f..32c8454 100644
--- a/services/oboeservice/AAudioEndpointManager.h
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -24,11 +24,12 @@
 #include "binding/AAudioServiceMessage.h"
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointMMAP.h"
 #include "AAudioServiceEndpointPlay.h"
 
 namespace aaudio {
 
-class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager>{
+class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager> {
 public:
     AAudioEndpointManager();
     ~AAudioEndpointManager() = default;
@@ -49,22 +50,42 @@
      * Find a service endpoint for the given deviceId and direction.
      * If an endpoint does not already exist then try to create one.
      *
-     * @param deviceId
-     * @param direction
-     * @return endpoint or nullptr
+     * @param audioService
+     * @param request
+     * @param sharingMode
+     * @return endpoint or null
      */
-    AAudioServiceEndpoint *openEndpoint(android::AAudioService &audioService,
-                                        const AAudioStreamConfiguration& configuration,
-                                        aaudio_direction_t direction);
+    android::sp<AAudioServiceEndpoint> openEndpoint(android::AAudioService &audioService,
+                                        const aaudio::AAudioStreamRequest &request,
+                                        aaudio_sharing_mode_t sharingMode);
 
-    void closeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+    void closeEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
 
 private:
+    android::sp<AAudioServiceEndpoint> openExclusiveEndpoint(android::AAudioService &aaudioService,
+                                                 const aaudio::AAudioStreamRequest &request);
 
-    mutable std::mutex mLock;
+    android::sp<AAudioServiceEndpoint> openSharedEndpoint(android::AAudioService &aaudioService,
+                                              const aaudio::AAudioStreamRequest &request);
 
-    std::vector<AAudioServiceEndpointCapture *> mInputs;
-    std::vector<AAudioServiceEndpointPlay *> mOutputs;
+    android::sp<AAudioServiceEndpoint> findExclusiveEndpoint_l(
+            const AAudioStreamConfiguration& configuration);
+
+    android::sp<AAudioServiceEndpointShared> findSharedEndpoint_l(
+            const AAudioStreamConfiguration& configuration);
+
+    void closeExclusiveEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
+    void closeSharedEndpoint(android::sp<AAudioServiceEndpoint> serviceEndpoint);
+
+    // Use separate locks because opening a Shared endpoint requires opening an Exclusive one.
+    // That could cause a recursive lock.
+    // Lock mSharedLock before mExclusiveLock.
+    // it is OK to only lock mExclusiveLock.
+    mutable std::mutex                                     mSharedLock;
+    std::vector<android::sp<AAudioServiceEndpointShared>>  mSharedStreams;
+
+    mutable std::mutex                                     mExclusiveLock;
+    std::vector<android::sp<AAudioServiceEndpointMMAP>>    mExclusiveStreams;
 
 };
 
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 3992719..855ae69 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -114,14 +114,12 @@
                 mAudioClient.clientUid == IPCThreadState::self()->getCallingUid()) {
             inService = request.isInService();
         }
-        serviceStream = new AAudioServiceStreamMMAP(mAudioClient, inService);
-        result = serviceStream->open(request, configurationOutput);
+        serviceStream = new AAudioServiceStreamMMAP(*this, inService);
+        result = serviceStream->open(request);
         if (result != AAUDIO_OK) {
-            // fall back to using a shared stream
+            // Clear it so we can possibly fall back to using a shared stream.
             ALOGW("AAudioService::openStream(), could not open in EXCLUSIVE mode");
             serviceStream.clear();
-        } else {
-            configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
         }
     }
 
@@ -129,8 +127,7 @@
     if (sharingMode == AAUDIO_SHARING_MODE_SHARED
          || (serviceStream == nullptr && !sharingModeMatchRequired)) {
         serviceStream =  new AAudioServiceStreamShared(*this);
-        result = serviceStream->open(request, configurationOutput);
-        configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+        result = serviceStream->open(request);
     }
 
     if (result != AAUDIO_OK) {
@@ -149,6 +146,7 @@
             serviceStream->setHandle(handle);
             pid_t pid = request.getProcessId();
             AAudioClientTracker::getInstance().registerClientStream(pid, serviceStream);
+            configurationOutput.copyFrom(*serviceStream);
         }
         return handle;
     }
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index 8421efc..ffaf538 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -28,7 +28,9 @@
 #include "binding/IAAudioService.h"
 #include "binding/AAudioServiceInterface.h"
 
-#include "AAudioServiceStreamBase.h"
+namespace aaudio {
+    class AAudioServiceStreamBase;
+};
 
 namespace android {
 
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index 81f1d1b..cba5bc8 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -33,17 +33,14 @@
 #include "core/AudioStreamBuilder.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
 
-#define MIN_TIMEOUT_NANOS        (1000 * AAUDIO_NANOS_PER_MILLISECOND)
-
-// Wait at least this many times longer than the operation should take.
-#define MIN_TIMEOUT_OPERATIONS    4
-
-// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
-#define DEFAULT_BUFFER_CAPACITY   (48 * 8)
+AAudioServiceEndpoint::~AAudioServiceEndpoint() {
+    ALOGD("AAudioServiceEndpoint::~AAudioServiceEndpoint() destroying endpoint %p", this);
+}
 
 std::string AAudioServiceEndpoint::dump() const {
     std::stringstream result;
@@ -53,20 +50,20 @@
             50 /* times */,
             20 /* sleepMs */);
     if (!isLocked) {
-        result << "EndpointManager may be deadlocked\n";
+        result << "AAudioServiceEndpoint 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";
+    result << "    Direction:            " << ((getDirection() == AAUDIO_DIRECTION_OUTPUT)
+                                   ? "OUTPUT" : "INPUT") << "\n";
+    result << "    Sample Rate:          " << getSampleRate() << "\n";
+    result << "    Frames Per Burst:     " << mFramesPerBurst << "\n";
+    result << "    Reference Count:      " << mOpenCount << "\n";
+    result << "    Requested Device Id:  " << mRequestedDeviceId << "\n";
+    result << "    Device Id:            " << getDeviceId() << "\n";
+    result << "    Registered Streams:  " << "\n";
     result << AAudioServiceStreamShared::dumpHeader() << "\n";
-    for (sp<AAudioServiceStreamShared> sharedStream : mRegisteredStreams) {
-        result << sharedStream->dump() << "\n";
+    for (const auto stream : mRegisteredStreams) {
+        result << stream->dump() << "\n";
     }
 
     if (isLocked) {
@@ -75,113 +72,44 @@
     return result.str();
 }
 
-// Set up an EXCLUSIVE MMAP stream that will be shared.
-aaudio_result_t AAudioServiceEndpoint::open(const AAudioStreamConfiguration& configuration) {
-    mRequestedDeviceId = configuration.getDeviceId();
-    mStreamInternal = getStreamInternal();
-
-    AudioStreamBuilder builder;
-    builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
-    // Don't fall back to SHARED because that would cause recursion.
-    builder.setSharingModeMatchRequired(true);
-    builder.setDeviceId(mRequestedDeviceId);
-    builder.setFormat(configuration.getFormat());
-    builder.setSampleRate(configuration.getSampleRate());
-    builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
-    builder.setDirection(getDirection());
-    builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
-
-    return getStreamInternal()->open(builder);
-}
-
-aaudio_result_t AAudioServiceEndpoint::close() {
-     return getStreamInternal()->close();
-}
-
-// TODO, maybe use an interface to reduce exposure
-aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamShared>sharedStream) {
-    std::lock_guard<std::mutex> lock(mLockStreams);
-    mRegisteredStreams.push_back(sharedStream);
-    return AAUDIO_OK;
-}
-
-aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamShared>sharedStream) {
-    std::lock_guard<std::mutex> lock(mLockStreams);
-    mRegisteredStreams.erase(std::remove(mRegisteredStreams.begin(), mRegisteredStreams.end(), sharedStream),
-              mRegisteredStreams.end());
-    return AAUDIO_OK;
-}
-
-aaudio_result_t AAudioServiceEndpoint::startStream(sp<AAudioServiceStreamShared> sharedStream) {
-    aaudio_result_t result = AAUDIO_OK;
-    if (++mRunningStreams == 1) {
-        // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
-        std::lock_guard<std::mutex> lock(mLockStreams);
-        result = getStreamInternal()->requestStart();
-        startSharingThread_l();
-    }
-    return result;
-}
-
-aaudio_result_t AAudioServiceEndpoint::stopStream(sp<AAudioServiceStreamShared> sharedStream) {
-    // Don't lock here because the disconnectRegisteredStreams also uses the lock.
-    if (--mRunningStreams == 0) { // atomic
-        stopSharingThread();
-        getStreamInternal()->requestStop();
-    }
-    return AAUDIO_OK;
-}
-
-static void *aaudio_endpoint_thread_proc(void *context) {
-    AAudioServiceEndpoint *endpoint = (AAudioServiceEndpoint *) context;
-    if (endpoint != NULL) {
-        return endpoint->callbackLoop();
-    } else {
-        return NULL;
-    }
-}
-
-aaudio_result_t AAudioServiceEndpoint::startSharingThread_l() {
-    // Launch the callback loop thread.
-    int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
-                          * AAUDIO_NANOS_PER_SECOND
-                          / getSampleRate();
-    mCallbackEnabled.store(true);
-    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
-}
-
-aaudio_result_t AAudioServiceEndpoint::stopSharingThread() {
-    mCallbackEnabled.store(false);
-    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
-    return result;
-}
-
 void AAudioServiceEndpoint::disconnectRegisteredStreams() {
     std::lock_guard<std::mutex> lock(mLockStreams);
-    for(auto sharedStream : mRegisteredStreams) {
-        sharedStream->stop();
-        sharedStream->disconnect();
+    for (const auto stream : mRegisteredStreams) {
+        stream->stop();
+        stream->disconnect();
     }
     mRegisteredStreams.clear();
 }
 
+aaudio_result_t AAudioServiceEndpoint::registerStream(sp<AAudioServiceStreamBase>stream) {
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRegisteredStreams.push_back(stream);
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpoint::unregisterStream(sp<AAudioServiceStreamBase>stream) {
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRegisteredStreams.erase(std::remove(
+            mRegisteredStreams.begin(), mRegisteredStreams.end(), stream),
+                             mRegisteredStreams.end());
+    return AAUDIO_OK;
+}
+
 bool AAudioServiceEndpoint::matches(const AAudioStreamConfiguration& configuration) {
+    if (configuration.getDirection() != getDirection()) {
+        return false;
+    }
     if (configuration.getDeviceId() != AAUDIO_UNSPECIFIED &&
-            configuration.getDeviceId() != mStreamInternal->getDeviceId()) {
+        configuration.getDeviceId() != getDeviceId()) {
         return false;
     }
     if (configuration.getSampleRate() != AAUDIO_UNSPECIFIED &&
-            configuration.getSampleRate() != mStreamInternal->getSampleRate()) {
+        configuration.getSampleRate() != getSampleRate()) {
         return false;
     }
     if (configuration.getSamplesPerFrame() != AAUDIO_UNSPECIFIED &&
-            configuration.getSamplesPerFrame() != mStreamInternal->getSamplesPerFrame()) {
+        configuration.getSamplesPerFrame() != getSamplesPerFrame()) {
         return false;
     }
     return true;
 }
-
-
-aaudio_result_t AAudioServiceEndpoint::getTimestamp(int64_t *positionFrames, int64_t *timeNanos) {
-    return mStreamInternal->getTimestamp(CLOCK_MONOTONIC, positionFrames, timeNanos);
-}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
index 603d497..2ef6234 100644
--- a/services/oboeservice/AAudioServiceEndpoint.h
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -24,72 +24,93 @@
 
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalPlay.h"
+#include "core/AAudioStreamParameters.h"
 #include "binding/AAudioServiceMessage.h"
-#include "AAudioServiceStreamShared.h"
-#include "AAudioServiceStreamMMAP.h"
-#include "AAudioMixer.h"
-#include "AAudioService.h"
+#include "binding/AAudioStreamConfiguration.h"
+
+#include "AAudioServiceStreamBase.h"
 
 namespace aaudio {
 
-class AAudioServiceEndpoint {
+/**
+ * AAudioServiceEndpoint is used by a subclass of AAudioServiceStreamBase
+ * to communicate with the underlying audio device or port.
+ */
+class AAudioServiceEndpoint
+        : public virtual android::RefBase
+        , public AAudioStreamParameters {
 public:
-    virtual ~AAudioServiceEndpoint() = default;
 
-    std::string dump() const;
+    virtual ~AAudioServiceEndpoint();
 
-    virtual aaudio_result_t open(const AAudioStreamConfiguration& configuration);
+    virtual std::string dump() const;
 
-    int32_t getSampleRate() const { return mStreamInternal->getSampleRate(); }
-    int32_t getSamplesPerFrame() const { return mStreamInternal->getSamplesPerFrame();  }
-    int32_t getFramesPerBurst() const { return mStreamInternal->getFramesPerBurst();  }
+    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
 
-    aaudio_result_t registerStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t startStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t stopStream(android::sp<AAudioServiceStreamShared> sharedStream);
-    aaudio_result_t close();
+    virtual aaudio_result_t close() = 0;
+
+    virtual aaudio_result_t registerStream(android::sp<AAudioServiceStreamBase> stream);
+
+    virtual aaudio_result_t unregisterStream(android::sp<AAudioServiceStreamBase> stream);
+
+    virtual aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+                                        audio_port_handle_t *clientHandle) = 0;
+
+    virtual aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+                                       audio_port_handle_t clientHandle) = 0;
+
+    virtual aaudio_result_t startClient(const android::AudioClient& client,
+                                        audio_port_handle_t *clientHandle) {
+        ALOGD("AAudioServiceEndpoint::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle) {
+        ALOGD("AAudioServiceEndpoint::stopClient(...) AAUDIO_ERROR_UNAVAILABLE");
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    /**
+     * @param positionFrames
+     * @param timeNanos
+     * @return AAUDIO_OK or AAUDIO_ERROR_UNAVAILABLE or other negative error
+     */
+    virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
+    virtual aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
+    int32_t getFramesPerBurst() const {
+        return mFramesPerBurst;
+    }
 
     int32_t getRequestedDeviceId() const { return mRequestedDeviceId; }
-    int32_t getDeviceId() const { return mStreamInternal->getDeviceId(); }
-
-    aaudio_direction_t getDirection() const { return mStreamInternal->getDirection(); }
-
-    void disconnectRegisteredStreams();
-
-    virtual void *callbackLoop() = 0;
-
-    // This should only be called from the AAudioEndpointManager under a mutex.
-    int32_t getReferenceCount() const {
-        return mReferenceCount;
-    }
-
-    // This should only be called from the AAudioEndpointManager under a mutex.
-    void setReferenceCount(int32_t count) {
-        mReferenceCount = count;
-    }
-
-    aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos);
 
     bool matches(const AAudioStreamConfiguration& configuration);
 
-    virtual AudioStreamInternal *getStreamInternal() = 0;
+    // This should only be called from the AAudioEndpointManager under a mutex.
+    int32_t getOpenCount() const {
+        return mOpenCount;
+    }
 
-    std::atomic<bool>        mCallbackEnabled{false};
+    // This should only be called from the AAudioEndpointManager under a mutex.
+    void setOpenCount(int32_t count) {
+        mOpenCount = count;
+    }
+
+protected:
+    void                     disconnectRegisteredStreams();
 
     mutable std::mutex       mLockStreams;
+    std::vector<android::sp<AAudioServiceStreamBase>> mRegisteredStreams;
 
-    std::vector<android::sp<AAudioServiceStreamShared>> mRegisteredStreams;
+    SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
 
-    std::atomic<int>         mRunningStreams{0};
+    android::AudioClient     mMmapClient;   // set in open, used in open and startStream
 
-private:
-    aaudio_result_t startSharingThread_l();
-    aaudio_result_t stopSharingThread();
-
-    AudioStreamInternal     *mStreamInternal = nullptr;
-    int32_t                  mReferenceCount = 0;
+    int32_t                  mFramesPerBurst = 0;
+    int32_t                  mOpenCount = 0;
     int32_t                  mRequestedDeviceId = 0;
+
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.cpp b/services/oboeservice/AAudioServiceEndpointCapture.cpp
index 6504cc1..97558ca 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.cpp
+++ b/services/oboeservice/AAudioServiceEndpointCapture.cpp
@@ -30,20 +30,22 @@
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceEndpointCapture.h"
+#include "AAudioServiceEndpointShared.h"
 
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
 
 AAudioServiceEndpointCapture::AAudioServiceEndpointCapture(AAudioService &audioService)
         : mStreamInternalCapture(audioService, true) {
+    mStreamInternal = &mStreamInternalCapture;
 }
 
 AAudioServiceEndpointCapture::~AAudioServiceEndpointCapture() {
     delete mDistributionBuffer;
 }
 
-aaudio_result_t AAudioServiceEndpointCapture::open(const AAudioStreamConfiguration& configuration) {
-    aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
+aaudio_result_t AAudioServiceEndpointCapture::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAudioServiceEndpointShared::open(request);
     if (result == AAUDIO_OK) {
         delete mDistributionBuffer;
         int distributionBufferSizeBytes = getStreamInternal()->getFramesPerBurst()
@@ -80,16 +82,19 @@
         { // brackets are for lock_guard
 
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (sp<AAudioServiceStreamShared> clientStream : mRegisteredStreams) {
+            for (const auto clientStream : mRegisteredStreams) {
                 if (clientStream->isRunning()) {
-                    FifoBuffer *fifo = clientStream->getDataFifoBuffer();
+                    AAudioServiceStreamShared *streamShared =
+                            static_cast<AAudioServiceStreamShared *>(clientStream.get());
+
+                    FifoBuffer *fifo = streamShared->getDataFifoBuffer();
 
                     // Determine offset between framePosition in client's stream vs the underlying
                     // MMAP stream.
                     int64_t clientFramesWritten = fifo->getWriteCounter();
                     // There are two indices that refer to the same frame.
                     int64_t positionOffset = mmapFramesRead - clientFramesWritten;
-                    clientStream->setTimestampPositionOffset(positionOffset);
+                    streamShared->setTimestampPositionOffset(positionOffset);
 
                     if (fifo->getFifoControllerBase()->getEmptyFramesAvailable() <
                         getFramesPerBurst()) {
@@ -102,7 +107,7 @@
                     // client buffer. It is sent to the client and used in the timing model
                     // to decide when data will be available to read.
                     Timestamp timestamp(fifo->getWriteCounter(), AudioClock::getNanoseconds());
-                    clientStream->markTransferTime(timestamp);
+                    streamShared->markTransferTime(timestamp);
                 }
             }
         }
diff --git a/services/oboeservice/AAudioServiceEndpointCapture.h b/services/oboeservice/AAudioServiceEndpointCapture.h
index 8a3d72f..971da9a 100644
--- a/services/oboeservice/AAudioServiceEndpointCapture.h
+++ b/services/oboeservice/AAudioServiceEndpointCapture.h
@@ -20,18 +20,18 @@
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalCapture.h"
 
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+
 namespace aaudio {
 
-class AAudioServiceEndpointCapture : public AAudioServiceEndpoint {
+class AAudioServiceEndpointCapture : public AAudioServiceEndpointShared {
 public:
     explicit AAudioServiceEndpointCapture(android::AAudioService &audioService);
     virtual ~AAudioServiceEndpointCapture();
 
-    aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
-    AudioStreamInternal *getStreamInternal() override {
-        return &mStreamInternalCapture;
-    }
 
     void *callbackLoop() override;
 
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.cpp b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
new file mode 100644
index 0000000..58213f8
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.cpp
@@ -0,0 +1,350 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "AAudioServiceEndpointMMAP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <algorithm>
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <sstream>
+#include <utils/Singleton.h>
+#include <vector>
+
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+
+#include "core/AudioStreamBuilder.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceEndpointPlay.h"
+#include "AAudioServiceEndpointMMAP.h"
+
+
+#define AAUDIO_BUFFER_CAPACITY_MIN    4 * 512
+#define AAUDIO_SAMPLE_RATE_DEFAULT    48000
+
+// This is an estimate of the time difference between the HW and the MMAP time.
+// TODO Get presentation timestamps from the HAL instead of using these estimates.
+#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS  (3 * AAUDIO_NANOS_PER_MILLISECOND)
+#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS   (-1 * AAUDIO_NANOS_PER_MILLISECOND)
+
+using namespace android;  // TODO just import names needed
+using namespace aaudio;   // TODO just import names needed
+
+AAudioServiceEndpointMMAP::AAudioServiceEndpointMMAP()
+        :  mMmapStream(nullptr) {}
+
+AAudioServiceEndpointMMAP::~AAudioServiceEndpointMMAP() {}
+
+std::string AAudioServiceEndpointMMAP::dump() const {
+    std::stringstream result;
+
+    result << "  MMAP: framesTransferred = " << mFramesTransferred.get();
+    result << ", HW nanos = " << mHardwareTimeOffsetNanos;
+    result << ", port handle = " << mPortHandle;
+    result << ", audio data FD = " << mAudioDataFileDescriptor;
+    result << "\n";
+
+    result << "    HW Offset Micros:     " <<
+                                      (getHardwareTimeOffsetNanos()
+                                       / AAUDIO_NANOS_PER_MICROSECOND) << "\n";
+
+    result << AAudioServiceEndpoint::dump();
+    return result.str();
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAUDIO_OK;
+    const audio_attributes_t attributes = {
+            .content_type = AUDIO_CONTENT_TYPE_MUSIC,
+            .usage = AUDIO_USAGE_MEDIA,
+            .source = AUDIO_SOURCE_VOICE_RECOGNITION,
+            .flags = AUDIO_FLAG_LOW_LATENCY,
+            .tags = ""
+    };
+    audio_config_base_t config;
+    audio_port_handle_t deviceId;
+
+    int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
+    int32_t burstMicros = 0;
+
+    copyFrom(request.getConstantConfiguration());
+
+    mMmapClient.clientUid = request.getUserId();
+    mMmapClient.clientPid = request.getProcessId();
+    mMmapClient.packageName.setTo(String16(""));
+
+    mRequestedDeviceId = deviceId = getDeviceId();
+
+    // Fill in config
+    aaudio_format_t aaudioFormat = getFormat();
+    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+    }
+    config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
+
+    int32_t aaudioSampleRate = getSampleRate();
+    if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
+        aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
+    }
+    config.sample_rate = aaudioSampleRate;
+
+    int32_t aaudioSamplesPerFrame = getSamplesPerFrame();
+
+    aaudio_direction_t direction = getDirection();
+    if (direction == AAUDIO_DIRECTION_OUTPUT) {
+        config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+                              ? AUDIO_CHANNEL_OUT_STEREO
+                              : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
+        mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
+
+    } else if (direction == AAUDIO_DIRECTION_INPUT) {
+        config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+                               ? AUDIO_CHANNEL_IN_STEREO
+                               : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
+        mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
+
+    } else {
+        ALOGE("openMmapStream - invalid direction = %d", direction);
+        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    MmapStreamInterface::stream_direction_t streamDirection =
+            (direction == AAUDIO_DIRECTION_OUTPUT)
+            ? MmapStreamInterface::DIRECTION_OUTPUT
+            : MmapStreamInterface::DIRECTION_INPUT;
+
+    // Open HAL stream. Set mMmapStream
+    status_t status = MmapStreamInterface::openMmapStream(streamDirection,
+                                                          &attributes,
+                                                          &config,
+                                                          mMmapClient,
+                                                          &deviceId,
+                                                          this, // callback
+                                                          mMmapStream,
+                                                          &mPortHandle);
+    ALOGD("AAudioServiceEndpointMMAP::open() mMapClient.uid = %d, pid = %d => portHandle = %d\n",
+          mMmapClient.clientUid,  mMmapClient.clientPid, mPortHandle);
+    if (status != OK) {
+        ALOGE("openMmapStream returned status %d", status);
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    if (deviceId == AAUDIO_UNSPECIFIED) {
+        ALOGW("AAudioServiceEndpointMMAP::open() - openMmapStream() failed to set deviceId");
+    }
+    setDeviceId(deviceId);
+
+    // Create MMAP/NOIRQ buffer.
+    int32_t minSizeFrames = getBufferCapacity();
+    if (minSizeFrames <= 0) { // zero will get rejected
+        minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
+    }
+    status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
+    if (status != OK) {
+        ALOGE("AAudioServiceEndpointMMAP::open() - createMmapBuffer() failed with status %d %s",
+              status, strerror(-status));
+        result = AAUDIO_ERROR_UNAVAILABLE;
+        goto error;
+    } else {
+        ALOGD("createMmapBuffer status = %d, buffer_size = %d, burst_size %d"
+                      ", Sharable FD: %s",
+              status,
+              abs(mMmapBufferinfo.buffer_size_frames),
+              mMmapBufferinfo.burst_size_frames,
+              mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
+    }
+
+    setBufferCapacity(mMmapBufferinfo.buffer_size_frames);
+    // The audio HAL indicates if the shared memory fd can be shared outside of audioserver
+    // by returning a negative buffer size
+    if (getBufferCapacity() < 0) {
+        // Exclusive mode can be used by client or service.
+        setBufferCapacity(-getBufferCapacity());
+    } else {
+        // Exclusive mode can only be used by the service because the FD cannot be shared.
+        uid_t audioServiceUid = getuid();
+        if ((mMmapClient.clientUid != audioServiceUid) &&
+            getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+            // Fallback is handled by caller but indicate what is possible in case
+            // this is used in the future
+            setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+            ALOGW("AAudioServiceEndpointMMAP::open() - exclusive FD cannot be used by client");
+            result = AAUDIO_ERROR_UNAVAILABLE;
+            goto error;
+        }
+    }
+
+    // Get information about the stream and pass it back to the caller.
+    setSamplesPerFrame((direction == AAUDIO_DIRECTION_OUTPUT)
+                       ? audio_channel_count_from_out_mask(config.channel_mask)
+                       : audio_channel_count_from_in_mask(config.channel_mask));
+
+    // AAudio creates a copy of this FD and retains ownership of the copy.
+    // Assume that AudioFlinger will close the original shared_memory_fd.
+    mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
+    if (mAudioDataFileDescriptor.get() == -1) {
+        ALOGE("AAudioServiceEndpointMMAP::open() - could not dup shared_memory_fd");
+        result = AAUDIO_ERROR_INTERNAL;
+        goto error;
+    }
+    mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
+    setFormat(AAudioConvert_androidToAAudioDataFormat(config.format));
+    setSampleRate(config.sample_rate);
+
+    // Scale up the burst size to meet the minimum equivalent in microseconds.
+    // This is to avoid waking the CPU too often when the HW burst is very small
+    // or at high sample rates.
+    do {
+        if (burstMicros > 0) {  // skip first loop
+            mFramesPerBurst *= 2;
+        }
+        burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
+    } while (burstMicros < burstMinMicros);
+
+    ALOGD("AAudioServiceEndpointMMAP::open() original burst = %d, minMicros = %d, to burst = %d\n",
+          mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
+
+    ALOGD("AAudioServiceEndpointMMAP::open() actual rate = %d, channels = %d"
+          ", deviceId = %d, capacity = %d\n",
+          getSampleRate(), getSamplesPerFrame(), deviceId, getBufferCapacity());
+
+    return result;
+
+error:
+    close();
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::close() {
+
+    if (mMmapStream != 0) {
+        ALOGD("AAudioServiceEndpointMMAP::close() clear() endpoint");
+        // 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
+        // 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);
+    }
+
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::startStream(sp<AAudioServiceStreamBase> stream,
+                                                   audio_port_handle_t *clientHandle) {
+    return startClient(mMmapClient, &mPortHandle);
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::stopStream(sp<AAudioServiceStreamBase> stream,
+                                                  audio_port_handle_t clientHandle) {
+    mFramesTransferred.reset32();
+    return stopClient(mPortHandle);
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::startClient(const android::AudioClient& client,
+                                                       audio_port_handle_t *clientHandle) {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+    audio_port_handle_t originalHandle =  *clientHandle;
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->start(client,
+                                                                                    clientHandle));
+    ALOGD("AAudioServiceEndpointMMAP::startClient(%p(uid=%d, pid=%d), %d => %d) returns %d",
+          &client, client.clientUid, client.clientPid,
+          originalHandle, *clientHandle, result);
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::stopClient(audio_port_handle_t clientHandle) {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+    ALOGD("AAudioServiceEndpointMMAP::stopClient(%d) returns %d", clientHandle, result);
+    return result;
+}
+
+// Get free-running DSP or DMA hardware position from the HAL.
+aaudio_result_t AAudioServiceEndpointMMAP::getFreeRunningPosition(int64_t *positionFrames,
+                                                                int64_t *timeNanos) {
+    struct audio_mmap_position position;
+    if (mMmapStream == nullptr) {
+        return AAUDIO_ERROR_NULL;
+    }
+    status_t status = mMmapStream->getMmapPosition(&position);
+    ALOGV("AAudioServiceEndpointMMAP::getFreeRunningPosition() status= %d, pos = %d, nanos = %lld\n",
+          status, position.position_frames, (long long) position.time_nanoseconds);
+    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
+    if (result == AAUDIO_ERROR_UNAVAILABLE) {
+        ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data available");
+    } else if (result != AAUDIO_OK) {
+        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
+    } else {
+        // Convert 32-bit position to 64-bit position.
+        mFramesTransferred.update32(position.position_frames);
+        *positionFrames = mFramesTransferred.get();
+        *timeNanos = position.time_nanoseconds;
+    }
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointMMAP::getTimestamp(int64_t *positionFrames,
+                                                    int64_t *timeNanos) {
+    return 0; // TODO
+}
+
+
+void AAudioServiceEndpointMMAP::onTearDown() {
+    ALOGD("AAudioServiceEndpointMMAP::onTearDown() called");
+    disconnectRegisteredStreams();
+};
+
+void AAudioServiceEndpointMMAP::onVolumeChanged(audio_channel_mask_t channels,
+                                              android::Vector<float> values) {
+    // TODO do we really need a different volume for each channel?
+    float volume = values[0];
+    ALOGD("AAudioServiceEndpointMMAP::onVolumeChanged() volume[0] = %f", volume);
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    for(const auto stream : mRegisteredStreams) {
+        stream->onVolumeChanged(volume);
+    }
+};
+
+void AAudioServiceEndpointMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
+    ALOGD("AAudioServiceEndpointMMAP::onRoutingChanged() called with %d, old = %d",
+          deviceId, getDeviceId());
+    if (getDeviceId() != AUDIO_PORT_HANDLE_NONE  && getDeviceId() != deviceId) {
+        disconnectRegisteredStreams();
+    }
+    setDeviceId(deviceId);
+};
+
+/**
+ * Get an immutable description of the data queue from the HAL.
+ */
+aaudio_result_t AAudioServiceEndpointMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
+{
+    // Gather information on the data queue based on HAL info.
+    int32_t bytesPerFrame = calculateBytesPerFrame();
+    int32_t capacityInBytes = getBufferCapacity() * bytesPerFrame;
+    int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
+    parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
+    parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
+    parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
+    parcelable.mDownDataQueueParcelable.setCapacityInFrames(getBufferCapacity());
+    return AAUDIO_OK;
+}
diff --git a/services/oboeservice/AAudioServiceEndpointMMAP.h b/services/oboeservice/AAudioServiceEndpointMMAP.h
new file mode 100644
index 0000000..16b6269
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointMMAP.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef AAUDIO_SERVICE_ENDPOINT_MMAP_H
+#define AAUDIO_SERVICE_ENDPOINT_MMAP_H
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+/**
+ * This is used by AAudioServiceStreamMMAP to access the MMAP devices
+ * through AudioFlinger.
+ */
+class AAudioServiceEndpointMMAP
+        : public AAudioServiceEndpoint
+        , public android::MmapStreamCallback {
+
+public:
+    AAudioServiceEndpointMMAP();
+
+    virtual ~AAudioServiceEndpointMMAP();
+
+    std::string dump() const override;
+
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
+
+    aaudio_result_t close() override;
+
+    aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+                                audio_port_handle_t *clientHandle) override;
+
+    aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+                               audio_port_handle_t clientHandle) override;
+
+    aaudio_result_t startClient(const android::AudioClient& client,
+                                        audio_port_handle_t *clientHandle)  override;
+
+    aaudio_result_t stopClient(audio_port_handle_t clientHandle)  override;
+
+    aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    // -------------- Callback functions for MmapStreamCallback ---------------------
+    void onTearDown() override;
+
+    void onVolumeChanged(audio_channel_mask_t channels,
+                         android::Vector<float> values) override;
+
+    void onRoutingChanged(audio_port_handle_t deviceId) override;
+    // ------------------------------------------------------------------------------
+
+    aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable);
+
+    int64_t getHardwareTimeOffsetNanos() const {
+        return mHardwareTimeOffsetNanos;
+    }
+
+private:
+    MonotonicCounter                          mFramesTransferred;
+
+    // Interface to the AudioFlinger MMAP support.
+    android::sp<android::MmapStreamInterface> mMmapStream;
+    struct audio_mmap_buffer_info             mMmapBufferinfo;
+    audio_port_handle_t                       mPortHandle = AUDIO_PORT_HANDLE_NONE;
+
+    android::base::unique_fd                  mAudioDataFileDescriptor;
+
+    int64_t                                   mHardwareTimeOffsetNanos = 0; // TODO get from HAL
+
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_SERVICE_ENDPOINT_MMAP_H
+
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.cpp b/services/oboeservice/AAudioServiceEndpointPlay.cpp
index 8b1cc9f..c42a6e2 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.cpp
+++ b/services/oboeservice/AAudioServiceEndpointPlay.cpp
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#define LOG_TAG "AAudioService"
+#define LOG_TAG "AAudioServiceEndpointPlay"
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
@@ -33,6 +33,7 @@
 #include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceEndpointPlay.h"
+#include "AAudioServiceEndpointShared.h"
 
 using namespace android;  // TODO just import names needed
 using namespace aaudio;   // TODO just import names needed
@@ -41,13 +42,14 @@
 
 AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
         : mStreamInternalPlay(audioService, true) {
+    mStreamInternal = &mStreamInternalPlay;
 }
 
 AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
 }
 
-aaudio_result_t AAudioServiceEndpointPlay::open(const AAudioStreamConfiguration& configuration) {
-    aaudio_result_t result = AAudioServiceEndpoint::open(configuration);
+aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAudioServiceEndpointShared::open(request);
     if (result == AAUDIO_OK) {
         mMixer.allocate(getStreamInternal()->getSamplesPerFrame(),
                         getStreamInternal()->getFramesPerBurst());
@@ -72,35 +74,42 @@
     while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
         // Mix data from each active stream.
         mMixer.clear();
+
         { // brackets are for lock_guard
             int index = 0;
             int64_t mmapFramesWritten = getStreamInternal()->getFramesWritten();
 
             std::lock_guard <std::mutex> lock(mLockStreams);
-            for (sp<AAudioServiceStreamShared> clientStream : mRegisteredStreams) {
-                if (clientStream->isRunning()) {
-                    FifoBuffer *fifo = clientStream->getDataFifoBuffer();
-                    // Determine offset between framePosition in client's stream vs the underlying
-                    // MMAP stream.
-                    int64_t clientFramesRead = fifo->getReadCounter();
-                    // These two indices refer to the same frame.
-                    int64_t positionOffset = mmapFramesWritten - clientFramesRead;
-                    clientStream->setTimestampPositionOffset(positionOffset);
-
-                    float volume = 1.0; // to match legacy volume
-                    bool underflowed = mMixer.mix(index, fifo, volume);
-
-                    // This timestamp represents the completion of data being read out of the
-                    // client buffer. It is sent to the client and used in the timing model
-                    // to decide when the client has room to write more data.
-                    Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
-                    clientStream->markTransferTime(timestamp);
-
-                    if (underflowed) {
-                        clientStream->incrementXRunCount();
-                    }
+            for (const auto clientStream : mRegisteredStreams) {
+                if (!clientStream->isRunning()) {
+                    continue;
                 }
-                index++;
+
+                AAudioServiceStreamShared *streamShared =
+                        static_cast<AAudioServiceStreamShared *>(clientStream.get());
+
+                FifoBuffer *fifo = streamShared->getDataFifoBuffer();
+                // Determine offset between framePosition in client's stream vs the underlying
+                // MMAP stream.
+                int64_t clientFramesRead = fifo->getReadCounter();
+                // These two indices refer to the same frame.
+                int64_t positionOffset = mmapFramesWritten - clientFramesRead;
+                streamShared->setTimestampPositionOffset(positionOffset);
+
+                float volume = 1.0; // to match legacy volume
+                bool underflowed = mMixer.mix(index, fifo, volume);
+
+                // This timestamp represents the completion of data being read out of the
+                // client buffer. It is sent to the client and used in the timing model
+                // to decide when the client has room to write more data.
+                Timestamp timestamp(fifo->getReadCounter(), AudioClock::getNanoseconds());
+                streamShared->markTransferTime(timestamp);
+
+                if (underflowed) {
+                    streamShared->incrementXRunCount();
+                }
+
+                index++; // just used for labelling tracks in systrace
             }
         }
 
@@ -108,7 +117,7 @@
         result = getStreamInternal()->write(mMixer.getOutputBuffer(),
                                             getFramesPerBurst(), timeoutNanos);
         if (result == AAUDIO_ERROR_DISCONNECTED) {
-            disconnectRegisteredStreams();
+            AAudioServiceEndpointShared::disconnectRegisteredStreams();
             break;
         } else if (result != getFramesPerBurst()) {
             ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
diff --git a/services/oboeservice/AAudioServiceEndpointPlay.h b/services/oboeservice/AAudioServiceEndpointPlay.h
index c22f510..a0a383c 100644
--- a/services/oboeservice/AAudioServiceEndpointPlay.h
+++ b/services/oboeservice/AAudioServiceEndpointPlay.h
@@ -25,6 +25,7 @@
 #include "client/AudioStreamInternal.h"
 #include "client/AudioStreamInternalPlay.h"
 #include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpointShared.h"
 #include "AAudioServiceStreamShared.h"
 #include "AAudioServiceStreamMMAP.h"
 #include "AAudioMixer.h"
@@ -35,16 +36,12 @@
 /**
  * Contains a mixer and a stream for writing the result of the mix.
  */
-class AAudioServiceEndpointPlay : public AAudioServiceEndpoint {
+class AAudioServiceEndpointPlay : public AAudioServiceEndpointShared {
 public:
     explicit AAudioServiceEndpointPlay(android::AAudioService &audioService);
     virtual ~AAudioServiceEndpointPlay();
 
-    aaudio_result_t open(const AAudioStreamConfiguration& configuration) override;
-
-    AudioStreamInternal *getStreamInternal() override {
-        return &mStreamInternalPlay;
-    }
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     void *callbackLoop() override;
 
diff --git a/services/oboeservice/AAudioServiceEndpointShared.cpp b/services/oboeservice/AAudioServiceEndpointShared.cpp
new file mode 100644
index 0000000..43d73b7
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointShared.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+
+#define LOG_TAG "AAudioServiceEndpointShared"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+
+#include "binding/AAudioServiceMessage.h"
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "core/AudioStreamBuilder.h"
+
+#include "AAudioServiceEndpointShared.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+using namespace android;
+using namespace aaudio;
+
+// This is the maximum size in frames. The effective size can be tuned smaller at runtime.
+#define DEFAULT_BUFFER_CAPACITY   (48 * 8)
+
+std::string AAudioServiceEndpointShared::dump() const {
+    std::stringstream result;
+
+    result << "  SHARED: sharing exclusive stream with handle = 0x"
+           << std::setfill('0') << std::setw(8)
+           << std::hex << mStreamInternal->getServiceHandle()
+           << std::dec << std::setfill(' ');
+    result << "\n";
+    result << "    Running Stream Count: " << mRunningStreamCount << "\n";
+
+    result << AAudioServiceEndpoint::dump();
+    return result.str();
+}
+
+// Share an AudioStreamInternal.
+aaudio_result_t AAudioServiceEndpointShared::open(const aaudio::AAudioStreamRequest &request) {
+    aaudio_result_t result = AAUDIO_OK;
+    const AAudioStreamConfiguration &configuration = request.getConstantConfiguration();
+
+    mRequestedDeviceId = configuration.getDeviceId();
+    setDirection(configuration.getDirection());
+
+    AudioStreamBuilder builder;
+    builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+    // Don't fall back to SHARED because that would cause recursion.
+    builder.setSharingModeMatchRequired(true);
+    builder.setDeviceId(mRequestedDeviceId);
+    builder.setFormat(configuration.getFormat());
+    builder.setSampleRate(configuration.getSampleRate());
+    builder.setSamplesPerFrame(configuration.getSamplesPerFrame());
+    builder.setDirection(configuration.getDirection());
+    builder.setBufferCapacity(DEFAULT_BUFFER_CAPACITY);
+
+    result = mStreamInternal->open(builder);
+
+    setSampleRate(mStreamInternal->getSampleRate());
+    setSamplesPerFrame(mStreamInternal->getSamplesPerFrame());
+    setDeviceId(mStreamInternal->getDeviceId());
+    mFramesPerBurst = mStreamInternal->getFramesPerBurst();
+
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::close() {
+    return getStreamInternal()->close();
+}
+
+// Glue between C and C++ callbacks.
+static void *aaudio_endpoint_thread_proc(void *context) {
+    AAudioServiceEndpointShared *endpoint = (AAudioServiceEndpointShared *) context;
+    if (endpoint != NULL) {
+        return endpoint->callbackLoop();
+    } else {
+        return NULL;
+    }
+}
+
+aaudio_result_t aaudio::AAudioServiceEndpointShared::startSharingThread_l() {
+    // Launch the callback loop thread.
+    int64_t periodNanos = getStreamInternal()->getFramesPerBurst()
+                          * AAUDIO_NANOS_PER_SECOND
+                          / getSampleRate();
+    mCallbackEnabled.store(true);
+    return getStreamInternal()->createThread(periodNanos, aaudio_endpoint_thread_proc, this);
+}
+
+aaudio_result_t aaudio::AAudioServiceEndpointShared::stopSharingThread() {
+    mCallbackEnabled.store(false);
+    aaudio_result_t result = getStreamInternal()->joinThread(NULL);
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::startStream(sp<AAudioServiceStreamBase> sharedStream,
+                                                         audio_port_handle_t *clientHandle) {
+    aaudio_result_t result = AAUDIO_OK;
+    if (++mRunningStreamCount == 1) {
+        // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+        std::lock_guard<std::mutex> lock(mLockStreams);
+        result = getStreamInternal()->requestStart();
+        startSharingThread_l();
+    }
+    if (result == AAUDIO_OK) {
+        ALOGD("AAudioServiceEndpointShared::startStream() use shared stream client.");
+        result = getStreamInternal()->startClient(sharedStream->getAudioClient(), clientHandle);
+    }
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpointShared::stopStream(sp<AAudioServiceStreamBase> sharedStream,
+                                                        audio_port_handle_t clientHandle) {
+    // Don't lock here because the disconnectRegisteredStreams also uses the lock.
+
+    // Ignore result.
+    (void) getStreamInternal()->stopClient(clientHandle);
+
+    if (--mRunningStreamCount == 0) { // atomic
+        stopSharingThread();
+        getStreamInternal()->requestStop();
+    }
+    return AAUDIO_OK;
+}
+
+
+// Get timestamp that was written by the real-time service thread, eg. mixer.
+aaudio_result_t AAudioServiceEndpointShared::getFreeRunningPosition(int64_t *positionFrames,
+                                                                  int64_t *timeNanos) {
+    if (mAtomicTimestamp.isValid()) {
+        Timestamp timestamp = mAtomicTimestamp.read();
+        *positionFrames = timestamp.getPosition();
+        *timeNanos = timestamp.getNanoseconds();
+        return AAUDIO_OK;
+    } else {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+}
+
+aaudio_result_t AAudioServiceEndpointShared::getTimestamp(int64_t *positionFrames,
+                                                          int64_t *timeNanos) {
+    return mStreamInternal->getTimestamp(CLOCK_MONOTONIC, positionFrames, timeNanos);
+}
diff --git a/services/oboeservice/AAudioServiceEndpointShared.h b/services/oboeservice/AAudioServiceEndpointShared.h
new file mode 100644
index 0000000..e3bd2c1
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpointShared.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef AAUDIO_SERVICE_ENDPOINT_SHARED_H
+#define AAUDIO_SERVICE_ENDPOINT_SHARED_H
+
+#include <atomic>
+#include <mutex>
+
+#include "AAudioServiceEndpoint.h"
+#include "client/AudioStreamInternal.h"
+#include "client/AudioStreamInternalPlay.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+/**
+ * This Service class corresponds to a Client stream that shares an MMAP device through a mixer
+ * or an input distributor.
+ */
+class AAudioServiceEndpointShared : public AAudioServiceEndpoint {
+
+public:
+
+    std::string dump() const override;
+
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
+
+    aaudio_result_t close() override;
+
+    aaudio_result_t startStream(android::sp<AAudioServiceStreamBase> stream,
+                                audio_port_handle_t *clientHandle) override;
+
+    aaudio_result_t stopStream(android::sp<AAudioServiceStreamBase> stream,
+                               audio_port_handle_t clientHandle) override;
+
+    aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    aaudio_result_t getTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
+
+    virtual void            *callbackLoop() = 0;
+
+    AudioStreamInternal *getStreamInternal() const {
+        return mStreamInternal;
+    };
+
+protected:
+
+    aaudio_result_t          startSharingThread_l();
+
+    aaudio_result_t          stopSharingThread();
+
+    // pointer to object statically allocated in subclasses
+    AudioStreamInternal     *mStreamInternal = nullptr;
+
+    std::atomic<bool>        mCallbackEnabled{false};
+
+    std::atomic<int>         mRunningStreamCount{0};
+};
+
+}
+
+#endif //AAUDIO_SERVICE_ENDPOINT_SHARED_H
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index e5f916c..2dc62a0 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -26,6 +26,9 @@
 #include "binding/AAudioServiceMessage.h"
 #include "utility/AudioClock.h"
 
+#include "AAudioEndpointManager.h"
+#include "AAudioService.h"
+#include "AAudioServiceEndpoint.h"
 #include "AAudioServiceStreamBase.h"
 #include "TimestampScheduler.h"
 
@@ -37,10 +40,11 @@
  * @return
  */
 
-AAudioServiceStreamBase::AAudioServiceStreamBase()
+AAudioServiceStreamBase::AAudioServiceStreamBase(AAudioService &audioService)
         : mUpMessageQueue(nullptr)
         , mAAudioThread()
-        , mAtomicTimestamp() {
+        , mAtomicTimestamp()
+        , mAudioService(audioService) {
     mMmapClient.clientUid = -1;
     mMmapClient.clientPid = -1;
     mMmapClient.packageName = String16("");
@@ -68,49 +72,112 @@
     result << std::setw(6) << mMmapClient.clientUid;
     result << std::setw(4) << (isRunning() ? "yes" : " no");
     result << std::setw(6) << mState;
-    result << std::setw(7) << mAudioFormat;
+    result << std::setw(7) << getFormat();
     result << std::setw(6) << mFramesPerBurst;
-    result << std::setw(5) << mSamplesPerFrame;
-    result << std::setw(9) << mCapacityInFrames;
+    result << std::setw(5) << getSamplesPerFrame();
+    result << std::setw(9) << getBufferCapacity();
 
     return result.str();
 }
 
 aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
-                     aaudio::AAudioStreamConfiguration &configurationOutput) {
+                                              aaudio_sharing_mode_t sharingMode) {
+    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+    aaudio_result_t result = AAUDIO_OK;
 
     mMmapClient.clientUid = request.getUserId();
     mMmapClient.clientPid = request.getProcessId();
-    mMmapClient.packageName.setTo(String16("")); // FIXME what should we do here?
+    mMmapClient.packageName.setTo(String16("")); // TODO What should we do here?
 
-    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
-    if (mUpMessageQueue != nullptr) {
-        return AAUDIO_ERROR_INVALID_STATE;
-    } else {
+    // Limit scope of lock to avoid recursive lock in close().
+    {
+        std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
+        if (mUpMessageQueue != nullptr) {
+            ALOGE("AAudioServiceStreamBase::open() called twice");
+            return AAUDIO_ERROR_INVALID_STATE;
+        }
+
         mUpMessageQueue = new SharedRingBuffer();
-        return mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), QUEUE_UP_CAPACITY_COMMANDS);
+        result = mUpMessageQueue->allocate(sizeof(AAudioServiceMessage),
+                                           QUEUE_UP_CAPACITY_COMMANDS);
+        if (result != AAUDIO_OK) {
+            goto error;
+        }
+
+        mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService,
+                                                         request,
+                                                         sharingMode);
+        if (mServiceEndpoint == nullptr) {
+            ALOGE("AAudioServiceStreamBase::open() openEndpoint() failed");
+            result = AAUDIO_ERROR_UNAVAILABLE;
+            goto error;
+        }
+        mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
+        copyFrom(*mServiceEndpoint);
     }
+    return result;
+
+error:
+    close();
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::close() {
-    if (mState != AAUDIO_STREAM_STATE_CLOSED) {
+    aaudio_result_t result = AAUDIO_OK;
+    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
+        return AAUDIO_OK;
+    }
+
+    stop();
+
+    if (mServiceEndpoint == nullptr) {
+        result = AAUDIO_ERROR_INVALID_STATE;
+    } else {
+        mServiceEndpoint->unregisterStream(this);
+        AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+        mEndpointManager.closeEndpoint(mServiceEndpoint);
+        mServiceEndpoint.clear();
+    }
+
+    {
+        std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
         stopTimestampThread();
-        std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
         delete mUpMessageQueue;
         mUpMessageQueue = nullptr;
-        mState = AAUDIO_STREAM_STATE_CLOSED;
     }
-    return AAUDIO_OK;
+
+    mState = AAUDIO_STREAM_STATE_CLOSED;
+    return result;
 }
 
+/**
+ * Start the flow of audio data.
+ *
+ * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
+ */
 aaudio_result_t AAudioServiceStreamBase::start() {
     if (isRunning()) {
         return AAUDIO_OK;
     }
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
-    mState = AAUDIO_STREAM_STATE_STARTED;
-    mThreadEnabled.store(true);
-    return mAAudioThread.start(this);
+
+    if (mServiceEndpoint == nullptr) {
+        ALOGE("AAudioServiceStreamBase::start() missing endpoint");
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    mClientHandle = AUDIO_PORT_HANDLE_NONE;
+    aaudio_result_t result = mServiceEndpoint->startStream(this, &mClientHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamBase::start() mServiceEndpoint returned %d", result);
+        disconnect();
+    } else {
+        if (result == AAUDIO_OK) {
+            sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+            mState = AAUDIO_STREAM_STATE_STARTED;
+            mThreadEnabled.store(true);
+            result = mAAudioThread.start(this);
+        }
+    }
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamBase::pause() {
@@ -118,6 +185,16 @@
     if (!isRunning()) {
         return result;
     }
+    if (mServiceEndpoint == nullptr) {
+        ALOGE("AAudioServiceStreamShared::pause() missing endpoint");
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
+        disconnect(); // TODO should we return or pause Base first?
+    }
+
     sendCurrentTimestamp();
     mThreadEnabled.store(false);
     result = mAAudioThread.stop();
@@ -135,13 +212,27 @@
     if (!isRunning()) {
         return result;
     }
-    // TODO wait for data to be played out
+
+    if (mServiceEndpoint == nullptr) {
+        ALOGE("AAudioServiceStreamShared::stop() missing endpoint");
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+
     sendCurrentTimestamp(); // warning - this calls a virtual function
     result = stopTimestampThread();
     if (result != AAUDIO_OK) {
         disconnect();
         return result;
     }
+
+    // TODO wait for data to be played out
+    result = mServiceEndpoint->stopStream(this, mClientHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
+        disconnect();
+        // TODO what to do with result here?
+    }
+
     sendServiceEvent(AAUDIO_SERVICE_EVENT_STOPPED);
     mState = AAUDIO_STREAM_STATE_STOPPED;
     return result;
@@ -157,6 +248,12 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::flush() {
+    if (mState != AAUDIO_STREAM_STATE_PAUSED) {
+        ALOGE("AAudioServiceStreamBase::flush() stream not paused, state = %s",
+              AAudio_convertStreamStateToText(mState));
+        return AAUDIO_ERROR_INVALID_STATE;
+    }
+    // Data will get flushed when the client receives the FLUSHED event.
     sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
     mState = AAUDIO_STREAM_STATE_FLUSHED;
     return AAUDIO_OK;
@@ -166,7 +263,7 @@
 void AAudioServiceStreamBase::run() {
     ALOGD("AAudioServiceStreamBase::run() entering ----------------");
     TimestampScheduler timestampScheduler;
-    timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
+    timestampScheduler.setBurstPeriod(mFramesPerBurst, getSampleRate());
     timestampScheduler.start(AudioClock::getNanoseconds());
     int64_t nextTime = timestampScheduler.nextAbsoluteTime();
     while(mThreadEnabled.load()) {
@@ -204,7 +301,7 @@
 }
 
 aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
-    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+    std::lock_guard<std::mutex> lock(mUpMessageQueueLock);
     if (mUpMessageQueue == nullptr) {
         ALOGE("writeUpMessageQueue(): mUpMessageQueue null! - stream not open");
         return AAUDIO_ERROR_NULL;
@@ -254,3 +351,7 @@
                                     parcelable.mUpMessageQueueParcelable);
     return getDownDataDescription(parcelable);
 }
+
+void AAudioServiceStreamBase::onVolumeChanged(float volume) {
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
+}
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 2f94614..301795d 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -32,22 +32,28 @@
 
 #include "SharedRingBuffer.h"
 #include "AAudioThread.h"
+#include "AAudioService.h"
 
 namespace aaudio {
 
+class AAudioServiceEndpoint;
+
 // We expect the queue to only have a few commands.
 // This should be way more than we need.
 #define QUEUE_UP_CAPACITY_COMMANDS (128)
 
 /**
- * Base class for a stream in the AAudio service.
+ * Each instance of AAudioServiceStreamBase corresponds to a client stream.
+ * It uses a subclass of AAudioServiceEndpoint to communicate with the underlying device or port.
  */
 class AAudioServiceStreamBase
     : public virtual android::RefBase
+    , public AAudioStreamParameters
     , public Runnable  {
 
 public:
-    AAudioServiceStreamBase();
+    AAudioServiceStreamBase(android::AAudioService &aAudioService);
+
     virtual ~AAudioServiceStreamBase();
 
     enum {
@@ -63,39 +69,53 @@
     /**
      * Open the device.
      */
-    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
-                                 aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
+    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request) = 0;
 
     virtual aaudio_result_t close();
 
     /**
-     * Start the flow of data.
+     * Start the flow of audio data.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
      */
     virtual aaudio_result_t start();
 
     /**
-     * Stop the flow of data such that start() can resume with loss of data.
-     */
+     * Stop the flow of data so that start() can resume without loss of data.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
+    */
     virtual aaudio_result_t pause();
 
     /**
-     * Stop the flow of data after data in buffer has played.
+     * Stop the flow of data after the currently queued data has finished playing.
+     *
+     * This is not guaranteed to be synchronous but it currently is.
+     * An AAUDIO_SERVICE_EVENT_STOPPED will be sent to the client when complete.
+     *
      */
     virtual aaudio_result_t stop();
 
     aaudio_result_t stopTimestampThread();
 
     /**
-     *  Discard any data held by the underlying HAL or Service.
+     * Discard any data held by the underlying HAL or Service.
+     *
+     * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
      */
     virtual aaudio_result_t flush();
 
+
     virtual aaudio_result_t startClient(const android::AudioClient& client __unused,
                                         audio_port_handle_t *clientHandle __unused) {
+        ALOGD("AAudioServiceStreamBase::startClient(%p, ...) AAUDIO_ERROR_UNAVAILABLE", &client);
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
     virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle __unused) {
+        ALOGD("AAudioServiceStreamBase::stopClient(%d) AAUDIO_ERROR_UNAVAILABLE", clientHandle);
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 
@@ -130,14 +150,14 @@
         return mFramesPerBurst;
     }
 
-    int32_t calculateBytesPerFrame() const {
-        return mSamplesPerFrame * AAudioConvert_formatToSizeInBytes(mAudioFormat);
-    }
-
     void run() override; // to implement Runnable
 
     void disconnect();
 
+    const android::AudioClient &getAudioClient() {
+        return mMmapClient;
+    }
+
     uid_t getOwnerUserId() const {
         return mMmapClient.clientUid;
     }
@@ -157,8 +177,16 @@
         return mState;
     }
 
+    void onVolumeChanged(float volume);
+
 protected:
 
+    /**
+     * Open the device.
+     */
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
+                         aaudio_sharing_mode_t sharingMode);
+
     void setState(aaudio_stream_state_t state) {
         mState = state;
     }
@@ -183,22 +211,21 @@
     pid_t                   mRegisteredClientThread = ILLEGAL_THREAD_ID;
 
     SharedRingBuffer*       mUpMessageQueue;
-    std::mutex              mLockUpMessageQueue;
+    std::mutex              mUpMessageQueueLock;
 
     AAudioThread            mAAudioThread;
     // This is used by one thread to tell another thread to exit. So it must be atomic.
-    std::atomic<bool>       mThreadEnabled;
+    std::atomic<bool>       mThreadEnabled{false};
 
-    aaudio_format_t         mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
     int32_t                 mFramesPerBurst = 0;
-    int32_t                 mSamplesPerFrame = AAUDIO_UNSPECIFIED;
-    int32_t                 mSampleRate = AAUDIO_UNSPECIFIED;
-    int32_t                 mCapacityInFrames = AAUDIO_UNSPECIFIED;
-    android::AudioClient    mMmapClient;
+    android::AudioClient    mMmapClient; // set in open, used in MMAP start()
     audio_port_handle_t     mClientHandle = AUDIO_PORT_HANDLE_NONE;
 
     SimpleDoubleBuffer<Timestamp>  mAtomicTimestamp;
 
+    android::AAudioService &mAudioService;
+    android::sp<AAudioServiceEndpoint> mServiceEndpoint;
+
 private:
     aaudio_handle_t         mHandle = -1;
 };
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
index 68dcaff..43595a4 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.cpp
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -19,41 +19,33 @@
 #include <utils/Log.h>
 
 #include <atomic>
+#include <iomanip>
+#include <iostream>
 #include <stdint.h>
 
 #include <utils/String16.h>
 #include <media/nbaio/AudioStreamOutSink.h>
 #include <media/MmapStreamInterface.h>
 
+#include "binding/AudioEndpointParcelable.h"
+#include "utility/AAudioUtilities.h"
+
+#include "AAudioServiceEndpointMMAP.h"
 #include "AAudioServiceStreamBase.h"
 #include "AAudioServiceStreamMMAP.h"
-#include "binding/AudioEndpointParcelable.h"
 #include "SharedMemoryProxy.h"
-#include "utility/AAudioUtilities.h"
 
 using android::base::unique_fd;
 using namespace android;
 using namespace aaudio;
 
-#define AAUDIO_BUFFER_CAPACITY_MIN    4 * 512
-#define AAUDIO_SAMPLE_RATE_DEFAULT    48000
-
-// This is an estimate of the time difference between the HW and the MMAP time.
-// TODO Get presentation timestamps from the HAL instead of using these estimates.
-#define OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS  (3 * AAUDIO_NANOS_PER_MILLISECOND)
-#define INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS   (-1 * AAUDIO_NANOS_PER_MILLISECOND)
-
 /**
  * Service Stream that uses an MMAP buffer.
  */
 
-AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(const android::AudioClient& serviceClient,
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP(android::AAudioService &aAudioService,
                                                  bool inService)
-        : AAudioServiceStreamBase()
-        , mMmapStreamCallback(new MyMmapStreamCallback(*this))
-        , mPreviousFrameCounter(0)
-        , mMmapStream(nullptr)
-        , mServiceClient(serviceClient)
+        : AAudioServiceStreamBase(aAudioService)
         , mInService(inService) {
 }
 
@@ -61,170 +53,32 @@
     if (mState == AAUDIO_STREAM_STATE_CLOSED) {
         return AAUDIO_OK;
     }
+
     stop();
-    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);
-    }
 
     return AAudioServiceStreamBase::close();
 }
 
 // Open stream on HAL and pass information about the shared memory buffer back to the client.
-aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request,
-                                       aaudio::AAudioStreamConfiguration &configurationOutput) {
-    const audio_attributes_t attributes = {
-        .content_type = AUDIO_CONTENT_TYPE_MUSIC,
-        .usage = AUDIO_USAGE_MEDIA,
-        .source = AUDIO_SOURCE_VOICE_RECOGNITION,
-        .flags = AUDIO_FLAG_LOW_LATENCY,
-        .tags = ""
-    };
-    audio_config_base_t config;
+aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request) {
 
-    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+    sp<AAudioServiceStreamMMAP> keep(this);
+
+    aaudio_result_t result = AAudioServiceStreamBase::open(request,
+                                                           AAUDIO_SHARING_MODE_EXCLUSIVE);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamBase open returned %d", result);
         return result;
     }
 
-    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
-    audio_port_handle_t deviceId = configurationInput.getDeviceId();
-    aaudio_direction_t direction = request.getDirection();
-
-    // Fill in config
-    aaudio_format_t aaudioFormat = configurationInput.getFormat();
-    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
-        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+    result = mServiceEndpoint->registerStream(keep);
+    if (result != AAUDIO_OK) {
+        goto error;
     }
-    config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
-
-    int32_t aaudioSampleRate = configurationInput.getSampleRate();
-    if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
-        aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
-    }
-    config.sample_rate = aaudioSampleRate;
-
-    int32_t aaudioSamplesPerFrame = configurationInput.getSamplesPerFrame();
-
-    if (direction == AAUDIO_DIRECTION_OUTPUT) {
-        config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
-                            ? AUDIO_CHANNEL_OUT_STEREO
-                            : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
-        mHardwareTimeOffsetNanos = OUTPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at DAC later
-
-    } else if (direction == AAUDIO_DIRECTION_INPUT) {
-        config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
-                            ? AUDIO_CHANNEL_IN_STEREO
-                            : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
-        mHardwareTimeOffsetNanos = INPUT_ESTIMATED_HARDWARE_OFFSET_NANOS; // frames at ADC earlier
-
-    } else {
-        ALOGE("openMmapStream - invalid direction = %d", direction);
-        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
-    }
-
-    MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT)
-        ? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT;
-
-    // Open HAL stream.
-    status_t status = MmapStreamInterface::openMmapStream(streamDirection,
-                                                          &attributes,
-                                                          &config,
-                                                          mMmapClient,
-                                                          &deviceId,
-                                                          mMmapStreamCallback,
-                                                          mMmapStream,
-                                                          &mPortHandle);
-    if (status != OK) {
-        ALOGE("openMmapStream returned status %d", status);
-        return AAUDIO_ERROR_UNAVAILABLE;
-    }
-
-    if (deviceId == AAUDIO_UNSPECIFIED) {
-        ALOGW("AAudioServiceStreamMMAP::open() - openMmapStream() failed to set deviceId");
-    }
-
-    // Create MMAP/NOIRQ buffer.
-    int32_t minSizeFrames = configurationInput.getBufferCapacity();
-    if (minSizeFrames <= 0) { // zero will get rejected
-        minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
-    }
-    status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
-    if (status != OK) {
-        ALOGE("AAudioServiceStreamMMAP::open() - createMmapBuffer() returned status %d",
-              status);
-        return AAUDIO_ERROR_UNAVAILABLE;
-    } else {
-        ALOGD("createMmapBuffer status = %d, buffer_size = %d, burst_size %d"
-                ", Sharable FD: %s",
-              status,
-              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 != mServiceClient.clientUid) &&
-                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.
-    mSamplesPerFrame = (direction == AAUDIO_DIRECTION_OUTPUT)
-                           ? audio_channel_count_from_out_mask(config.channel_mask)
-                           : audio_channel_count_from_in_mask(config.channel_mask);
-
-    // AAudio creates a copy of this FD and retains ownership of the copy.
-    // Assume that AudioFlinger will close the original shared_memory_fd.
-    mAudioDataFileDescriptor.reset(dup(mMmapBufferinfo.shared_memory_fd));
-    if (mAudioDataFileDescriptor.get() == -1) {
-        ALOGE("AAudioServiceStreamMMAP::open() - could not dup shared_memory_fd");
-        return AAUDIO_ERROR_INTERNAL; // TODO review
-    }
-    mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
-    mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
-    mSampleRate = config.sample_rate;
-
-    // Scale up the burst size to meet the minimum equivalent in microseconds.
-    // This is to avoid waking the CPU too often when the HW burst is very small
-    // or at high sample rates.
-    int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
-    int32_t burstMicros = 0;
-    do {
-        if (burstMicros > 0) {  // skip first loop
-            mFramesPerBurst *= 2;
-        }
-        burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / mSampleRate;
-    } while (burstMicros < burstMinMicros);
-
-    ALOGD("AAudioServiceStreamMMAP::open() original burst = %d, minMicros = %d, final burst = %d\n",
-          mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
-
-    ALOGD("AAudioServiceStreamMMAP::open() actual rate = %d, channels = %d, deviceId = %d\n",
-          mSampleRate, mSamplesPerFrame, deviceId);
-
-    // Fill in AAudioStreamConfiguration
-    configurationOutput.setSampleRate(mSampleRate);
-    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
-    configurationOutput.setFormat(mAudioFormat);
-    configurationOutput.setDeviceId(deviceId);
 
     setState(AAUDIO_STREAM_STATE_OPEN);
+
+error:
     return AAUDIO_OK;
 }
 
@@ -235,18 +89,10 @@
     if (isRunning()) {
         return AAUDIO_OK;
     }
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result;
-    status_t status = mMmapStream->start(mServiceClient, &mPortHandle);
-    if (status != OK) {
-        ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
-        disconnect();
-        result = AAudioConvert_androidToAAudioResult(status);
-    } else {
-        result = AAudioServiceStreamBase::start();
-        if (!mInService && result == AAUDIO_OK) {
-            startClient(mMmapClient, &mClientHandle);
-        }
+
+    aaudio_result_t result = AAudioServiceStreamBase::start();
+    if (!mInService && result == AAUDIO_OK) {
+        startClient(mMmapClient, &mClientHandle);
     }
     return result;
 }
@@ -258,70 +104,50 @@
     if (!isRunning()) {
         return AAUDIO_OK;
     }
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result1 = AAudioServiceStreamBase::pause();
+    aaudio_result_t result = AAudioServiceStreamBase::pause();
+    // TODO put before base::pause()?
     if (!mInService) {
         stopClient(mClientHandle);
     }
-    status_t status = mMmapStream->stop(mPortHandle);
-    mFramesRead.reset32();
-    return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stop() {
     if (!isRunning()) {
         return AAUDIO_OK;
     }
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result1 = AAudioServiceStreamBase::stop();
+    aaudio_result_t result = AAudioServiceStreamBase::stop();
+    // TODO put before base::stop()?
     if (!mInService) {
         stopClient(mClientHandle);
     }
-    aaudio_result_t status = mMmapStream->stop(mPortHandle);
-    mFramesRead.reset32();
-    return (result1 != AAUDIO_OK) ? result1 :  AAudioConvert_androidToAAudioResult(status);
-}
-
-/**
- *  Discard any data held by the underlying HAL or Service.
- */
-aaudio_result_t AAudioServiceStreamMMAP::flush() {
-    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
-    // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
-    return AAudioServiceStreamBase::flush();;
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
-                                                     audio_port_handle_t *clientHandle) {
-    return AAudioConvert_androidToAAudioResult(mMmapStream->start(client, clientHandle));
+                                                       audio_port_handle_t *clientHandle) {
+    aaudio_result_t result = mServiceEndpoint->startClient(client, clientHandle);
+    return result;
 }
 
 aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
-    return AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
+    aaudio_result_t result = mServiceEndpoint->stopClient(clientHandle);
+    return result;
 }
 
 // Get free-running DSP or DMA hardware position from the HAL.
 aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
-                                                                int64_t *timeNanos) {
-    struct audio_mmap_position position;
-    if (mMmapStream == nullptr) {
-        disconnect();
-        return AAUDIO_ERROR_NULL;
-    }
-    status_t status = mMmapStream->getMmapPosition(&position);
-    aaudio_result_t result = AAudioConvert_androidToAAudioResult(status);
-    if (result == AAUDIO_ERROR_UNAVAILABLE) {
-        ALOGW("sendCurrentTimestamp(): getMmapPosition() has no position data yet");
-    } else if (result != AAUDIO_OK) {
-        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned status %d", status);
-        disconnect();
-    } else {
-        mFramesRead.update32(position.position_frames);
-
-        Timestamp timestamp(mFramesRead.get(), position.time_nanoseconds);
+                                                                  int64_t *timeNanos) {
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    aaudio_result_t result = serviceEndpointMMAP->getFreeRunningPosition(positionFrames, timeNanos);
+    if (result == AAUDIO_OK) {
+        Timestamp timestamp(*positionFrames, *timeNanos);
         mAtomicTimestamp.write(timestamp);
         *positionFrames = timestamp.getPosition();
         *timeNanos = timestamp.getNanoseconds();
+    } else if (result != AAUDIO_ERROR_UNAVAILABLE) {
+        disconnect();
     }
     return result;
 }
@@ -329,51 +155,25 @@
 // Get timestamp that was written by getFreeRunningPosition()
 aaudio_result_t AAudioServiceStreamMMAP::getHardwareTimestamp(int64_t *positionFrames,
                                                                 int64_t *timeNanos) {
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
     // TODO Get presentation timestamp from the HAL
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
         *positionFrames = timestamp.getPosition();
-        *timeNanos = timestamp.getNanoseconds() + mHardwareTimeOffsetNanos;
+        *timeNanos = timestamp.getNanoseconds() + serviceEndpointMMAP->getHardwareTimeOffsetNanos();
         return AAUDIO_OK;
     } else {
         return AAUDIO_ERROR_UNAVAILABLE;
     }
 }
 
-void AAudioServiceStreamMMAP::onTearDown() {
-    ALOGD("AAudioServiceStreamMMAP::onTearDown() called");
-    disconnect();
-};
-
-void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels,
-                     android::Vector<float> values) {
-    // TODO do we really need a different volume for each channel?
-    float volume = values[0];
-    ALOGD("AAudioServiceStreamMMAP::onVolumeChanged() volume[0] = %f", volume);
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
-};
-
-void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
-    ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d",
-          deviceId, mDeviceId);
-    if (mDeviceId != AUDIO_PORT_HANDLE_NONE  && mDeviceId != deviceId) {
-        disconnect();
-    }
-    mDeviceId = deviceId;
-};
-
 /**
  * Get an immutable description of the data queue from the HAL.
  */
 aaudio_result_t AAudioServiceStreamMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
 {
-    // Gather information on the data queue based on HAL info.
-    int32_t bytesPerFrame = calculateBytesPerFrame();
-    int32_t capacityInBytes = mCapacityInFrames * bytesPerFrame;
-    int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
-    parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
-    parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
-    parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
-    parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames);
-    return AAUDIO_OK;
+    sp<AAudioServiceEndpointMMAP> serviceEndpointMMAP{
+            static_cast<AAudioServiceEndpointMMAP *>(mServiceEndpoint.get())};
+    return serviceEndpointMMAP->getDownDataDescription(parcelable);
 }
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
index e631fd3..bf0aab3 100644
--- a/services/oboeservice/AAudioServiceStreamMMAP.h
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -37,19 +37,20 @@
 
 namespace aaudio {
 
-    /**
-     * Manage one memory mapped buffer that originated from a HAL.
-     */
-class AAudioServiceStreamMMAP
-    : public AAudioServiceStreamBase
-    , public android::MmapStreamCallback {
+
+/**
+ * These corresponds to an EXCLUSIVE mode MMAP client stream.
+ * It has exclusive use of one AAudioServiceEndpointMMAP to communicate with the underlying
+ * device or port.
+ */
+class AAudioServiceStreamMMAP : public AAudioServiceStreamBase {
 
 public:
-    AAudioServiceStreamMMAP(const android::AudioClient& serviceClient, bool inService);
+    AAudioServiceStreamMMAP(android::AAudioService &aAudioService,
+                            bool inService);
     virtual ~AAudioServiceStreamMMAP() = default;
 
-    aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
-                                 aaudio::AAudioStreamConfiguration &configurationOutput) override;
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     /**
      * Start the flow of audio data.
@@ -69,83 +70,28 @@
 
     aaudio_result_t stop() override;
 
-    /**
-     *  Discard any data held by the underlying HAL or Service.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
-     */
-    aaudio_result_t flush() override;
+    aaudio_result_t startClient(const android::AudioClient& client,
+                                audio_port_handle_t *clientHandle) override;
+
+    aaudio_result_t stopClient(audio_port_handle_t clientHandle) override;
 
     aaudio_result_t close() override;
 
-    virtual aaudio_result_t startClient(const android::AudioClient& client,
-                                        audio_port_handle_t *clientHandle);
-
-    virtual aaudio_result_t stopClient(audio_port_handle_t clientHandle);
-
     /**
      * Send a MMAP/NOIRQ buffer timestamp to the client.
      */
-    aaudio_result_t sendCurrentTimestamp();
-
-    // -------------- Callback functions ---------------------
-    void onTearDown() override;
-
-    void onVolumeChanged(audio_channel_mask_t channels,
-                         android::Vector<float> values) override;
-
-    void onRoutingChanged(audio_port_handle_t deviceId) override;
 
 protected:
 
     aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
 
     aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
-    virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames,
-                                                 int64_t *timeNanos) override;
+
+    aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
 
 private:
-    // This proxy class was needed to prevent a crash in AudioFlinger
-    // when the stream was closed.
-    class MyMmapStreamCallback : public android::MmapStreamCallback {
-    public:
-        explicit MyMmapStreamCallback(android::MmapStreamCallback &serviceCallback)
-            : mServiceCallback(serviceCallback){}
-        virtual ~MyMmapStreamCallback() = default;
 
-        void onTearDown() override {
-            mServiceCallback.onTearDown();
-        };
-
-        void onVolumeChanged(audio_channel_mask_t channels, android::Vector<float> values) override
-        {
-            mServiceCallback.onVolumeChanged(channels, values);
-        };
-
-        void onRoutingChanged(audio_port_handle_t deviceId) override {
-            mServiceCallback.onRoutingChanged(deviceId);
-        };
-
-    private:
-        android::MmapStreamCallback &mServiceCallback;
-    };
-
-    android::sp<MyMmapStreamCallback>   mMmapStreamCallback;
-    MonotonicCounter                    mFramesWritten;
-    MonotonicCounter                    mFramesRead;
-    int32_t                             mPreviousFrameCounter = 0;   // from HAL
-    int64_t                             mHardwareTimeOffsetNanos = 0; // TODO get from HAL
-
-
-    // Interface to the AudioFlinger MMAP support.
-    android::sp<android::MmapStreamInterface> mMmapStream;
-    struct audio_mmap_buffer_info             mMmapBufferinfo;
-    audio_port_handle_t                       mPortHandle = AUDIO_PORT_HANDLE_NONE;
-    audio_port_handle_t                       mDeviceId = AUDIO_PORT_HANDLE_NONE;
-    android::AudioClient                      mServiceClient;
-    bool                                      mInService = false;
-    android::base::unique_fd                  mAudioDataFileDescriptor;
+    bool                     mInService = false;
 };
 
 } // namespace aaudio
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
index 57990ce..834f39f 100644
--- a/services/oboeservice/AAudioServiceStreamShared.cpp
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -42,10 +42,9 @@
 #define MAX_FRAMES_PER_BUFFER       (32 * 1024)
 
 AAudioServiceStreamShared::AAudioServiceStreamShared(AAudioService &audioService)
-    : mAudioService(audioService)
+    : AAudioServiceStreamBase(audioService)
     , mTimestampPositionOffset(0)
-    , mXRunCount(0)
-    {
+    , mXRunCount(0) {
 }
 
 std::string AAudioServiceStreamShared::dumpHeader() {
@@ -57,6 +56,7 @@
 
 std::string AAudioServiceStreamShared::dump() const {
     std::stringstream result;
+
     result << AAudioServiceStreamBase::dump();
 
     auto fifo = mAudioDataQueue->getFifoBuffer();
@@ -116,87 +116,69 @@
     return capacityInFrames;
 }
 
-aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request,
-                     aaudio::AAudioStreamConfiguration &configurationOutput)  {
+aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request)  {
 
     sp<AAudioServiceStreamShared> keep(this);
 
-    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+    aaudio_result_t result = AAudioServiceStreamBase::open(request, AAUDIO_SHARING_MODE_SHARED);
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamBase open() returned %d", result);
         return result;
     }
 
     const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
-    aaudio_direction_t direction = request.getDirection();
 
-    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-    mServiceEndpoint = mEndpointManager.openEndpoint(mAudioService, configurationInput, direction);
-    if (mServiceEndpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::open() mServiceEndPoint = %p", mServiceEndpoint);
-        return AAUDIO_ERROR_UNAVAILABLE;
-    }
 
     // Is the request compatible with the shared endpoint?
-    mAudioFormat = configurationInput.getFormat();
-    if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
-        mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
-    } else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
-        ALOGE("AAudioServiceStreamShared::open() mAudioFormat = %d, need FLOAT", mAudioFormat);
+    setFormat(configurationInput.getFormat());
+    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
+        setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    } else if (getFormat() != AAUDIO_FORMAT_PCM_FLOAT) {
+        ALOGE("AAudioServiceStreamShared::open() mAudioFormat = %d, need FLOAT", getFormat());
         result = AAUDIO_ERROR_INVALID_FORMAT;
         goto error;
     }
 
-    mSampleRate = configurationInput.getSampleRate();
-    if (mSampleRate == AAUDIO_UNSPECIFIED) {
-        mSampleRate = mServiceEndpoint->getSampleRate();
-    } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+    setSampleRate(configurationInput.getSampleRate());
+    if (getSampleRate() == AAUDIO_UNSPECIFIED) {
+        setSampleRate(mServiceEndpoint->getSampleRate());
+    } else if (getSampleRate() != mServiceEndpoint->getSampleRate()) {
         ALOGE("AAudioServiceStreamShared::open() mSampleRate = %d, need %d",
-              mSampleRate, mServiceEndpoint->getSampleRate());
+              getSampleRate(), mServiceEndpoint->getSampleRate());
         result = AAUDIO_ERROR_INVALID_RATE;
         goto error;
     }
 
-    mSamplesPerFrame = configurationInput.getSamplesPerFrame();
-    if (mSamplesPerFrame == AAUDIO_UNSPECIFIED) {
-        mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
-    } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+    setSamplesPerFrame(configurationInput.getSamplesPerFrame());
+    if (getSamplesPerFrame() == AAUDIO_UNSPECIFIED) {
+        setSamplesPerFrame(mServiceEndpoint->getSamplesPerFrame());
+    } else if (getSamplesPerFrame() != mServiceEndpoint->getSamplesPerFrame()) {
         ALOGE("AAudioServiceStreamShared::open() mSamplesPerFrame = %d, need %d",
-              mSamplesPerFrame, mServiceEndpoint->getSamplesPerFrame());
+              getSamplesPerFrame(), mServiceEndpoint->getSamplesPerFrame());
         result = AAUDIO_ERROR_OUT_OF_RANGE;
         goto error;
     }
 
-    mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
-    ALOGD("AAudioServiceStreamShared::open() mSampleRate = %d, mFramesPerBurst = %d",
-          mSampleRate, mFramesPerBurst);
-
-    mCapacityInFrames = calculateBufferCapacity(configurationInput.getBufferCapacity(),
-                                     mFramesPerBurst);
-    if (mCapacityInFrames < 0) {
-        result = mCapacityInFrames; // negative error code
-        mCapacityInFrames = 0;
+    setBufferCapacity(calculateBufferCapacity(configurationInput.getBufferCapacity(),
+                                     mFramesPerBurst));
+    if (getBufferCapacity() < 0) {
+        result = getBufferCapacity(); // negative error code
+        setBufferCapacity(0);
         goto error;
     }
 
     // Create audio data shared memory buffer for client.
     mAudioDataQueue = new SharedRingBuffer();
-    result = mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
+    result = mAudioDataQueue->allocate(calculateBytesPerFrame(), getBufferCapacity());
     if (result != AAUDIO_OK) {
         ALOGE("AAudioServiceStreamShared::open() could not allocate FIFO with %d frames",
-              mCapacityInFrames);
+              getBufferCapacity());
         result = AAUDIO_ERROR_NO_MEMORY;
         goto error;
     }
 
     ALOGD("AAudioServiceStreamShared::open() actual rate = %d, channels = %d, deviceId = %d",
-          mSampleRate, mSamplesPerFrame, mServiceEndpoint->getDeviceId());
-
-    // Fill in configuration for client.
-    configurationOutput.setSampleRate(mSampleRate);
-    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
-    configurationOutput.setFormat(mAudioFormat);
-    configurationOutput.setDeviceId(mServiceEndpoint->getDeviceId());
+          getSampleRate(), getSamplesPerFrame(), mServiceEndpoint->getDeviceId());
 
     result = mServiceEndpoint->registerStream(keep);
     if (result != AAUDIO_OK) {
@@ -211,118 +193,14 @@
     return result;
 }
 
-/**
- * Start the flow of audio data.
- *
- * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
- */
-aaudio_result_t AAudioServiceStreamShared::start()  {
-    if (isRunning()) {
-        return AAUDIO_OK;
-    }
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::start() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    // For output streams, this will add the stream to the mixer.
-    aaudio_result_t result = endpoint->startStream(this);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
-        disconnect();
-    } else {
-        result = endpoint->getStreamInternal()->startClient(mMmapClient, &mClientHandle);
-        if (result == AAUDIO_OK) {
-            result = AAudioServiceStreamBase::start();
-        }
-    }
-    return result;
-}
-
-/**
- * Stop the flow of data so that start() can resume without loss of data.
- *
- * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
-*/
-aaudio_result_t AAudioServiceStreamShared::pause()  {
-    if (!isRunning()) {
-        return AAUDIO_OK;
-    }
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::pause() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    endpoint->getStreamInternal()->stopClient(mClientHandle);
-    aaudio_result_t result = endpoint->stopStream(this);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::pause() mServiceEndpoint returned %d", result);
-        disconnect(); // TODO should we return or pause Base first?
-    }
-    return AAudioServiceStreamBase::pause();
-}
-
-aaudio_result_t AAudioServiceStreamShared::stop()  {
-    if (!isRunning()) {
-        return AAUDIO_OK;
-    }
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::stop() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    endpoint->getStreamInternal()->stopClient(mClientHandle);
-    aaudio_result_t result = endpoint->stopStream(this);
-    if (result != AAUDIO_OK) {
-        ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
-        disconnect();
-    }
-    return AAudioServiceStreamBase::stop();
-}
-
-/**
- *  Discard any data held by the underlying HAL or Service.
- *
- * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
- */
-aaudio_result_t AAudioServiceStreamShared::flush()  {
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        ALOGE("AAudioServiceStreamShared::flush() missing endpoint");
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    if (mState != AAUDIO_STREAM_STATE_PAUSED) {
-         ALOGE("AAudioServiceStreamShared::flush() stream not paused, state = %s",
-            AAudio_convertStreamStateToText(mState));
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-    // Data will get flushed when the client receives the FLUSHED event.
-    return AAudioServiceStreamBase::flush();
-}
 
 aaudio_result_t AAudioServiceStreamShared::close()  {
-    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
-        return AAUDIO_OK;
-    }
+    aaudio_result_t result = AAudioServiceStreamBase::close();
 
-    stop();
+    delete mAudioDataQueue;
+    mAudioDataQueue = nullptr;
 
-    AAudioServiceEndpoint *endpoint = mServiceEndpoint;
-    if (endpoint == nullptr) {
-        return AAUDIO_ERROR_INVALID_STATE;
-    }
-
-    endpoint->unregisterStream(this);
-
-    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
-    mEndpointManager.closeEndpoint(endpoint);
-    mServiceEndpoint = nullptr;
-
-    if (mAudioDataQueue != nullptr) {
-        delete mAudioDataQueue;
-        mAudioDataQueue = nullptr;
-    }
-    return AAudioServiceStreamBase::close();
+    return result;
 }
 
 /**
@@ -341,9 +219,10 @@
     mAtomicTimestamp.write(timestamp);
 }
 
-// Get timestamp that was written by the real-time service thread, eg. mixer.
+// Get timestamp that was written by mixer or distributor.
 aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
-                                                                int64_t *timeNanos) {
+                                                                  int64_t *timeNanos) {
+    // TODO Get presentation timestamp from the HAL
     if (mAtomicTimestamp.isValid()) {
         Timestamp timestamp = mAtomicTimestamp.read();
         *positionFrames = timestamp.getPosition();
@@ -356,7 +235,7 @@
 
 // Get timestamp from lower level service.
 aaudio_result_t AAudioServiceStreamShared::getHardwareTimestamp(int64_t *positionFrames,
-                                                              int64_t *timeNanos) {
+                                                                int64_t *timeNanos) {
 
     aaudio_result_t result = mServiceEndpoint->getTimestamp(positionFrames, timeNanos);
     if (result == AAUDIO_OK) {
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
index 36a56b8..bc86dcc 100644
--- a/services/oboeservice/AAudioServiceStreamShared.h
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -50,37 +50,7 @@
 
     std::string dump() const override;
 
-    aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
-                         aaudio::AAudioStreamConfiguration &configurationOutput) override;
-
-    /**
-     * Start the flow of audio data.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
-     */
-    aaudio_result_t start() override;
-
-    /**
-     * Stop the flow of data so that start() can resume without loss of data.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_PAUSED will be sent to the client when complete.
-    */
-    aaudio_result_t pause() override;
-
-    /**
-     * Stop the flow of data after data in buffer has played.
-     */
-    aaudio_result_t stop() override;
-
-    /**
-     *  Discard any data held by the underlying HAL or Service.
-     *
-     * This is not guaranteed to be synchronous but it currently is.
-     * An AAUDIO_SERVICE_EVENT_FLUSHED will be sent to the client when complete.
-     */
-    aaudio_result_t flush() override;
+    aaudio_result_t open(const aaudio::AAudioStreamRequest &request) override;
 
     aaudio_result_t close() override;
 
@@ -109,8 +79,7 @@
 
     aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
 
-    virtual aaudio_result_t getHardwareTimestamp(int64_t *positionFrames,
-                                                 int64_t *timeNanos) override;
+    aaudio_result_t getHardwareTimestamp(int64_t *positionFrames, int64_t *timeNanos) override;
 
     /**
      * @param requestedCapacityFrames
@@ -121,12 +90,11 @@
                                             int32_t framesPerBurst);
 
 private:
-    android::AAudioService  &mAudioService;
-    AAudioServiceEndpoint   *mServiceEndpoint = nullptr;
     SharedRingBuffer        *mAudioDataQueue = nullptr;
 
     std::atomic<int64_t>     mTimestampPositionOffset;
     std::atomic<int32_t>     mXRunCount;
+
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index a896a7a..1b74ad3 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -31,7 +31,9 @@
     AAudioService.cpp \
     AAudioServiceEndpoint.cpp \
     AAudioServiceEndpointCapture.cpp \
+    AAudioServiceEndpointMMAP.cpp \
     AAudioServiceEndpointPlay.cpp \
+    AAudioServiceEndpointShared.cpp \
     AAudioServiceStreamBase.cpp \
     AAudioServiceStreamMMAP.cpp \
     AAudioServiceStreamShared.cpp \