AAudioService: integrated with audioserver

Call the MmapStreamInterface from AudioFlinger instead of the FakeHAL.
Fix sending timestamps from the thread.
Add shared mode in service.

Bug: 35260844
Test: CTS test_aaudio.cpp
Change-Id: I44c7e4ecae4ce205611b6b73a72e0ae8a5b243e5
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 4b2e643..959baf7 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -2133,7 +2133,7 @@
                 return BAD_VALUE;
             }
             mMmapThreads.removeItem(output);
-            ALOGV("closing mmapThread %p", mmapThread.get());
+            ALOGD("closing mmapThread %p", mmapThread.get());
         }
         const sp<AudioIoDescriptor> ioDesc = new AudioIoDescriptor();
         ioDesc->mIoHandle = output;
@@ -2148,7 +2148,7 @@
             closeOutputFinish(playbackThread);
         }
     } else if (mmapThread != 0) {
-        ALOGV("mmapThread exit()");
+        ALOGD("mmapThread exit()");
         mmapThread->exit();
         AudioStreamOut *out = mmapThread->clearOutput();
         ALOG_ASSERT(out != NULL, "out shouldn't be NULL");
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 6a75bb0..e8cde88 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7592,7 +7592,11 @@
 
     if (mActiveTracks.size() == 0) {
         // for the first track, reuse portId and session allocated when the stream was opened
-        mHalStream->start();
+        ret = mHalStream->start();
+        if (ret != NO_ERROR) {
+            ALOGE("%s: error mHalStream->start() = %d for first track", __FUNCTION__, ret);
+            return ret;
+        }
         portId = mPortId;
         sessionId = mSessionId;
         mStandby = false;
@@ -7647,6 +7651,7 @@
 
     // abort if start is rejected by audio policy manager
     if (ret != NO_ERROR) {
+        ALOGE("%s: error start rejected by AudioPolicyManager = %d", __FUNCTION__, ret);
         if (mActiveTracks.size() != 0) {
             if (isOutput()) {
                 AudioSystem::releaseOutput(mId, streamType(), sessionId);
@@ -7942,15 +7947,17 @@
     if (isOutput() && mPrevOutDevice != mOutDevice) {
         mPrevOutDevice = type;
         sendIoConfigEvent_l(AUDIO_OUTPUT_CONFIG_CHANGED);
-        if (mCallback != 0) {
-            mCallback->onRoutingChanged(deviceId);
+        sp<MmapStreamCallback> callback = mCallback.promote();
+        if (callback != 0) {
+            callback->onRoutingChanged(deviceId);
         }
     }
     if (!isOutput() && mPrevInDevice != mInDevice) {
         mPrevInDevice = type;
         sendIoConfigEvent_l(AUDIO_INPUT_CONFIG_CHANGED);
-        if (mCallback != 0) {
-            mCallback->onRoutingChanged(deviceId);
+        sp<MmapStreamCallback> callback = mCallback.promote();
+        if (callback != 0) {
+            callback->onRoutingChanged(deviceId);
         }
     }
     return status;
@@ -8065,8 +8072,9 @@
 
 void AudioFlinger::MmapThread::threadLoop_exit()
 {
-    if (mCallback != 0) {
-        mCallback->onTearDown();
+    sp<MmapStreamCallback> callback = mCallback.promote();
+    if (callback != 0) {
+        callback->onTearDown();
     }
 }
 
@@ -8114,8 +8122,9 @@
 {
     for (const sp<MmapTrack> &track : mActiveTracks) {
         if (track->isInvalid()) {
-            if (mCallback != 0) {
-                mCallback->onTearDown();
+            sp<MmapStreamCallback> callback = mCallback.promote();
+            if (callback != 0) {
+                callback->onTearDown();
             }
             break;
         }
@@ -8290,7 +8299,8 @@
 
         mOutput->stream->setVolume(volume, volume);
 
-        if (mCallback != 0) {
+        sp<MmapStreamCallback> callback = mCallback.promote();
+        if (callback != 0) {
             int channelCount;
             if (isOutput()) {
                 channelCount = audio_channel_count_from_out_mask(mChannelMask);
@@ -8301,7 +8311,7 @@
             for (int i = 0; i < channelCount; i++) {
                 values.add(volume);
             }
-            mCallback->onVolumeChanged(mChannelMask, values);
+            callback->onVolumeChanged(mChannelMask, values);
         }
     }
 }
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index cc66cad..7469710 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1528,7 +1528,7 @@
                 audio_session_t         mSessionId;
                 audio_port_handle_t     mPortId;
 
-                sp<MmapStreamCallback>  mCallback;
+                wp<MmapStreamCallback>  mCallback;
                 sp<StreamHalInterface>  mHalStream;
                 sp<DeviceHalInterface>  mHalDevice;
                 AudioHwDevice* const    mAudioHwDev;
diff --git a/services/oboeservice/AAudioEndpointManager.cpp b/services/oboeservice/AAudioEndpointManager.cpp
new file mode 100644
index 0000000..84fa227
--- /dev/null
+++ b/services/oboeservice/AAudioEndpointManager.cpp
@@ -0,0 +1,87 @@
+/*
+ * 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.
+ */
+
+#include <assert.h>
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "AAudioEndpointManager.h"
+#include "AAudioServiceEndpoint.h"
+
+using namespace android;
+using namespace aaudio;
+
+ANDROID_SINGLETON_STATIC_INSTANCE(AAudioEndpointManager);
+
+AAudioEndpointManager::AAudioEndpointManager()
+        : Singleton<AAudioEndpointManager>() {
+}
+
+AAudioServiceEndpoint *AAudioEndpointManager::findEndpoint(AAudioService &audioService, int32_t deviceId,
+                                                           aaudio_direction_t direction) {
+    AAudioServiceEndpoint *endpoint = nullptr;
+    std::lock_guard<std::mutex> lock(mLock);
+    switch (direction) {
+        case AAUDIO_DIRECTION_INPUT:
+            endpoint = mInputs[deviceId];
+            break;
+        case AAUDIO_DIRECTION_OUTPUT:
+            endpoint = mOutputs[deviceId];
+            break;
+        default:
+            assert(false); // There are only two possible directions.
+            break;
+    }
+
+    // If we can't find an existing one then open one.
+    ALOGD("AAudioEndpointManager::findEndpoint(), found %p", endpoint);
+    if (endpoint == nullptr) {
+        endpoint = new AAudioServiceEndpoint(audioService);
+        if (endpoint->open(deviceId, direction) != AAUDIO_OK) {
+            ALOGD("AAudioEndpointManager::findEndpoint(), open failed");
+            delete endpoint;
+            endpoint = nullptr;
+        } else {
+            switch(direction) {
+                case AAUDIO_DIRECTION_INPUT:
+                    mInputs[deviceId] = endpoint;
+                    break;
+                case AAUDIO_DIRECTION_OUTPUT:
+                    mOutputs[deviceId] = endpoint;
+                    break;
+            }
+        }
+    }
+    return endpoint;
+}
+
+// FIXME add reference counter for serviceEndpoints and removed on last use.
+
+void AAudioEndpointManager::removeEndpoint(AAudioServiceEndpoint *serviceEndpoint) {
+    aaudio_direction_t direction = serviceEndpoint->getDirection();
+    int32_t deviceId = serviceEndpoint->getDeviceId();
+
+    std::lock_guard<std::mutex> lock(mLock);
+    switch(direction) {
+        case AAUDIO_DIRECTION_INPUT:
+            mInputs.erase(deviceId);
+            break;
+        case AAUDIO_DIRECTION_OUTPUT:
+            mOutputs.erase(deviceId);
+            break;
+    }
+}
\ No newline at end of file
diff --git a/services/oboeservice/AAudioEndpointManager.h b/services/oboeservice/AAudioEndpointManager.h
new file mode 100644
index 0000000..48b27f0
--- /dev/null
+++ b/services/oboeservice/AAudioEndpointManager.h
@@ -0,0 +1,61 @@
+/*
+ * 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_AAUDIO_ENDPOINT_MANAGER_H
+#define AAUDIO_AAUDIO_ENDPOINT_MANAGER_H
+
+#include <map>
+#include <mutex>
+#include <utils/Singleton.h>
+
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceEndpoint.h"
+
+namespace aaudio {
+
+class AAudioEndpointManager : public android::Singleton<AAudioEndpointManager>{
+public:
+    AAudioEndpointManager();
+    ~AAudioEndpointManager() = default;
+
+    /**
+     * Find a service endpoint for the given deviceId and direction.
+     * If an endpoint does not already exist then it will try to create one.
+     *
+     * @param deviceId
+     * @param direction
+     * @return endpoint or nullptr
+     */
+    AAudioServiceEndpoint *findEndpoint(android::AAudioService &audioService,
+                                        int32_t deviceId,
+                                        aaudio_direction_t direction);
+
+    void removeEndpoint(AAudioServiceEndpoint *serviceEndpoint);
+
+private:
+
+    std::mutex    mLock;
+
+    // We need separate inputs and outputs because they may both have device==0.
+    // TODO review
+    std::map<int32_t, AAudioServiceEndpoint *> mInputs;
+    std::map<int32_t, AAudioServiceEndpoint *> mOutputs;
+
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_AAUDIO_ENDPOINT_MANAGER_H
diff --git a/services/oboeservice/AAudioMixer.cpp b/services/oboeservice/AAudioMixer.cpp
new file mode 100644
index 0000000..70da339
--- /dev/null
+++ b/services/oboeservice/AAudioMixer.cpp
@@ -0,0 +1,85 @@
+/*
+ * 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 "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <cstring>
+#include "AAudioMixer.h"
+
+using android::WrappingBuffer;
+using android::FifoBuffer;
+using android::fifo_frames_t;
+
+AAudioMixer::~AAudioMixer() {
+    delete[] mOutputBuffer;
+}
+
+void AAudioMixer::allocate(int32_t samplesPerFrame, int32_t framesPerBurst) {
+    mSamplesPerFrame = samplesPerFrame;
+    mFramesPerBurst = framesPerBurst;
+    int32_t samplesPerBuffer = samplesPerFrame * framesPerBurst;
+    mOutputBuffer = new float[samplesPerBuffer];
+    mBufferSizeInBytes = samplesPerBuffer * sizeof(float);
+}
+
+void AAudioMixer::clear() {
+    memset(mOutputBuffer, 0, mBufferSizeInBytes);
+}
+
+void AAudioMixer::mix(FifoBuffer *fifo, float volume) {
+    WrappingBuffer wrappingBuffer;
+    float *destination = mOutputBuffer;
+    fifo_frames_t framesLeft = mFramesPerBurst;
+
+    // Gather the data from the client. May be in two parts.
+    fifo->getFullDataAvailable(&wrappingBuffer);
+
+    // Mix data in one or two parts.
+    int partIndex = 0;
+    while (framesLeft > 0 && partIndex < WrappingBuffer::SIZE) {
+        fifo_frames_t framesToMix = framesLeft;
+        fifo_frames_t framesAvailable = wrappingBuffer.numFrames[partIndex];
+        if (framesAvailable > 0) {
+            if (framesToMix > framesAvailable) {
+                framesToMix = framesAvailable;
+            }
+            mixPart(destination, (float *)wrappingBuffer.data[partIndex], framesToMix, volume);
+
+            destination += framesToMix * mSamplesPerFrame;
+            framesLeft -= framesToMix;
+        }
+        partIndex++;
+    }
+    fifo->getFifoControllerBase()->advanceReadIndex(mFramesPerBurst - framesLeft);
+    if (framesLeft > 0) {
+        ALOGW("AAudioMixer::mix() UNDERFLOW by %d / %d frames ----- UNDERFLOW !!!!!!!!!!",
+              framesLeft, mFramesPerBurst);
+    }
+}
+
+void AAudioMixer::mixPart(float *destination, float *source, int32_t numFrames, float volume) {
+    int32_t numSamples = numFrames * mSamplesPerFrame;
+    // TODO maybe optimize using SIMD
+    for (int sampleIndex = 0; sampleIndex < numSamples; sampleIndex++) {
+        *destination++ += *source++ * volume;
+    }
+}
+
+float *AAudioMixer::getOutputBuffer() {
+    return mOutputBuffer;
+}
diff --git a/services/oboeservice/AAudioMixer.h b/services/oboeservice/AAudioMixer.h
new file mode 100644
index 0000000..2191183
--- /dev/null
+++ b/services/oboeservice/AAudioMixer.h
@@ -0,0 +1,48 @@
+/*
+ * 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_AAUDIO_MIXER_H
+#define AAUDIO_AAUDIO_MIXER_H
+
+#include <stdint.h>
+
+#include <aaudio/AAudio.h>
+#include <fifo/FifoBuffer.h>
+
+class AAudioMixer {
+public:
+    AAudioMixer() {}
+    ~AAudioMixer();
+
+    void allocate(int32_t samplesPerFrame, int32_t framesPerBurst);
+
+    void clear();
+
+    void mix(android::FifoBuffer *fifo, float volume);
+
+    void mixPart(float *destination, float *source, int32_t numFrames, float volume);
+
+    float *getOutputBuffer();
+
+private:
+    float   *mOutputBuffer = nullptr;
+    int32_t  mSamplesPerFrame = 0;
+    int32_t  mFramesPerBurst = 0;
+    int32_t  mBufferSizeInBytes = 0;
+};
+
+
+#endif //AAUDIO_AAUDIO_MIXER_H
diff --git a/services/oboeservice/AAudioService.cpp b/services/oboeservice/AAudioService.cpp
index 99b0b4d..e4fa1c5 100644
--- a/services/oboeservice/AAudioService.cpp
+++ b/services/oboeservice/AAudioService.cpp
@@ -18,28 +18,29 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
-#include <time.h>
-#include <pthread.h>
+//#include <time.h>
+//#include <pthread.h>
 
 #include <aaudio/AAudioDefinitions.h>
+#include <mediautils/SchedulingPolicyService.h>
+#include <utils/String16.h>
 
-#include "HandleTracker.h"
-#include "IAAudioService.h"
-#include "AAudioServiceDefinitions.h"
+#include "binding/AAudioServiceMessage.h"
 #include "AAudioService.h"
-#include "AAudioServiceStreamFakeHal.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "binding/IAAudioService.h"
+#include "utility/HandleTracker.h"
 
 using namespace android;
 using namespace aaudio;
 
 typedef enum
 {
-    AAUDIO_HANDLE_TYPE_DUMMY1, // TODO remove DUMMYs
-    AAUDIO_HANDLE_TYPE_DUMMY2, // make server handles different than client
-    AAUDIO_HANDLE_TYPE_STREAM,
-    AAUDIO_HANDLE_TYPE_COUNT
+    AAUDIO_HANDLE_TYPE_STREAM
 } aaudio_service_handle_type_t;
-static_assert(AAUDIO_HANDLE_TYPE_COUNT <= HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
+static_assert(AAUDIO_HANDLE_TYPE_STREAM < HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
 
 android::AAudioService::AAudioService()
     : BnAAudioService() {
@@ -48,18 +49,50 @@
 AAudioService::~AAudioService() {
 }
 
-aaudio_handle_t AAudioService::openStream(aaudio::AAudioStreamRequest &request,
-                                                aaudio::AAudioStreamConfiguration &configuration) {
-    AAudioServiceStreamBase *serviceStream =  new AAudioServiceStreamFakeHal();
-    ALOGD("AAudioService::openStream(): created serviceStream = %p", serviceStream);
-    aaudio_result_t result = serviceStream->open(request, configuration);
-    if (result < 0) {
-        ALOGE("AAudioService::openStream(): open returned %d", result);
+aaudio_handle_t AAudioService::openStream(const aaudio::AAudioStreamRequest &request,
+                                          aaudio::AAudioStreamConfiguration &configurationOutput) {
+    aaudio_result_t result = AAUDIO_OK;
+    AAudioServiceStreamBase *serviceStream = nullptr;
+    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+    aaudio_sharing_mode_t sharingMode = configurationInput.getSharingMode();
+    ALOGE("AAudioService::openStream(): sharingMode = %d", sharingMode);
+
+    if (sharingMode != AAUDIO_SHARING_MODE_EXCLUSIVE && sharingMode != AAUDIO_SHARING_MODE_SHARED) {
+        ALOGE("AAudioService::openStream(): unrecognized sharing mode = %d", sharingMode);
+        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    if (sharingMode == AAUDIO_SHARING_MODE_EXCLUSIVE) {
+        ALOGD("AAudioService::openStream(), sharingMode = AAUDIO_SHARING_MODE_EXCLUSIVE");
+        serviceStream = new AAudioServiceStreamMMAP();
+        result = serviceStream->open(request, configurationOutput);
+        if (result != AAUDIO_OK) {
+            // fall back to using a shared stream
+            ALOGD("AAudioService::openStream(), EXCLUSIVE mode failed");
+            delete serviceStream;
+            serviceStream = nullptr;
+        } else {
+            configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+        }
+    }
+
+    // if SHARED requested or if EXCLUSIVE failed
+    if (serviceStream == nullptr) {
+        ALOGD("AAudioService::openStream(), sharingMode = AAUDIO_SHARING_MODE_SHARED");
+        serviceStream =  new AAudioServiceStreamShared(*this);
+        result = serviceStream->open(request, configurationOutput);
+        configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
+    }
+
+    if (result != AAUDIO_OK) {
+        delete serviceStream;
+        ALOGE("AAudioService::openStream(): failed, return %d", result);
         return result;
     } else {
         aaudio_handle_t handle = mHandleTracker.put(AAUDIO_HANDLE_TYPE_STREAM, serviceStream);
         ALOGD("AAudioService::openStream(): handle = 0x%08X", handle);
         if (handle < 0) {
+            ALOGE("AAudioService::openStream(): handle table full");
             delete serviceStream;
         }
         return handle;
@@ -72,7 +105,7 @@
                                   streamHandle);
     ALOGD("AAudioService.closeStream(0x%08X)", streamHandle);
     if (serviceStream != nullptr) {
-        ALOGD("AAudioService::closeStream(): deleting serviceStream = %p", serviceStream);
+        serviceStream->close();
         delete serviceStream;
         return AAUDIO_OK;
     }
@@ -89,27 +122,32 @@
                 aaudio_handle_t streamHandle,
                 aaudio::AudioEndpointParcelable &parcelable) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
-    ALOGD("AAudioService::getStreamDescription(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
+        ALOGE("AAudioService::getStreamDescription(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
-    return serviceStream->getDescription(parcelable);
+    ALOGD("AAudioService::getStreamDescription(), handle = 0x%08x", streamHandle);
+    aaudio_result_t result = serviceStream->getDescription(parcelable);
+    ALOGD("AAudioService::getStreamDescription(), result = %d", result);
+    // parcelable.dump();
+    return result;
 }
 
 aaudio_result_t AAudioService::startStream(aaudio_handle_t streamHandle) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
-    ALOGD("AAudioService::startStream(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
+        ALOGE("AAudioService::startStream(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
     aaudio_result_t result = serviceStream->start();
+    ALOGD("AAudioService::startStream(), serviceStream->start() returned %d", result);
     return result;
 }
 
 aaudio_result_t AAudioService::pauseStream(aaudio_handle_t streamHandle) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
-    ALOGD("AAudioService::pauseStream(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
+        ALOGE("AAudioService::pauseStream(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
     aaudio_result_t result = serviceStream->pause();
@@ -118,35 +156,33 @@
 
 aaudio_result_t AAudioService::flushStream(aaudio_handle_t streamHandle) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
-    ALOGD("AAudioService::flushStream(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
+        ALOGE("AAudioService::flushStream(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
     return serviceStream->flush();
 }
 
 aaudio_result_t AAudioService::registerAudioThread(aaudio_handle_t streamHandle,
+                                                         pid_t clientProcessId,
                                                          pid_t clientThreadId,
                                                          int64_t periodNanoseconds) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
     ALOGD("AAudioService::registerAudioThread(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
-        ALOGE("AAudioService::registerAudioThread(), serviceStream == nullptr");
+        ALOGE("AAudioService::registerAudioThread(), illegal stream handle = 0x%0x", streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
     if (serviceStream->getRegisteredThread() != AAudioServiceStreamBase::ILLEGAL_THREAD_ID) {
         ALOGE("AAudioService::registerAudioThread(), thread already registered");
-        return AAUDIO_ERROR_INVALID_ORDER;
+        return AAUDIO_ERROR_INVALID_STATE;
     }
     serviceStream->setRegisteredThread(clientThreadId);
-    // Boost client thread to SCHED_FIFO
-    struct sched_param sp;
-    memset(&sp, 0, sizeof(sp));
-    sp.sched_priority = 2; // TODO use 'requestPriority' function from frameworks/av/media/utils
-    int err = sched_setscheduler(clientThreadId, SCHED_FIFO, &sp);
+    int err = android::requestPriority(clientProcessId, clientThreadId,
+                                       DEFAULT_AUDIO_PRIORITY, true /* isForApp */);
     if (err != 0){
-        ALOGE("AAudioService::sched_setscheduler() failed, errno = %d, priority = %d",
-              errno, sp.sched_priority);
+        ALOGE("AAudioService::registerAudioThread() failed, errno = %d, priority = %d",
+              errno, DEFAULT_AUDIO_PRIORITY);
         return AAUDIO_ERROR_INTERNAL;
     } else {
         return AAUDIO_OK;
@@ -154,11 +190,13 @@
 }
 
 aaudio_result_t AAudioService::unregisterAudioThread(aaudio_handle_t streamHandle,
-                                                           pid_t clientThreadId) {
+                                                     pid_t clientProcessId,
+                                                     pid_t clientThreadId) {
     AAudioServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
     ALOGI("AAudioService::unregisterAudioThread(), serviceStream = %p", serviceStream);
     if (serviceStream == nullptr) {
-        ALOGE("AAudioService::unregisterAudioThread(), serviceStream == nullptr");
+        ALOGE("AAudioService::unregisterAudioThread(), illegal stream handle = 0x%0x",
+              streamHandle);
         return AAUDIO_ERROR_INVALID_HANDLE;
     }
     if (serviceStream->getRegisteredThread() != clientThreadId) {
diff --git a/services/oboeservice/AAudioService.h b/services/oboeservice/AAudioService.h
index a520d7a..5a7a2b6 100644
--- a/services/oboeservice/AAudioService.h
+++ b/services/oboeservice/AAudioService.h
@@ -22,17 +22,19 @@
 
 #include <binder/BinderService.h>
 
-#include <aaudio/AAudioDefinitions.h>
 #include <aaudio/AAudio.h>
 #include "utility/HandleTracker.h"
-#include "IAAudioService.h"
+#include "binding/IAAudioService.h"
+#include "binding/AAudioServiceInterface.h"
+
 #include "AAudioServiceStreamBase.h"
 
 namespace android {
 
 class AAudioService :
     public BinderService<AAudioService>,
-    public BnAAudioService
+    public BnAAudioService,
+    public aaudio::AAudioServiceInterface
 {
     friend class BinderService<AAudioService>;
 
@@ -40,9 +42,9 @@
     AAudioService();
     virtual ~AAudioService();
 
-    static const char* getServiceName() { return "media.audio_aaudio"; }
+    static const char* getServiceName() { return AAUDIO_SERVICE_NAME; }
 
-    virtual aaudio_handle_t openStream(aaudio::AAudioStreamRequest &request,
+    virtual aaudio_handle_t openStream(const aaudio::AAudioStreamRequest &request,
                                      aaudio::AAudioStreamConfiguration &configuration);
 
     virtual aaudio_result_t closeStream(aaudio_handle_t streamHandle);
@@ -58,9 +60,11 @@
     virtual aaudio_result_t flushStream(aaudio_handle_t streamHandle);
 
     virtual aaudio_result_t registerAudioThread(aaudio_handle_t streamHandle,
-                                              pid_t pid, int64_t periodNanoseconds) ;
+                                              pid_t pid, pid_t tid,
+                                              int64_t periodNanoseconds) ;
 
-    virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle, pid_t pid);
+    virtual aaudio_result_t unregisterAudioThread(aaudio_handle_t streamHandle,
+                                                  pid_t pid, pid_t tid);
 
 private:
 
@@ -68,6 +72,9 @@
 
     HandleTracker mHandleTracker;
 
+    enum constants {
+        DEFAULT_AUDIO_PRIORITY = 2
+    };
 };
 
 } /* namespace android */
diff --git a/services/oboeservice/AAudioServiceDefinitions.h b/services/oboeservice/AAudioServiceDefinitions.h
deleted file mode 100644
index f98acbf..0000000
--- a/services/oboeservice/AAudioServiceDefinitions.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2016 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_AAUDIO_SERVICE_DEFINITIONS_H
-#define AAUDIO_AAUDIO_SERVICE_DEFINITIONS_H
-
-#include <stdint.h>
-
-#include <aaudio/AAudio.h>
-
-#include "binding/RingBufferParcelable.h"
-
-namespace aaudio {
-
-// TODO move this an "include" folder for the service.
-
-struct AAudioMessageTimestamp {
-    int64_t position;
-    int64_t                deviceOffset; // add to client position to get device position
-    int64_t     timestamp;
-};
-
-typedef enum aaudio_service_event_e : uint32_t {
-    AAUDIO_SERVICE_EVENT_STARTED,
-    AAUDIO_SERVICE_EVENT_PAUSED,
-    AAUDIO_SERVICE_EVENT_FLUSHED,
-    AAUDIO_SERVICE_EVENT_CLOSED,
-    AAUDIO_SERVICE_EVENT_DISCONNECTED
-} aaudio_service_event_t;
-
-struct AAudioMessageEvent {
-    aaudio_service_event_t event;
-    int32_t data1;
-    int64_t data2;
-};
-
-typedef struct AAudioServiceMessage_s {
-    enum class code : uint32_t {
-        NOTHING,
-        TIMESTAMP,
-        EVENT,
-    };
-
-    code what;
-    union {
-        AAudioMessageTimestamp timestamp;
-        AAudioMessageEvent event;
-    };
-} AAudioServiceMessage;
-
-} /* namespace aaudio */
-
-#endif //AAUDIO_AAUDIO_SERVICE_DEFINITIONS_H
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
new file mode 100644
index 0000000..80551c9
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -0,0 +1,188 @@
+/*
+ * 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.
+ */
+
+#include <algorithm>
+#include <mutex>
+#include <vector>
+
+#include "core/AudioStreamBuilder.h"
+#include "AAudioServiceEndpoint.h"
+#include "AAudioServiceStreamShared.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
+
+// The mStreamInternal will use a service interface that does not go through Binder.
+AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)
+        : mStreamInternal(audioService, true)
+        {
+}
+
+AAudioServiceEndpoint::~AAudioServiceEndpoint() {
+}
+
+// Set up an EXCLUSIVE MMAP stream that will be shared.
+aaudio_result_t AAudioServiceEndpoint::open(int32_t deviceId, aaudio_direction_t direction) {
+    AudioStreamBuilder builder;
+    builder.setSharingMode(AAUDIO_SHARING_MODE_EXCLUSIVE);
+    builder.setDeviceId(deviceId);
+    builder.setDirection(direction);
+    aaudio_result_t result = mStreamInternal.open(builder);
+    if (result == AAUDIO_OK) {
+        mMixer.allocate(mStreamInternal.getSamplesPerFrame(), mStreamInternal.getFramesPerBurst());
+    }
+    return result;
+}
+
+aaudio_result_t AAudioServiceEndpoint::close() {
+    return mStreamInternal.close();
+}
+
+// TODO, maybe use an interface to reduce exposure
+aaudio_result_t AAudioServiceEndpoint::registerStream(AAudioServiceStreamShared *sharedStream) {
+    ALOGD("AAudioServiceEndpoint::registerStream(%p)", sharedStream);
+    // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRegisteredStreams.push_back(sharedStream);
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpoint::unregisterStream(AAudioServiceStreamShared *sharedStream) {
+    ALOGD("AAudioServiceEndpoint::unregisterStream(%p)", 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(AAudioServiceStreamShared *sharedStream) {
+    // TODO use real-time technique to avoid mutex, eg. atomic command FIFO
+    ALOGD("AAudioServiceEndpoint(): startStream() entering");
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRunningStreams.push_back(sharedStream);
+    if (mRunningStreams.size() == 1) {
+        startMixer_l();
+    }
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceEndpoint::stopStream(AAudioServiceStreamShared *sharedStream) {
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    mRunningStreams.erase(std::remove(mRunningStreams.begin(), mRunningStreams.end(), sharedStream),
+              mRunningStreams.end());
+    if (mRunningStreams.size() == 0) {
+        stopMixer_l();
+    }
+    return AAUDIO_OK;
+}
+
+static void *aaudio_mixer_thread_proc(void *context) {
+    AAudioServiceEndpoint *stream = (AAudioServiceEndpoint *) context;
+    //LOGD("AudioStreamAAudio(): oboe_callback_thread, stream = %p", stream);
+    if (stream != NULL) {
+        return stream->callbackLoop();
+    } else {
+        return NULL;
+    }
+}
+
+// Render audio in the application callback and then write the data to the stream.
+void *AAudioServiceEndpoint::callbackLoop() {
+    aaudio_result_t result = AAUDIO_OK;
+
+    ALOGD("AAudioServiceEndpoint(): callbackLoop() entering");
+
+    result = mStreamInternal.requestStart();
+    ALOGD("AAudioServiceEndpoint(): callbackLoop() after requestStart()  %d, isPlaying() = %d",
+          result, (int) mStreamInternal.isPlaying());
+
+    // result might be a frame count
+    while (mCallbackEnabled.load() && mStreamInternal.isPlaying() && (result >= 0)) {
+        // Mix data from each active stream.
+        {
+            mMixer.clear();
+            std::lock_guard<std::mutex> lock(mLockStreams);
+            for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
+                FifoBuffer *fifo = sharedStream->getDataFifoBuffer();
+                float volume = 0.5; // TODO get from system
+                mMixer.mix(fifo, volume);
+            }
+        }
+
+        // Write audio data to stream using a blocking write.
+        ALOGD("AAudioServiceEndpoint(): callbackLoop() write(%d)", getFramesPerBurst());
+        int64_t timeoutNanos = calculateReasonableTimeout(mStreamInternal.getFramesPerBurst());
+        result = mStreamInternal.write(mMixer.getOutputBuffer(), getFramesPerBurst(), timeoutNanos);
+        if (result == AAUDIO_ERROR_DISCONNECTED) {
+            disconnectRegisteredStreams();
+            break;
+        } else if (result != getFramesPerBurst()) {
+            ALOGW("AAudioServiceEndpoint(): callbackLoop() wrote %d / %d",
+                  result, getFramesPerBurst());
+            break;
+        }
+    }
+
+    ALOGD("AAudioServiceEndpoint(): callbackLoop() exiting, result = %d, isPlaying() = %d",
+          result, (int) mStreamInternal.isPlaying());
+
+    result = mStreamInternal.requestStop();
+
+    return NULL; // TODO review
+}
+
+aaudio_result_t AAudioServiceEndpoint::startMixer_l() {
+    // Launch the callback loop thread.
+    int64_t periodNanos = mStreamInternal.getFramesPerBurst()
+                          * AAUDIO_NANOS_PER_SECOND
+                          / getSampleRate();
+    mCallbackEnabled.store(true);
+    return mStreamInternal.createThread(periodNanos, aaudio_mixer_thread_proc, this);
+}
+
+aaudio_result_t AAudioServiceEndpoint::stopMixer_l() {
+    mCallbackEnabled.store(false);
+    return mStreamInternal.joinThread(NULL, calculateReasonableTimeout(mStreamInternal.getFramesPerBurst()));
+}
+
+// TODO Call method in AudioStreamInternal when that callback CL is merged.
+int64_t AAudioServiceEndpoint::calculateReasonableTimeout(int32_t framesPerOperation) {
+
+    // Wait for at least a second or some number of callbacks to join the thread.
+    int64_t timeoutNanoseconds = (MIN_TIMEOUT_OPERATIONS * framesPerOperation * AAUDIO_NANOS_PER_SECOND)
+                                 / getSampleRate();
+    if (timeoutNanoseconds < MIN_TIMEOUT_NANOS) { // arbitrary number of seconds
+        timeoutNanoseconds = MIN_TIMEOUT_NANOS;
+    }
+    return timeoutNanoseconds;
+}
+
+void AAudioServiceEndpoint::disconnectRegisteredStreams() {
+    std::lock_guard<std::mutex> lock(mLockStreams);
+    for(AAudioServiceStreamShared *sharedStream : mRunningStreams) {
+        sharedStream->onStop();
+    }
+    mRunningStreams.clear();
+    for(AAudioServiceStreamShared *sharedStream : mRegisteredStreams) {
+        sharedStream->onDisconnect();
+    }
+    mRegisteredStreams.clear();
+}
diff --git a/services/oboeservice/AAudioServiceEndpoint.h b/services/oboeservice/AAudioServiceEndpoint.h
new file mode 100644
index 0000000..020d38a
--- /dev/null
+++ b/services/oboeservice/AAudioServiceEndpoint.h
@@ -0,0 +1,79 @@
+/*
+ * 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_H
+#define AAUDIO_SERVICE_ENDPOINT_H
+
+#include <atomic>
+#include <functional>
+#include <mutex>
+#include <vector>
+
+#include "client/AudioStreamInternal.h"
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "AAudioMixer.h"
+#include "AAudioService.h"
+
+namespace aaudio {
+
+class AAudioServiceEndpoint {
+public:
+    explicit AAudioServiceEndpoint(android::AAudioService &audioService);
+    virtual ~AAudioServiceEndpoint();
+
+    aaudio_result_t open(int32_t deviceId, aaudio_direction_t direction);
+
+    int32_t getSampleRate() const { return mStreamInternal.getSampleRate(); }
+    int32_t getSamplesPerFrame() const { return mStreamInternal.getSamplesPerFrame();  }
+    int32_t getFramesPerBurst() const { return mStreamInternal.getFramesPerBurst();  }
+
+    aaudio_result_t registerStream(AAudioServiceStreamShared *sharedStream);
+    aaudio_result_t unregisterStream(AAudioServiceStreamShared *sharedStream);
+    aaudio_result_t startStream(AAudioServiceStreamShared *sharedStream);
+    aaudio_result_t stopStream(AAudioServiceStreamShared *sharedStream);
+    aaudio_result_t close();
+
+    int32_t getDeviceId() const { return mStreamInternal.getDeviceId(); }
+
+    aaudio_direction_t getDirection() const { return mStreamInternal.getDirection(); }
+
+    void disconnectRegisteredStreams();
+
+    void *callbackLoop();
+
+private:
+    aaudio_result_t startMixer_l();
+    aaudio_result_t stopMixer_l();
+
+    int64_t calculateReasonableTimeout(int32_t framesPerOperation);
+
+    AudioStreamInternal      mStreamInternal;
+    AAudioMixer              mMixer;
+    AAudioServiceStreamMMAP  mStreamMMAP;
+
+    std::atomic<bool>        mCallbackEnabled;
+
+    std::mutex               mLockStreams;
+    std::vector<AAudioServiceStreamShared *> mRegisteredStreams;
+    std::vector<AAudioServiceStreamShared *> mRunningStreams;
+};
+
+} /* namespace aaudio */
+
+
+#endif //AAUDIO_SERVICE_ENDPOINT_H
diff --git a/services/oboeservice/AAudioServiceMain.cpp b/services/oboeservice/AAudioServiceMain.cpp
deleted file mode 100644
index aa89180..0000000
--- a/services/oboeservice/AAudioServiceMain.cpp
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2016 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 "AAudioService"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <stdint.h>
-#include <math.h>
-
-#include <utils/RefBase.h>
-#include <binder/TextOutput.h>
-
-#include <binder/IInterface.h>
-#include <binder/IBinder.h>
-#include <binder/ProcessState.h>
-#include <binder/IServiceManager.h>
-#include <binder/IPCThreadState.h>
-
-#include <cutils/ashmem.h>
-#include <sys/mman.h>
-
-#include "AAudioServiceDefinitions.h"
-#include "IAAudioService.h"
-#include "AAudioService.h"
-
-using namespace android;
-using namespace aaudio;
-
-/**
- * This is used to test the AAudioService as a standalone application.
- * It is not used when the AAudioService is integrated with AudioFlinger.
- */
-int main(int argc, char **argv) {
-    printf("Test AAudioService %s\n", argv[1]);
-    ALOGD("This is the AAudioService");
-
-    defaultServiceManager()->addService(String16("AAudioService"), new AAudioService());
-    android::ProcessState::self()->startThreadPool();
-    printf("AAudioService service is now ready\n");
-    IPCThreadState::self()->joinThreadPool();
-    printf("AAudioService service thread joined\n");
-
-    return 0;
-}
diff --git a/services/oboeservice/AAudioServiceStreamBase.cpp b/services/oboeservice/AAudioServiceStreamBase.cpp
index a7938dc..b15043d 100644
--- a/services/oboeservice/AAudioServiceStreamBase.cpp
+++ b/services/oboeservice/AAudioServiceStreamBase.cpp
@@ -18,43 +18,138 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
-#include "IAAudioService.h"
-#include "AAudioServiceDefinitions.h"
-#include "AAudioServiceStreamBase.h"
-#include "AudioEndpointParcelable.h"
+#include <mutex>
 
-using namespace android;
-using namespace aaudio;
+#include "binding/IAAudioService.h"
+#include "binding/AAudioServiceMessage.h"
+#include "utility/AudioClock.h"
+
+#include "AAudioServiceStreamBase.h"
+#include "TimestampScheduler.h"
+
+using namespace android;  // TODO just import names needed
+using namespace aaudio;   // TODO just import names needed
 
 /**
- * Construct the AudioCommandQueues and the AudioDataQueue
- * and fill in the endpoint parcelable.
+ * Base class for streams in the service.
+ * @return
  */
 
 AAudioServiceStreamBase::AAudioServiceStreamBase()
         : mUpMessageQueue(nullptr)
-{
-    // TODO could fail so move out of constructor
-    mUpMessageQueue = new SharedRingBuffer();
-    mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), QUEUE_UP_CAPACITY_COMMANDS);
+        , mAAudioThread() {
 }
 
 AAudioServiceStreamBase::~AAudioServiceStreamBase() {
-    Mutex::Autolock _l(mLockUpMessageQueue);
-    delete mUpMessageQueue;
+    close();
 }
 
-void AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event,
-                              int32_t data1,
-                              int64_t data2) {
+aaudio_result_t AAudioServiceStreamBase::open(const aaudio::AAudioStreamRequest &request,
+                     aaudio::AAudioStreamConfiguration &configurationOutput) {
+    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+    if (mUpMessageQueue != nullptr) {
+        return AAUDIO_ERROR_INVALID_STATE;
+    } else {
+        mUpMessageQueue = new SharedRingBuffer();
+        return mUpMessageQueue->allocate(sizeof(AAudioServiceMessage), QUEUE_UP_CAPACITY_COMMANDS);
+    }
+}
 
-    Mutex::Autolock _l(mLockUpMessageQueue);
+aaudio_result_t AAudioServiceStreamBase::close() {
+    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+    delete mUpMessageQueue;
+    mUpMessageQueue = nullptr;
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceStreamBase::start() {
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
+    mState = AAUDIO_STREAM_STATE_STARTED;
+    mThreadEnabled.store(true);
+    return mAAudioThread.start(this);
+}
+
+aaudio_result_t AAudioServiceStreamBase::pause() {
+
+    sendCurrentTimestamp();
+    mThreadEnabled.store(false);
+    aaudio_result_t result = mAAudioThread.stop();
+    if (result != AAUDIO_OK) {
+        processError();
+        return result;
+    }
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
+    mState = AAUDIO_STREAM_STATE_PAUSED;
+    return result;
+}
+
+// implement Runnable
+void AAudioServiceStreamBase::run() {
+    ALOGD("AAudioServiceStreamMMAP::run() entering ----------------");
+    TimestampScheduler timestampScheduler;
+    timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
+    timestampScheduler.start(AudioClock::getNanoseconds());
+    int64_t nextTime = timestampScheduler.nextAbsoluteTime();
+    while(mThreadEnabled.load()) {
+        if (AudioClock::getNanoseconds() >= nextTime) {
+            aaudio_result_t result = sendCurrentTimestamp();
+            if (result != AAUDIO_OK) {
+                break;
+            }
+            nextTime = timestampScheduler.nextAbsoluteTime();
+        } else  {
+            // Sleep until it is time to send the next timestamp.
+            AudioClock::sleepUntilNanoTime(nextTime);
+        }
+    }
+    ALOGD("AAudioServiceStreamMMAP::run() exiting ----------------");
+}
+
+void AAudioServiceStreamBase::processError() {
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
+}
+
+aaudio_result_t AAudioServiceStreamBase::sendServiceEvent(aaudio_service_event_t event,
+                                               double  dataDouble,
+                                               int64_t dataLong) {
     AAudioServiceMessage command;
     command.what = AAudioServiceMessage::code::EVENT;
     command.event.event = event;
-    command.event.data1 = data1;
-    command.event.data2 = data2;
-    mUpMessageQueue->getFifoBuffer()->write(&command, 1);
+    command.event.dataDouble = dataDouble;
+    command.event.dataLong = dataLong;
+    return writeUpMessageQueue(&command);
+}
+
+aaudio_result_t AAudioServiceStreamBase::writeUpMessageQueue(AAudioServiceMessage *command) {
+    std::lock_guard<std::mutex> lock(mLockUpMessageQueue);
+    int32_t count = mUpMessageQueue->getFifoBuffer()->write(command, 1);
+    if (count != 1) {
+        ALOGE("writeUpMessageQueue(): Queue full. Did client die?");
+        return AAUDIO_ERROR_WOULD_BLOCK;
+    } else {
+        return AAUDIO_OK;
+    }
+}
+
+aaudio_result_t AAudioServiceStreamBase::sendCurrentTimestamp() {
+    AAudioServiceMessage command;
+    aaudio_result_t result = getFreeRunningPosition(&command.timestamp.position,
+                                                    &command.timestamp.timestamp);
+    if (result == AAUDIO_OK) {
+        command.what = AAudioServiceMessage::code::TIMESTAMP;
+        result = writeUpMessageQueue(&command);
+    }
+    return result;
 }
 
 
+/**
+ * Get an immutable description of the in-memory queues
+ * used to communicate with the underlying HAL or Service.
+ */
+aaudio_result_t AAudioServiceStreamBase::getDescription(AudioEndpointParcelable &parcelable) {
+    // Gather information on the message queue.
+    mUpMessageQueue->fillParcelable(parcelable,
+                                    parcelable.mUpMessageQueueParcelable);
+    return getDownDataDescription(parcelable);
+}
\ No newline at end of file
diff --git a/services/oboeservice/AAudioServiceStreamBase.h b/services/oboeservice/AAudioServiceStreamBase.h
index 7a812f9..91eec35 100644
--- a/services/oboeservice/AAudioServiceStreamBase.h
+++ b/services/oboeservice/AAudioServiceStreamBase.h
@@ -17,13 +17,15 @@
 #ifndef AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
 #define AAUDIO_AAUDIO_SERVICE_STREAM_BASE_H
 
-#include <utils/Mutex.h>
+#include <mutex>
 
-#include "IAAudioService.h"
-#include "AAudioServiceDefinitions.h"
 #include "fifo/FifoBuffer.h"
+#include "binding/IAAudioService.h"
+#include "binding/AudioEndpointParcelable.h"
+#include "binding/AAudioServiceMessage.h"
+#include "utility/AAudioUtilities.h"
+
 #include "SharedRingBuffer.h"
-#include "AudioEndpointParcelable.h"
 #include "AAudioThread.h"
 
 namespace aaudio {
@@ -32,7 +34,11 @@
 // This should be way more than we need.
 #define QUEUE_UP_CAPACITY_COMMANDS (128)
 
-class AAudioServiceStreamBase {
+/**
+ * Base class for a stream in the AAudio service.
+ */
+class AAudioServiceStreamBase
+    : public Runnable  {
 
 public:
     AAudioServiceStreamBase();
@@ -42,16 +48,14 @@
         ILLEGAL_THREAD_ID = 0
     };
 
-    /**
-     * Fill in a parcelable description of stream.
-     */
-    virtual aaudio_result_t getDescription(aaudio::AudioEndpointParcelable &parcelable) = 0;
-
+    // -------------------------------------------------------------------
     /**
      * Open the device.
      */
-    virtual aaudio_result_t open(aaudio::AAudioStreamRequest &request,
-                               aaudio::AAudioStreamConfiguration &configuration) = 0;
+    virtual aaudio_result_t open(const aaudio::AAudioStreamRequest &request,
+                                 aaudio::AAudioStreamConfiguration &configurationOutput) = 0;
+
+    virtual aaudio_result_t close();
 
     /**
      * Start the flow of data.
@@ -68,39 +72,69 @@
      */
     virtual aaudio_result_t flush() = 0;
 
-    virtual aaudio_result_t close() = 0;
+    // -------------------------------------------------------------------
 
-    virtual void sendCurrentTimestamp() = 0;
+    /**
+     * Send a message to the client.
+     */
+    aaudio_result_t sendServiceEvent(aaudio_service_event_t event,
+                                     double  dataDouble = 0.0,
+                                     int64_t dataLong = 0);
 
-    int32_t getFramesPerBurst() {
-        return mFramesPerBurst;
-    }
+    /**
+     * Fill in a parcelable description of stream.
+     */
+    aaudio_result_t getDescription(AudioEndpointParcelable &parcelable);
 
-    virtual void sendServiceEvent(aaudio_service_event_t event,
-                                  int32_t data1 = 0,
-                                  int64_t data2 = 0);
 
-    virtual void setRegisteredThread(pid_t pid) {
+    void setRegisteredThread(pid_t pid) {
         mRegisteredClientThread = pid;
     }
 
-    virtual pid_t getRegisteredThread() {
+    pid_t getRegisteredThread() const {
         return mRegisteredClientThread;
     }
 
+    int32_t getFramesPerBurst() const {
+        return mFramesPerBurst;
+    }
+
+    int32_t calculateBytesPerFrame() const {
+        return mSamplesPerFrame * AAudioConvert_formatToSizeInBytes(mAudioFormat);
+    }
+
+    void run() override; // to implement Runnable
+
+    void processError();
+
 protected:
+    aaudio_result_t writeUpMessageQueue(AAudioServiceMessage *command);
+
+    aaudio_result_t sendCurrentTimestamp();
+
+    virtual aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) = 0;
+
+    virtual aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) = 0;
+
+    aaudio_stream_state_t               mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
 
     pid_t              mRegisteredClientThread = ILLEGAL_THREAD_ID;
 
     SharedRingBuffer*  mUpMessageQueue;
+    std::mutex         mLockUpMessageQueue;
 
-    int32_t            mSampleRate = 0;
-    int32_t            mBytesPerFrame = 0;
+    AAudioThread        mAAudioThread;
+    // This is used by one thread to tell another thread to exit. So it must be atomic.
+    std::atomic<bool>   mThreadEnabled;
+
+
+    int                mAudioDataFileDescriptor = -1;
+
+    aaudio_audio_format_t mAudioFormat = AAUDIO_FORMAT_UNSPECIFIED;
     int32_t            mFramesPerBurst = 0;
-    int32_t            mCapacityInFrames = 0;
-    int32_t            mCapacityInBytes = 0;
-
-    android::Mutex     mLockUpMessageQueue;
+    int32_t            mSamplesPerFrame = AAUDIO_UNSPECIFIED;
+    int32_t            mSampleRate = AAUDIO_UNSPECIFIED;
+    int32_t            mCapacityInFrames = AAUDIO_UNSPECIFIED;
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/AAudioServiceStreamExclusive.h b/services/oboeservice/AAudioServiceStreamExclusive.h
new file mode 100644
index 0000000..db382a3
--- /dev/null
+++ b/services/oboeservice/AAudioServiceStreamExclusive.h
@@ -0,0 +1,40 @@
+/*
+ * 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_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
+#define AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
+
+#include "AAudioServiceStreamMMAP.h"
+
+namespace aaudio {
+
+/**
+ * Exclusive mode stream in the AAudio service.
+ *
+ * This is currently a stub.
+ * We may move code from AAudioServiceStreamMMAP into this class.
+ * If not, then it will be removed.
+ */
+class AAudioServiceStreamExclusive : public AAudioServiceStreamMMAP {
+
+public:
+    AAudioServiceStreamExclusive() {};
+    virtual ~AAudioServiceStreamExclusive() = default;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_AAUDIO_SERVICE_STREAM_EXCLUSIVE_H
diff --git a/services/oboeservice/AAudioServiceStreamFakeHal.cpp b/services/oboeservice/AAudioServiceStreamFakeHal.cpp
deleted file mode 100644
index 71d3542..0000000
--- a/services/oboeservice/AAudioServiceStreamFakeHal.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2016 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 "AAudioService"
-//#define LOG_NDEBUG 0
-#include <utils/Log.h>
-
-#include <atomic>
-
-#include "AudioClock.h"
-#include "AudioEndpointParcelable.h"
-
-#include "AAudioServiceStreamBase.h"
-#include "AAudioServiceStreamFakeHal.h"
-
-#include "FakeAudioHal.h"
-
-using namespace android;
-using namespace aaudio;
-
-// HACK values for Marlin
-#define CARD_ID              0
-#define DEVICE_ID            19
-
-/**
- * Construct the audio message queuues and message queues.
- */
-
-AAudioServiceStreamFakeHal::AAudioServiceStreamFakeHal()
-        : AAudioServiceStreamBase()
-        , mStreamId(nullptr)
-        , mPreviousFrameCounter(0)
-        , mAAudioThread()
-{
-}
-
-AAudioServiceStreamFakeHal::~AAudioServiceStreamFakeHal() {
-    ALOGD("AAudioServiceStreamFakeHal::~AAudioServiceStreamFakeHal() call close()");
-    close();
-}
-
-aaudio_result_t AAudioServiceStreamFakeHal::open(aaudio::AAudioStreamRequest &request,
-                                       aaudio::AAudioStreamConfiguration &configurationOutput) {
-    // Open stream on HAL and pass information about the ring buffer to the client.
-    mmap_buffer_info mmapInfo;
-    aaudio_result_t error;
-
-    // Open HAL
-    int bufferCapacity = request.getConfiguration().getBufferCapacity();
-    error = fake_hal_open(CARD_ID, DEVICE_ID, bufferCapacity, &mStreamId);
-    if(error < 0) {
-        ALOGE("Could not open card %d, device %d", CARD_ID, DEVICE_ID);
-        return error;
-    }
-
-    // Get information about the shared audio buffer.
-    error = fake_hal_get_mmap_info(mStreamId, &mmapInfo);
-    if (error < 0) {
-        ALOGE("fake_hal_get_mmap_info returned %d", error);
-        fake_hal_close(mStreamId);
-        mStreamId = nullptr;
-        return error;
-    }
-    mHalFileDescriptor = mmapInfo.fd;
-    mFramesPerBurst = mmapInfo.burst_size_in_frames;
-    mCapacityInFrames = mmapInfo.buffer_capacity_in_frames;
-    mCapacityInBytes = mmapInfo.buffer_capacity_in_bytes;
-    mSampleRate = mmapInfo.sample_rate;
-    mBytesPerFrame = mmapInfo.channel_count * sizeof(int16_t); // FIXME based on data format
-    ALOGD("AAudioServiceStreamFakeHal::open() mmapInfo.burst_size_in_frames = %d",
-         mmapInfo.burst_size_in_frames);
-    ALOGD("AAudioServiceStreamFakeHal::open() mmapInfo.buffer_capacity_in_frames = %d",
-         mmapInfo.buffer_capacity_in_frames);
-    ALOGD("AAudioServiceStreamFakeHal::open() mmapInfo.buffer_capacity_in_bytes = %d",
-         mmapInfo.buffer_capacity_in_bytes);
-
-    // Fill in AAudioStreamConfiguration
-    configurationOutput.setSampleRate(mSampleRate);
-    configurationOutput.setSamplesPerFrame(mmapInfo.channel_count);
-    configurationOutput.setAudioFormat(AAUDIO_FORMAT_PCM_I16);
-
-    return AAUDIO_OK;
-}
-
-/**
- * Get an immutable description of the in-memory queues
- * used to communicate with the underlying HAL or Service.
- */
-aaudio_result_t AAudioServiceStreamFakeHal::getDescription(AudioEndpointParcelable &parcelable) {
-    // Gather information on the message queue.
-    mUpMessageQueue->fillParcelable(parcelable,
-                                    parcelable.mUpMessageQueueParcelable);
-
-    // Gather information on the data queue.
-    // TODO refactor into a SharedRingBuffer?
-    int fdIndex = parcelable.addFileDescriptor(mHalFileDescriptor, mCapacityInBytes);
-    parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, mCapacityInBytes);
-    parcelable.mDownDataQueueParcelable.setBytesPerFrame(mBytesPerFrame);
-    parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
-    parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames);
-    return AAUDIO_OK;
-}
-
-/**
- * Start the flow of data.
- */
-aaudio_result_t AAudioServiceStreamFakeHal::start() {
-    if (mStreamId == nullptr) return AAUDIO_ERROR_NULL;
-    aaudio_result_t result = fake_hal_start(mStreamId);
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_STARTED);
-    mState = AAUDIO_STREAM_STATE_STARTED;
-    if (result == AAUDIO_OK) {
-        mThreadEnabled.store(true);
-        result = mAAudioThread.start(this);
-    }
-    return result;
-}
-
-/**
- * Stop the flow of data such that start() can resume with loss of data.
- */
-aaudio_result_t AAudioServiceStreamFakeHal::pause() {
-    if (mStreamId == nullptr) return AAUDIO_ERROR_NULL;
-    sendCurrentTimestamp();
-    aaudio_result_t result = fake_hal_pause(mStreamId);
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_PAUSED);
-    mState = AAUDIO_STREAM_STATE_PAUSED;
-    mFramesRead.reset32();
-    ALOGD("AAudioServiceStreamFakeHal::pause() sent AAUDIO_SERVICE_EVENT_PAUSED");
-    mThreadEnabled.store(false);
-    result = mAAudioThread.stop();
-    return result;
-}
-
-/**
- *  Discard any data held by the underlying HAL or Service.
- */
-aaudio_result_t AAudioServiceStreamFakeHal::flush() {
-    if (mStreamId == nullptr) return AAUDIO_ERROR_NULL;
-    // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
-    ALOGD("AAudioServiceStreamFakeHal::pause() send AAUDIO_SERVICE_EVENT_FLUSHED");
-    sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
-    mState = AAUDIO_STREAM_STATE_FLUSHED;
-    return AAUDIO_OK;
-}
-
-aaudio_result_t AAudioServiceStreamFakeHal::close() {
-    aaudio_result_t result = AAUDIO_OK;
-    if (mStreamId != nullptr) {
-        result = fake_hal_close(mStreamId);
-        mStreamId = nullptr;
-    }
-    return result;
-}
-
-void AAudioServiceStreamFakeHal::sendCurrentTimestamp() {
-    int frameCounter = 0;
-    int error = fake_hal_get_frame_counter(mStreamId, &frameCounter);
-    if (error < 0) {
-        ALOGE("AAudioServiceStreamFakeHal::sendCurrentTimestamp() error %d",
-                error);
-    } else if (frameCounter != mPreviousFrameCounter) {
-        AAudioServiceMessage command;
-        command.what = AAudioServiceMessage::code::TIMESTAMP;
-        mFramesRead.update32(frameCounter);
-        command.timestamp.position = mFramesRead.get();
-        ALOGD("AAudioServiceStreamFakeHal::sendCurrentTimestamp() HAL frames = %d, pos = %d",
-                frameCounter, (int)mFramesRead.get());
-        command.timestamp.timestamp = AudioClock::getNanoseconds();
-        mUpMessageQueue->getFifoBuffer()->write(&command, 1);
-        mPreviousFrameCounter = frameCounter;
-    }
-}
-
-// implement Runnable
-void AAudioServiceStreamFakeHal::run() {
-    TimestampScheduler timestampScheduler;
-    timestampScheduler.setBurstPeriod(mFramesPerBurst, mSampleRate);
-    timestampScheduler.start(AudioClock::getNanoseconds());
-    while(mThreadEnabled.load()) {
-        int64_t nextTime = timestampScheduler.nextAbsoluteTime();
-        if (AudioClock::getNanoseconds() >= nextTime) {
-            sendCurrentTimestamp();
-        } else  {
-            // Sleep until it is time to send the next timestamp.
-            AudioClock::sleepUntilNanoTime(nextTime);
-        }
-    }
-}
-
diff --git a/services/oboeservice/AAudioServiceStreamFakeHal.h b/services/oboeservice/AAudioServiceStreamFakeHal.h
deleted file mode 100644
index e9480fb..0000000
--- a/services/oboeservice/AAudioServiceStreamFakeHal.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * Copyright (C) 2016 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_AAUDIO_SERVICE_STREAM_FAKE_HAL_H
-#define AAUDIO_AAUDIO_SERVICE_STREAM_FAKE_HAL_H
-
-#include "AAudioServiceDefinitions.h"
-#include "AAudioServiceStreamBase.h"
-#include "FakeAudioHal.h"
-#include "MonotonicCounter.h"
-#include "AudioEndpointParcelable.h"
-#include "TimestampScheduler.h"
-
-namespace aaudio {
-
-class AAudioServiceStreamFakeHal
-    : public AAudioServiceStreamBase
-    , public Runnable {
-
-public:
-    AAudioServiceStreamFakeHal();
-    virtual ~AAudioServiceStreamFakeHal();
-
-    virtual aaudio_result_t getDescription(AudioEndpointParcelable &parcelable) override;
-
-    virtual aaudio_result_t open(aaudio::AAudioStreamRequest &request,
-                                 aaudio::AAudioStreamConfiguration &configurationOutput) override;
-
-    /**
-     * Start the flow of data.
-     */
-    virtual aaudio_result_t start() override;
-
-    /**
-     * Stop the flow of data such that start() can resume with loss of data.
-     */
-    virtual aaudio_result_t pause() override;
-
-    /**
-     *  Discard any data held by the underlying HAL or Service.
-     */
-    virtual aaudio_result_t flush() override;
-
-    virtual aaudio_result_t close() override;
-
-    void sendCurrentTimestamp();
-
-    virtual void run() override; // to implement Runnable
-
-private:
-    fake_hal_stream_ptr    mStreamId; // Move to HAL
-
-    MonotonicCounter       mFramesWritten;
-    MonotonicCounter       mFramesRead;
-    int                    mHalFileDescriptor = -1;
-    int                    mPreviousFrameCounter = 0;   // from HAL
-
-    aaudio_stream_state_t    mState = AAUDIO_STREAM_STATE_UNINITIALIZED;
-
-    AAudioThread             mAAudioThread;
-    std::atomic<bool>      mThreadEnabled;
-};
-
-} // namespace aaudio
-
-#endif //AAUDIO_AAUDIO_SERVICE_STREAM_FAKE_HAL_H
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.cpp b/services/oboeservice/AAudioServiceStreamMMAP.cpp
new file mode 100644
index 0000000..b70c625
--- /dev/null
+++ b/services/oboeservice/AAudioServiceStreamMMAP.cpp
@@ -0,0 +1,269 @@
+/*
+ * 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 "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <atomic>
+#include <stdint.h>
+
+#include <utils/String16.h>
+#include <media/nbaio/AudioStreamOutSink.h>
+#include <media/MmapStreamInterface.h>
+
+#include "AAudioServiceStreamBase.h"
+#include "AAudioServiceStreamMMAP.h"
+#include "binding/AudioEndpointParcelable.h"
+#include "SharedMemoryProxy.h"
+#include "utility/AAudioUtilities.h"
+
+using namespace android;
+using namespace aaudio;
+
+#define AAUDIO_BUFFER_CAPACITY_MIN    4 * 512
+#define AAUDIO_SAMPLE_RATE_DEFAULT    48000
+
+/**
+ * Stream that uses an MMAP buffer.
+ */
+
+AAudioServiceStreamMMAP::AAudioServiceStreamMMAP()
+        : AAudioServiceStreamBase()
+        , mMmapStreamCallback(new MyMmapStreamCallback(*this))
+        , mPreviousFrameCounter(0)
+        , mMmapStream(nullptr) {
+}
+
+AAudioServiceStreamMMAP::~AAudioServiceStreamMMAP() {
+    close();
+}
+
+aaudio_result_t AAudioServiceStreamMMAP::close() {
+    ALOGD("AAudioServiceStreamMMAP::close() called, %p", mMmapStream.get());
+    mMmapStream.clear(); // TODO review. Is that all we have to do?
+    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_DEFAULT,
+        .flags = AUDIO_FLAG_LOW_LATENCY,
+        .tags = ""
+    };
+    audio_config_base_t config;
+
+    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamBase open returned %d", result);
+        return result;
+    }
+
+    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+    audio_port_handle_t deviceId = configurationInput.getDeviceId();
+
+    ALOGI("open request dump()");
+    request.dump();
+
+    mMmapClient.clientUid = request.getUserId();
+    mMmapClient.clientPid = request.getProcessId();
+    aaudio_direction_t direction = request.getDirection();
+
+    // Fill in config
+    aaudio_audio_format_t aaudioFormat = configurationInput.getAudioFormat();
+    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+        ALOGI("open forcing use of AAUDIO_FORMAT_PCM_I16");
+        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
+    }
+    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);
+    } else if (direction == AAUDIO_DIRECTION_INPUT) {
+        config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
+                            ? AUDIO_CHANNEL_IN_STEREO
+                            : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
+    } else {
+        ALOGE("openMmapStream - invalid direction = %d", direction);
+        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    mMmapClient.packageName.setTo(String16("aaudio_service")); // FIXME what should we do here?
+
+    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);
+    if (status != OK) {
+        ALOGE("openMmapStream returned status %d", status);
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    // 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("%s: createMmapBuffer() returned status %d, return AAUDIO_ERROR_UNAVAILABLE",
+              __FILE__, status);
+        return AAUDIO_ERROR_UNAVAILABLE;
+    } else {
+        ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d",
+              status, mMmapBufferinfo.shared_memory_address,
+              mMmapBufferinfo.buffer_size_frames,
+              mMmapBufferinfo.burst_size_frames);
+    }
+
+    // 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);
+
+    mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
+    mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
+    mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
+    mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
+    mSampleRate = config.sample_rate;
+
+    // Fill in AAudioStreamConfiguration
+    configurationOutput.setSampleRate(mSampleRate);
+    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
+    configurationOutput.setAudioFormat(mAudioFormat);
+    configurationOutput.setDeviceId(deviceId);
+
+    return AAUDIO_OK;
+}
+
+
+/**
+ * Start the flow of data.
+ */
+aaudio_result_t AAudioServiceStreamMMAP::start() {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+    aaudio_result_t result = mMmapStream->start(mMmapClient, &mPortHandle);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", result);
+        processError();
+    } else {
+        result = AAudioServiceStreamBase::start();
+    }
+    return result;
+}
+
+/**
+ * Stop the flow of data such that start() can resume with loss of data.
+ */
+aaudio_result_t AAudioServiceStreamMMAP::pause() {
+    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
+
+    aaudio_result_t result1 = AAudioServiceStreamBase::pause();
+    aaudio_result_t result2 = mMmapStream->stop(mPortHandle);
+    mFramesRead.reset32();
+    return (result1 != AAUDIO_OK) ? result1 : result2;
+}
+
+/**
+ *  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?
+    ALOGD("AAudioServiceStreamMMAP::pause() send AAUDIO_SERVICE_EVENT_FLUSHED");
+    sendServiceEvent(AAUDIO_SERVICE_EVENT_FLUSHED);
+    mState = AAUDIO_STREAM_STATE_FLUSHED;
+    return AAUDIO_OK;
+}
+
+
+aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
+                                                                int64_t *timeNanos) {
+    struct audio_mmap_position position;
+    if (mMmapStream == nullptr) {
+        processError();
+        return AAUDIO_ERROR_NULL;
+    }
+    status_t status = mMmapStream->getMmapPosition(&position);
+    if (status != OK) {
+        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned %d", status);
+        processError();
+        return AAudioConvert_androidToAAudioResult(status);
+    } else {
+        mFramesRead.update32(position.position_frames);
+        *positionFrames = mFramesRead.get();
+        *timeNanos = position.time_nanoseconds;
+    }
+    return AAUDIO_OK;
+}
+
+void AAudioServiceStreamMMAP::onTearDown() {
+    ALOGD("AAudioServiceStreamMMAP::onTearDown() called - TODO");
+};
+
+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, mPortHandle);
+    if (mPortHandle > 0 && mPortHandle != deviceId) {
+        sendServiceEvent(AAUDIO_SERVICE_EVENT_DISCONNECTED);
+    }
+    mPortHandle = 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;
+}
\ No newline at end of file
diff --git a/services/oboeservice/AAudioServiceStreamMMAP.h b/services/oboeservice/AAudioServiceStreamMMAP.h
new file mode 100644
index 0000000..f121c5c
--- /dev/null
+++ b/services/oboeservice/AAudioServiceStreamMMAP.h
@@ -0,0 +1,138 @@
+/*
+ * 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_AAUDIO_SERVICE_STREAM_MMAP_H
+#define AAUDIO_AAUDIO_SERVICE_STREAM_MMAP_H
+
+#include <atomic>
+
+#include <media/audiohal/StreamHalInterface.h>
+#include <media/MmapStreamCallback.h>
+#include <media/MmapStreamInterface.h>
+#include <utils/RefBase.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceStreamBase.h"
+#include "binding/AudioEndpointParcelable.h"
+#include "SharedMemoryProxy.h"
+#include "TimestampScheduler.h"
+#include "utility/MonotonicCounter.h"
+
+namespace aaudio {
+
+    /**
+     * Manage one memory mapped buffer that originated from a HAL.
+     */
+class AAudioServiceStreamMMAP
+    : public AAudioServiceStreamBase
+    , public android::MmapStreamCallback {
+
+public:
+    AAudioServiceStreamMMAP();
+    virtual ~AAudioServiceStreamMMAP();
+
+
+    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;
+
+    /**
+     *  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 close() override;
+
+    /**
+     * 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;
+
+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
+
+    // Interface to the AudioFlinger MMAP support.
+    android::sp<android::MmapStreamInterface> mMmapStream;
+    struct audio_mmap_buffer_info             mMmapBufferinfo;
+    android::MmapStreamInterface::Client      mMmapClient;
+    audio_port_handle_t                       mPortHandle = -1; // TODO review best default
+};
+
+} // namespace aaudio
+
+#endif //AAUDIO_AAUDIO_SERVICE_STREAM_MMAP_H
diff --git a/services/oboeservice/AAudioServiceStreamShared.cpp b/services/oboeservice/AAudioServiceStreamShared.cpp
new file mode 100644
index 0000000..cd9336b
--- /dev/null
+++ b/services/oboeservice/AAudioServiceStreamShared.cpp
@@ -0,0 +1,198 @@
+/*
+ * 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 "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <mutex>
+
+#include <aaudio/AAudio.h>
+
+#include "binding/IAAudioService.h"
+
+#include "binding/AAudioServiceMessage.h"
+#include "AAudioServiceStreamBase.h"
+#include "AAudioServiceStreamShared.h"
+#include "AAudioEndpointManager.h"
+#include "AAudioService.h"
+#include "AAudioServiceEndpoint.h"
+
+using namespace android;
+using namespace aaudio;
+
+#define MIN_BURSTS_PER_BUFFER   2
+#define MAX_BURSTS_PER_BUFFER   32
+
+AAudioServiceStreamShared::AAudioServiceStreamShared(AAudioService &audioService)
+    : mAudioService(audioService)
+    {
+}
+
+AAudioServiceStreamShared::~AAudioServiceStreamShared() {
+    close();
+}
+
+aaudio_result_t AAudioServiceStreamShared::open(const aaudio::AAudioStreamRequest &request,
+                     aaudio::AAudioStreamConfiguration &configurationOutput)  {
+
+    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamBase open returned %d", result);
+        return result;
+    }
+
+    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
+    int32_t deviceId = configurationInput.getDeviceId();
+    aaudio_direction_t direction = request.getDirection();
+
+    ALOGD("AAudioServiceStreamShared::open(), direction = %d", direction);
+    AAudioEndpointManager &mEndpointManager = AAudioEndpointManager::getInstance();
+    mServiceEndpoint = mEndpointManager.findEndpoint(mAudioService, deviceId, direction);
+    ALOGD("AAudioServiceStreamShared::open(), mServiceEndPoint = %p", mServiceEndpoint);
+    if (mServiceEndpoint == nullptr) {
+        return AAUDIO_ERROR_UNAVAILABLE;
+    }
+
+    // Is the request compatible with the shared endpoint?
+    mAudioFormat = configurationInput.getAudioFormat();
+    if (mAudioFormat == AAUDIO_FORMAT_UNSPECIFIED) {
+        mAudioFormat = AAUDIO_FORMAT_PCM_FLOAT;
+    } else if (mAudioFormat != AAUDIO_FORMAT_PCM_FLOAT) {
+        return AAUDIO_ERROR_INVALID_FORMAT;
+    }
+
+    mSampleRate = configurationInput.getSampleRate();
+    if (mSampleRate == AAUDIO_FORMAT_UNSPECIFIED) {
+        mSampleRate = mServiceEndpoint->getSampleRate();
+    } else if (mSampleRate != mServiceEndpoint->getSampleRate()) {
+        return AAUDIO_ERROR_INVALID_RATE;
+    }
+
+    mSamplesPerFrame = configurationInput.getSamplesPerFrame();
+    if (mSamplesPerFrame == AAUDIO_FORMAT_UNSPECIFIED) {
+        mSamplesPerFrame = mServiceEndpoint->getSamplesPerFrame();
+    } else if (mSamplesPerFrame != mServiceEndpoint->getSamplesPerFrame()) {
+        return AAUDIO_ERROR_OUT_OF_RANGE;
+    }
+
+    // Determine this stream's shared memory buffer capacity.
+    mFramesPerBurst = mServiceEndpoint->getFramesPerBurst();
+    int32_t minCapacityFrames = configurationInput.getBufferCapacity();
+    int32_t numBursts = (minCapacityFrames + mFramesPerBurst - 1) / mFramesPerBurst;
+    if (numBursts < MIN_BURSTS_PER_BUFFER) {
+        numBursts = MIN_BURSTS_PER_BUFFER;
+    } else if (numBursts > MAX_BURSTS_PER_BUFFER) {
+        numBursts = MAX_BURSTS_PER_BUFFER;
+    }
+    mCapacityInFrames = numBursts * mFramesPerBurst;
+    ALOGD("AAudioServiceStreamShared::open(), mCapacityInFrames = %d", mCapacityInFrames);
+
+    // Create audio data shared memory buffer for client.
+    mAudioDataQueue = new SharedRingBuffer();
+    mAudioDataQueue->allocate(calculateBytesPerFrame(), mCapacityInFrames);
+
+    // Fill in configuration for client.
+    configurationOutput.setSampleRate(mSampleRate);
+    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
+    configurationOutput.setAudioFormat(mAudioFormat);
+    configurationOutput.setDeviceId(deviceId);
+
+    mServiceEndpoint->registerStream(this);
+
+    return AAUDIO_OK;
+}
+
+/**
+ * Start the flow of audio data.
+ *
+ * An AAUDIO_SERVICE_EVENT_STARTED will be sent to the client when complete.
+ */
+aaudio_result_t AAudioServiceStreamShared::start()  {
+    // Add this stream to the mixer.
+    aaudio_result_t result = mServiceEndpoint->startStream(this);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::start() mServiceEndpoint returned %d", result);
+        processError();
+    } else {
+        result = AAudioServiceStreamBase::start();
+    }
+    return AAUDIO_OK;
+}
+
+/**
+ * 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()  {
+    // Add this stream to the mixer.
+    aaudio_result_t result = mServiceEndpoint->stopStream(this);
+    if (result != AAUDIO_OK) {
+        ALOGE("AAudioServiceStreamShared::stop() mServiceEndpoint returned %d", result);
+        processError();
+    } else {
+        result = AAudioServiceStreamBase::start();
+    }
+    return AAUDIO_OK;
+}
+
+/**
+ *  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()  {
+    // TODO make sure we are paused
+    return AAUDIO_OK;
+}
+
+aaudio_result_t AAudioServiceStreamShared::close()  {
+    pause();
+    // TODO wait for pause() to synchronize
+    mServiceEndpoint->unregisterStream(this);
+    mServiceEndpoint->close();
+    mServiceEndpoint = nullptr;
+    return AAudioServiceStreamBase::close();
+}
+
+/**
+ * Get an immutable description of the data queue created by this service.
+ */
+aaudio_result_t AAudioServiceStreamShared::getDownDataDescription(AudioEndpointParcelable &parcelable)
+{
+    // Gather information on the data queue.
+    mAudioDataQueue->fillParcelable(parcelable,
+                                    parcelable.mDownDataQueueParcelable);
+    parcelable.mDownDataQueueParcelable.setFramesPerBurst(getFramesPerBurst());
+    return AAUDIO_OK;
+}
+
+void AAudioServiceStreamShared::onStop() {
+}
+
+void AAudioServiceStreamShared::onDisconnect() {
+    mServiceEndpoint->close();
+    mServiceEndpoint = nullptr;
+}
+
+
+aaudio_result_t AAudioServiceStreamShared::getFreeRunningPosition(int64_t *positionFrames,
+                                                                int64_t *timeNanos) {
+    *positionFrames = mAudioDataQueue->getFifoBuffer()->getReadCounter();
+    *timeNanos = AudioClock::getNanoseconds();
+    return AAUDIO_OK;
+}
diff --git a/services/oboeservice/AAudioServiceStreamShared.h b/services/oboeservice/AAudioServiceStreamShared.h
new file mode 100644
index 0000000..f6df7ce
--- /dev/null
+++ b/services/oboeservice/AAudioServiceStreamShared.h
@@ -0,0 +1,98 @@
+/*
+ * 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_AAUDIO_SERVICE_STREAM_SHARED_H
+#define AAUDIO_AAUDIO_SERVICE_STREAM_SHARED_H
+
+#include "fifo/FifoBuffer.h"
+#include "binding/AAudioServiceMessage.h"
+#include "binding/AAudioStreamRequest.h"
+#include "binding/AAudioStreamConfiguration.h"
+
+#include "AAudioService.h"
+#include "AAudioServiceStreamBase.h"
+
+namespace aaudio {
+
+// We expect the queue to only have a few commands.
+// This should be way more than we need.
+#define QUEUE_UP_CAPACITY_COMMANDS (128)
+
+class AAudioEndpointManager;
+class AAudioServiceEndpoint;
+class SharedRingBuffer;
+
+/**
+ * One of these is created for every MODE_SHARED stream in the AAudioService.
+ *
+ * Each Shared stream will register itself with an AAudioServiceEndpoint when it is opened.
+ */
+class AAudioServiceStreamShared : public AAudioServiceStreamBase {
+
+public:
+    AAudioServiceStreamShared(android::AAudioService &aAudioService);
+    virtual ~AAudioServiceStreamShared();
+
+    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;
+
+    /**
+     *  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 close() override;
+
+    android::FifoBuffer *getDataFifoBuffer() { return mAudioDataQueue->getFifoBuffer(); }
+
+    void onStop();
+
+    void onDisconnect();
+
+protected:
+
+    aaudio_result_t getDownDataDescription(AudioEndpointParcelable &parcelable) override;
+
+    aaudio_result_t getFreeRunningPosition(int64_t *positionFrames, int64_t *timeNanos) override;
+
+private:
+    android::AAudioService  &mAudioService;
+    AAudioServiceEndpoint   *mServiceEndpoint = nullptr;
+    SharedRingBuffer        *mAudioDataQueue;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_AAUDIO_SERVICE_STREAM_SHARED_H
diff --git a/services/oboeservice/AAudioThread.cpp b/services/oboeservice/AAudioThread.cpp
index f5e5784..b1b563d 100644
--- a/services/oboeservice/AAudioThread.cpp
+++ b/services/oboeservice/AAudioThread.cpp
@@ -21,14 +21,17 @@
 #include <pthread.h>
 
 #include <aaudio/AAudioDefinitions.h>
+#include <utility/AAudioUtilities.h>
 
 #include "AAudioThread.h"
 
 using namespace aaudio;
 
 
-AAudioThread::AAudioThread() {
-    // mThread is a pthread_t of unknown size so we need memset.
+AAudioThread::AAudioThread()
+    : mRunnable(nullptr)
+    , mHasThread(false) {
+    // mThread is a pthread_t of unknown size so we need memset().
     memset(&mThread, 0, sizeof(mThread));
 }
 
@@ -50,14 +53,16 @@
 
 aaudio_result_t AAudioThread::start(Runnable *runnable) {
     if (mHasThread) {
+        ALOGE("AAudioThread::start() - mHasThread.load() already true");
         return AAUDIO_ERROR_INVALID_STATE;
     }
-    mRunnable = runnable; // TODO use atomic?
+    // mRunnable will be read by the new thread when it starts.
+    // pthread_create() forces a memory synchronization so mRunnable does not need to be atomic.
+    mRunnable = runnable;
     int err = pthread_create(&mThread, nullptr, AAudioThread_internalThreadProc, this);
     if (err != 0) {
-        ALOGE("AAudioThread::pthread_create() returned %d", err);
-        // TODO convert errno to aaudio_result_t
-        return AAUDIO_ERROR_INTERNAL;
+        ALOGE("AAudioThread::start() - pthread_create() returned %d %s", err, strerror(err));
+        return AAudioConvert_androidToAAudioResult(-err);
     } else {
         mHasThread = true;
         return AAUDIO_OK;
@@ -70,7 +75,11 @@
     }
     int err = pthread_join(mThread, nullptr);
     mHasThread = false;
-    // TODO convert errno to aaudio_result_t
-    return err ? AAUDIO_ERROR_INTERNAL : AAUDIO_OK;
+    if (err != 0) {
+        ALOGE("AAudioThread::stop() - pthread_join() returned %d %s", err, strerror(err));
+        return AAudioConvert_androidToAAudioResult(-err);
+    } else {
+        return AAUDIO_OK;
+    }
 }
 
diff --git a/services/oboeservice/AAudioThread.h b/services/oboeservice/AAudioThread.h
index a5d43a4..dd9f640 100644
--- a/services/oboeservice/AAudioThread.h
+++ b/services/oboeservice/AAudioThread.h
@@ -24,16 +24,20 @@
 
 namespace aaudio {
 
+/**
+ * Abstract class similar to Java Runnable.
+ */
 class Runnable {
 public:
     Runnable() {};
     virtual ~Runnable() = default;
 
-    virtual void run() {}
+    virtual void run() = 0;
 };
 
 /**
- * Abstraction for a host thread.
+ * Abstraction for a host dependent thread.
+ * TODO Consider using Android "Thread" class or std::thread instead.
  */
 class AAudioThread
 {
@@ -62,9 +66,9 @@
     void dispatch(); // called internally from 'C' thread wrapper
 
 private:
-    Runnable*          mRunnable = nullptr; // TODO make atomic with memory barrier?
-    bool               mHasThread = false;
-    pthread_t          mThread; // initialized in constructor
+    Runnable    *mRunnable;
+    bool         mHasThread;
+    pthread_t    mThread; // initialized in constructor
 
 };
 
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
index 5cd9121..a9c80ae 100644
--- a/services/oboeservice/Android.mk
+++ b/services/oboeservice/Android.mk
@@ -3,52 +3,54 @@
 # AAudio Service
 include $(CLEAR_VARS)
 
-LOCAL_MODULE := aaudioservice
+LOCAL_MODULE := libaaudioservice
 LOCAL_MODULE_TAGS := optional
 
 LIBAAUDIO_DIR := ../../media/libaaudio
 LIBAAUDIO_SRC_DIR := $(LIBAAUDIO_DIR)/src
 
 LOCAL_C_INCLUDES := \
+    $(TOPDIR)frameworks/av/services/audioflinger \
     $(call include-path-for, audio-utils) \
     frameworks/native/include \
     system/core/base/include \
     $(TOP)/frameworks/native/media/libaaudio/include/include \
     $(TOP)/frameworks/av/media/libaaudio/include \
+    $(TOP)/frameworks/av/media/utils/include \
     frameworks/native/include \
     $(TOP)/external/tinyalsa/include \
-    $(TOP)/frameworks/av/media/libaaudio/src \
-    $(TOP)/frameworks/av/media/libaaudio/src/binding \
-    $(TOP)/frameworks/av/media/libaaudio/src/client \
-    $(TOP)/frameworks/av/media/libaaudio/src/core \
-    $(TOP)/frameworks/av/media/libaaudio/src/fifo \
-    $(TOP)/frameworks/av/media/libaaudio/src/utility
+    $(TOP)/frameworks/av/media/libaaudio/src
 
-# TODO These could be in a libaaudio_common library
 LOCAL_SRC_FILES += \
     $(LIBAAUDIO_SRC_DIR)/utility/HandleTracker.cpp \
-    $(LIBAAUDIO_SRC_DIR)/utility/AAudioUtilities.cpp \
-    $(LIBAAUDIO_SRC_DIR)/fifo/FifoBuffer.cpp \
-    $(LIBAAUDIO_SRC_DIR)/fifo/FifoControllerBase.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/SharedMemoryParcelable.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/SharedRegionParcelable.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/RingBufferParcelable.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/AudioEndpointParcelable.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/AAudioStreamRequest.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/AAudioStreamConfiguration.cpp \
-    $(LIBAAUDIO_SRC_DIR)/binding/IAAudioService.cpp \
+    SharedMemoryProxy.cpp \
     SharedRingBuffer.cpp \
-    FakeAudioHal.cpp \
+    AAudioEndpointManager.cpp \
+    AAudioMixer.cpp \
     AAudioService.cpp \
+    AAudioServiceEndpoint.cpp \
     AAudioServiceStreamBase.cpp \
-    AAudioServiceStreamFakeHal.cpp \
+    AAudioServiceStreamMMAP.cpp \
+    AAudioServiceStreamShared.cpp \
     TimestampScheduler.cpp \
-    AAudioServiceMain.cpp \
     AAudioThread.cpp
 
+LOCAL_MULTILIB := $(AUDIOSERVER_MULTILIB)
+
+# LOCAL_CFLAGS += -fvisibility=hidden
 LOCAL_CFLAGS += -Wno-unused-parameter
 LOCAL_CFLAGS += -Wall -Werror
 
-LOCAL_SHARED_LIBRARIES :=  libbinder libcutils libutils liblog libtinyalsa
+LOCAL_SHARED_LIBRARIES :=  \
+    libaaudio \
+    libaudioflinger \
+    libbinder \
+    libcutils \
+    libmediautils \
+    libutils \
+    liblog \
+    libtinyalsa
 
-include $(BUILD_EXECUTABLE)
+include $(BUILD_SHARED_LIBRARY)
+
+
diff --git a/services/oboeservice/FakeAudioHal.cpp b/services/oboeservice/FakeAudioHal.cpp
deleted file mode 100644
index 122671e..0000000
--- a/services/oboeservice/FakeAudioHal.cpp
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-/**
- * Simple fake HAL that supports ALSA MMAP/NOIRQ mode.
- */
-
-#include <iostream>
-#include <math.h>
-#include <limits>
-#include <string.h>
-#include <unistd.h>
-
-#include <sound/asound.h>
-
-#include "tinyalsa/asoundlib.h"
-
-#include "FakeAudioHal.h"
-
-//using namespace aaudio;
-
-using sample_t = int16_t;
-using std::cout;
-using std::endl;
-
-#undef SNDRV_PCM_IOCTL_SYNC_PTR
-#define SNDRV_PCM_IOCTL_SYNC_PTR 0xc0884123
-#define PCM_ERROR_MAX 128
-
-const int SAMPLE_RATE = 48000;       // Hz
-const int CHANNEL_COUNT = 2;
-
-struct pcm {
-    int fd;
-    unsigned int flags;
-    int running:1;
-    int prepared:1;
-    int underruns;
-    unsigned int buffer_size;
-    unsigned int boundary;
-    char error[PCM_ERROR_MAX];
-    struct pcm_config config;
-    struct snd_pcm_mmap_status *mmap_status;
-    struct snd_pcm_mmap_control *mmap_control;
-    struct snd_pcm_sync_ptr *sync_ptr;
-    void *mmap_buffer;
-    unsigned int noirq_frames_per_msec;
-    int wait_for_avail_min;
-};
-
-static int pcm_sync_ptr(struct pcm *pcm, int flags) {
-    if (pcm->sync_ptr) {
-        pcm->sync_ptr->flags = flags;
-        if (ioctl(pcm->fd, SNDRV_PCM_IOCTL_SYNC_PTR, pcm->sync_ptr) < 0)
-            return -1;
-    }
-    return 0;
-}
-
-int pcm_get_hw_ptr(struct pcm* pcm, unsigned int* hw_ptr) {
-    if (!hw_ptr || !pcm) return -EINVAL;
-
-    int result = pcm_sync_ptr(pcm, SNDRV_PCM_SYNC_PTR_HWSYNC);
-    if (!result) {
-        *hw_ptr = pcm->sync_ptr->s.status.hw_ptr;
-    }
-
-    return result;
-}
-
-typedef struct stream_tracker {
-    struct pcm * pcm;
-    int          framesPerBurst;
-    sample_t   * hwBuffer;
-    int32_t      capacityInFrames;
-    int32_t      capacityInBytes;
-} stream_tracker_t;
-
-#define FRAMES_PER_BURST_QUALCOMM 192
-#define FRAMES_PER_BURST_NVIDIA   128
-
-int fake_hal_open(int card_id, int device_id,
-                  int frameCapacity,
-                  fake_hal_stream_ptr *streamPP) {
-    int framesPerBurst = FRAMES_PER_BURST_QUALCOMM; // TODO update as needed
-    int periodCountRequested = frameCapacity / framesPerBurst;
-    int periodCount = 32;
-    unsigned int offset1;
-    unsigned int frames1;
-    void *area = nullptr;
-    int mmapAvail = 0;
-
-    // Try to match requested size with a power of 2.
-    while (periodCount < periodCountRequested && periodCount < 1024) {
-        periodCount *= 2;
-    }
-    std::cout << "fake_hal_open() requested frameCapacity = " << frameCapacity << std::endl;
-    std::cout << "fake_hal_open() periodCountRequested = " << periodCountRequested << std::endl;
-    std::cout << "fake_hal_open() periodCount = " << periodCount << std::endl;
-
-    // Configuration for an ALSA stream.
-    pcm_config cfg;
-    memset(&cfg, 0, sizeof(cfg));
-    cfg.channels = CHANNEL_COUNT;
-    cfg.format = PCM_FORMAT_S16_LE;
-    cfg.rate = SAMPLE_RATE;
-    cfg.period_count = periodCount;
-    cfg.period_size = framesPerBurst;
-    cfg.start_threshold = 0; // for NOIRQ, should just start, was     framesPerBurst;
-    cfg.stop_threshold = INT32_MAX;
-    cfg.silence_size = 0;
-    cfg.silence_threshold = 0;
-    cfg.avail_min = framesPerBurst;
-
-    stream_tracker_t *streamTracker = (stream_tracker_t *) malloc(sizeof(stream_tracker_t));
-    if (streamTracker == nullptr) {
-        return -1;
-    }
-    memset(streamTracker, 0, sizeof(stream_tracker_t));
-
-    streamTracker->pcm = pcm_open(card_id, device_id, PCM_OUT | PCM_MMAP | PCM_NOIRQ, &cfg);
-    if (streamTracker->pcm == nullptr) {
-        cout << "Could not open device." << endl;
-        free(streamTracker);
-        return -1;
-    }
-
-    streamTracker->framesPerBurst = cfg.period_size; // Get from ALSA
-    streamTracker->capacityInFrames = pcm_get_buffer_size(streamTracker->pcm);
-    streamTracker->capacityInBytes = pcm_frames_to_bytes(streamTracker->pcm, streamTracker->capacityInFrames);
-    std::cout << "fake_hal_open() streamTracker->framesPerBurst = " << streamTracker->framesPerBurst << std::endl;
-    std::cout << "fake_hal_open() streamTracker->capacityInFrames = " << streamTracker->capacityInFrames << std::endl;
-
-    if (pcm_is_ready(streamTracker->pcm) < 0) {
-        cout << "Device is not ready." << endl;
-        goto error;
-    }
-
-    if (pcm_prepare(streamTracker->pcm) < 0) {
-        cout << "Device could not be prepared." << endl;
-        cout << "For Marlin, please enter:" << endl;
-        cout << "   adb shell" << endl;
-        cout << "   tinymix \"QUAT_MI2S_RX Audio Mixer MultiMedia8\" 1" << endl;
-        goto error;
-    }
-    mmapAvail = pcm_mmap_avail(streamTracker->pcm);
-    if (mmapAvail <= 0) {
-        cout << "fake_hal_open() mmap_avail is <=0" << endl;
-        goto error;
-    }
-    cout << "fake_hal_open() mmap_avail = " << mmapAvail << endl;
-
-    // Where is the memory mapped area?
-    if (pcm_mmap_begin(streamTracker->pcm, &area, &offset1, &frames1) < 0)  {
-        cout << "fake_hal_open() pcm_mmap_begin failed" << endl;
-        goto error;
-    }
-
-    // Clear the buffer.
-    memset((sample_t*) area, 0, streamTracker->capacityInBytes);
-    streamTracker->hwBuffer = (sample_t*) area;
-    streamTracker->hwBuffer[0] = 32000; // impulse
-
-    // Prime the buffer so it can start.
-    if (pcm_mmap_commit(streamTracker->pcm, 0, framesPerBurst) < 0) {
-        cout << "fake_hal_open() pcm_mmap_commit failed" << endl;
-        goto error;
-    }
-
-    *streamPP = streamTracker;
-    return 1;
-
-error:
-    fake_hal_close(streamTracker);
-    return -1;
-}
-
-int fake_hal_get_mmap_info(fake_hal_stream_ptr stream, mmap_buffer_info *info) {
-    stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
-    info->fd = streamTracker->pcm->fd; // TODO use tinyalsa function
-    info->hw_buffer = streamTracker->hwBuffer;
-    info->burst_size_in_frames = streamTracker->framesPerBurst;
-    info->buffer_capacity_in_frames = streamTracker->capacityInFrames;
-    info->buffer_capacity_in_bytes = streamTracker->capacityInBytes;
-    info->sample_rate = SAMPLE_RATE;
-    info->channel_count = CHANNEL_COUNT;
-    return 0;
-}
-
-int fake_hal_start(fake_hal_stream_ptr stream) {
-    stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
-    if (pcm_start(streamTracker->pcm) < 0) {
-        cout << "fake_hal_start failed" << endl;
-        return -1;
-    }
-    return 0;
-}
-
-int fake_hal_pause(fake_hal_stream_ptr stream) {
-    stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
-    if (pcm_stop(streamTracker->pcm) < 0) {
-        cout << "fake_hal_stop failed" << endl;
-        return -1;
-    }
-    return 0;
-}
-
-int fake_hal_get_frame_counter(fake_hal_stream_ptr stream, int *frame_counter) {
-    stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
-    if (pcm_get_hw_ptr(streamTracker->pcm, (unsigned int *)frame_counter) < 0) {
-        cout << "fake_hal_get_frame_counter failed" << endl;
-        return -1;
-    }
-    return 0;
-}
-
-int fake_hal_close(fake_hal_stream_ptr stream) {
-    stream_tracker_t *streamTracker = (stream_tracker_t *) stream;
-    pcm_close(streamTracker->pcm);
-    free(streamTracker);
-    return 0;
-}
-
diff --git a/services/oboeservice/FakeAudioHal.h b/services/oboeservice/FakeAudioHal.h
deleted file mode 100644
index d3aa4e8..0000000
--- a/services/oboeservice/FakeAudioHal.h
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2016 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.
- */
-
-/**
- * Simple fake HAL that supports ALSA MMAP/NOIRQ mode.
- */
-
-#ifndef FAKE_AUDIO_HAL_H
-#define FAKE_AUDIO_HAL_H
-
-//namespace aaudio {
-
-using sample_t = int16_t;
-struct mmap_buffer_info {
-    int       fd;
-    int32_t   burst_size_in_frames;
-    int32_t   buffer_capacity_in_frames;
-    int32_t   buffer_capacity_in_bytes;
-    int32_t   sample_rate;
-    int32_t   channel_count;
-    sample_t *hw_buffer;
-};
-
-typedef void *fake_hal_stream_ptr;
-
-//extern "C"
-//{
-
-int fake_hal_open(int card_id, int device_id,
-                  int frameCapacity,
-                  fake_hal_stream_ptr *stream_pp);
-
-int fake_hal_get_mmap_info(fake_hal_stream_ptr stream, mmap_buffer_info *info);
-
-int fake_hal_start(fake_hal_stream_ptr stream);
-
-int fake_hal_pause(fake_hal_stream_ptr stream);
-
-int fake_hal_get_frame_counter(fake_hal_stream_ptr stream, int *frame_counter);
-
-int fake_hal_close(fake_hal_stream_ptr stream);
-
-//} /* "C" */
-
-//} /* namespace aaudio */
-
-#endif // FAKE_AUDIO_HAL_H
diff --git a/services/oboeservice/SharedMemoryProxy.cpp b/services/oboeservice/SharedMemoryProxy.cpp
new file mode 100644
index 0000000..83ae1d4
--- /dev/null
+++ b/services/oboeservice/SharedMemoryProxy.cpp
@@ -0,0 +1,83 @@
+/*
+ * 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 "AAudioService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <aaudio/AAudioDefinitions.h>
+#include "SharedMemoryProxy.h"
+
+using namespace android;
+using namespace aaudio;
+
+SharedMemoryProxy::~SharedMemoryProxy()
+{
+    if (mOriginalSharedMemory != nullptr) {
+        munmap(mOriginalSharedMemory, mSharedMemorySizeInBytes);
+        mOriginalSharedMemory = nullptr;
+    }
+    if (mProxySharedMemory != nullptr) {
+        munmap(mProxySharedMemory, mSharedMemorySizeInBytes);
+        close(mProxyFileDescriptor);
+        mProxySharedMemory = nullptr;
+    }
+}
+
+aaudio_result_t SharedMemoryProxy::open(int originalFD, int32_t capacityInBytes) {
+    mOriginalFileDescriptor = originalFD;
+    mSharedMemorySizeInBytes = capacityInBytes;
+
+    mProxyFileDescriptor = ashmem_create_region("AAudioProxyDataBuffer", mSharedMemorySizeInBytes);
+    if (mProxyFileDescriptor < 0) {
+        ALOGE("SharedMemoryProxy::open() ashmem_create_region() failed %d", errno);
+        return AAUDIO_ERROR_INTERNAL;
+    }
+    int err = ashmem_set_prot_region(mProxyFileDescriptor, PROT_READ|PROT_WRITE);
+    if (err < 0) {
+        ALOGE("SharedMemoryProxy::open() ashmem_set_prot_region() failed %d", errno);
+        close(mProxyFileDescriptor);
+        mProxyFileDescriptor = -1;
+        return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
+    }
+
+    // Get original memory address.
+    mOriginalSharedMemory = (uint8_t *) mmap(0, mSharedMemorySizeInBytes,
+                         PROT_READ|PROT_WRITE,
+                         MAP_SHARED,
+                         mOriginalFileDescriptor, 0);
+    if (mOriginalSharedMemory == MAP_FAILED) {
+        ALOGE("SharedMemoryProxy::open() original mmap(%d) failed %d (%s)",
+                mOriginalFileDescriptor, errno, strerror(errno));
+        return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
+    }
+
+    // Map the fd to the same memory addresses.
+    mProxySharedMemory = (uint8_t *) mmap(mOriginalSharedMemory, mSharedMemorySizeInBytes,
+                         PROT_READ|PROT_WRITE,
+                         MAP_SHARED,
+                         mProxyFileDescriptor, 0);
+    if (mProxySharedMemory != mOriginalSharedMemory) {
+        ALOGE("SharedMemoryProxy::open() proxy mmap(%d) failed %d", mProxyFileDescriptor, errno);
+        munmap(mOriginalSharedMemory, mSharedMemorySizeInBytes);
+        mOriginalSharedMemory = nullptr;
+        close(mProxyFileDescriptor);
+        mProxyFileDescriptor = -1;
+        return AAUDIO_ERROR_INTERNAL; // TODO convert errno to a better AAUDIO_ERROR;
+    }
+
+    return AAUDIO_OK;
+}
diff --git a/services/oboeservice/SharedMemoryProxy.h b/services/oboeservice/SharedMemoryProxy.h
new file mode 100644
index 0000000..99bfdea
--- /dev/null
+++ b/services/oboeservice/SharedMemoryProxy.h
@@ -0,0 +1,53 @@
+/*
+ * 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_SHARED_MEMORY_PROXY_H
+#define AAUDIO_SHARED_MEMORY_PROXY_H
+
+#include <stdint.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+#include <aaudio/AAudioDefinitions.h>
+
+namespace aaudio {
+
+/**
+ * Proxy for sharing memory between two file descriptors.
+ */
+class SharedMemoryProxy {
+public:
+    SharedMemoryProxy() {}
+
+    ~SharedMemoryProxy();
+
+    aaudio_result_t open(int fd, int32_t capacityInBytes);
+
+    int getFileDescriptor() const {
+        return mProxyFileDescriptor;
+    }
+
+private:
+    int            mOriginalFileDescriptor = -1;
+    int            mProxyFileDescriptor = -1;
+    uint8_t       *mOriginalSharedMemory = nullptr;
+    uint8_t       *mProxySharedMemory = nullptr;
+    int32_t        mSharedMemorySizeInBytes = 0;
+};
+
+} /* namespace aaudio */
+
+#endif //AAUDIO_SHARED_MEMORY_PROXY_H
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
index 9ac8fdf..efcc9d6 100644
--- a/services/oboeservice/SharedRingBuffer.cpp
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -18,11 +18,8 @@
 //#define LOG_NDEBUG 0
 #include <utils/Log.h>
 
-#include "AudioClock.h"
-#include "AudioEndpointParcelable.h"
-
-//#include "AAudioServiceStreamBase.h"
-//#include "AAudioServiceStreamFakeHal.h"
+#include "binding/RingBufferParcelable.h"
+#include "binding/AudioEndpointParcelable.h"
 
 #include "SharedRingBuffer.h"
 
diff --git a/services/oboeservice/SharedRingBuffer.h b/services/oboeservice/SharedRingBuffer.h
index 75f138b..a2c3766 100644
--- a/services/oboeservice/SharedRingBuffer.h
+++ b/services/oboeservice/SharedRingBuffer.h
@@ -22,8 +22,8 @@
 #include <sys/mman.h>
 
 #include "fifo/FifoBuffer.h"
-#include "RingBufferParcelable.h"
-#include "AudioEndpointParcelable.h"
+#include "binding/RingBufferParcelable.h"
+#include "binding/AudioEndpointParcelable.h"
 
 namespace aaudio {
 
@@ -41,22 +41,22 @@
 
     virtual ~SharedRingBuffer();
 
-    aaudio_result_t allocate(fifo_frames_t bytesPerFrame, fifo_frames_t capacityInFrames);
+    aaudio_result_t allocate(android::fifo_frames_t bytesPerFrame, android::fifo_frames_t capacityInFrames);
 
     void fillParcelable(AudioEndpointParcelable &endpointParcelable,
                         RingBufferParcelable &ringBufferParcelable);
 
-    FifoBuffer * getFifoBuffer() {
+    android::FifoBuffer * getFifoBuffer() {
         return mFifoBuffer;
     }
 
 private:
-    int            mFileDescriptor = -1;
-    FifoBuffer   * mFifoBuffer = nullptr;
-    uint8_t      * mSharedMemory = nullptr;
-    int32_t        mSharedMemorySizeInBytes = 0;
-    int32_t        mDataMemorySizeInBytes = 0;
-    fifo_frames_t  mCapacityInFrames = 0;
+    int                    mFileDescriptor = -1;
+    android::FifoBuffer   *mFifoBuffer = nullptr;
+    uint8_t               *mSharedMemory = nullptr;
+    int32_t                mSharedMemorySizeInBytes = 0;
+    int32_t                mDataMemorySizeInBytes = 0;
+    android::fifo_frames_t mCapacityInFrames = 0;
 };
 
 } /* namespace aaudio */
diff --git a/services/oboeservice/TimestampScheduler.h b/services/oboeservice/TimestampScheduler.h
index 91a2477..325bee4 100644
--- a/services/oboeservice/TimestampScheduler.h
+++ b/services/oboeservice/TimestampScheduler.h
@@ -17,15 +17,8 @@
 #ifndef AAUDIO_TIMESTAMP_SCHEDULER_H
 #define AAUDIO_TIMESTAMP_SCHEDULER_H
 
-
-
-#include "IAAudioService.h"
-#include "AAudioServiceDefinitions.h"
-#include "AudioStream.h"
-#include "fifo/FifoBuffer.h"
-#include "SharedRingBuffer.h"
-#include "AudioEndpointParcelable.h"
-#include "utility/AudioClock.h"
+#include <aaudio/AAudioDefinitions.h>
+#include <utility/AudioClock.h>
 
 namespace aaudio {
 
@@ -47,8 +40,7 @@
     void start(int64_t startTime);
 
     /**
-     * Calculate the next time that the read position should be
-     * measured.
+     * Calculate the next time that the read position should be measured.
      */
     int64_t nextAbsoluteTime();
 
@@ -68,8 +60,8 @@
 private:
     // Start with an arbitrary default so we do not divide by zero.
     int64_t mBurstPeriod = AAUDIO_NANOS_PER_MILLISECOND;
-    int64_t mStartTime;
-    int64_t mLastTime;
+    int64_t mStartTime = 0;
+    int64_t mLastTime = 0;
 };
 
 } /* namespace aaudio */