OboeService: initial commit

This builds a standalone service that is easy to test.

Bug: 33269952
Test: test_oboe_api

Change-Id: I1890b1b974e728c2c0c15e24aa02121c2774bd56
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/services/oboeservice/Android.mk b/services/oboeservice/Android.mk
new file mode 100644
index 0000000..07b4d76
--- /dev/null
+++ b/services/oboeservice/Android.mk
@@ -0,0 +1,52 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Oboe Service
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := oboeservice
+LOCAL_MODULE_TAGS := optional
+
+LIBOBOE_DIR := ../../media/liboboe
+LIBOBOE_SRC_DIR := $(LIBOBOE_DIR)/src
+
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/native/include \
+    system/core/base/include \
+    $(TOP)/frameworks/native/media/liboboe/include/include \
+    $(TOP)/frameworks/av/media/liboboe/include \
+    frameworks/native/include \
+    $(TOP)/external/tinyalsa/include \
+    $(TOP)/frameworks/av/media/liboboe/src \
+    $(TOP)/frameworks/av/media/liboboe/src/binding \
+    $(TOP)/frameworks/av/media/liboboe/src/client \
+    $(TOP)/frameworks/av/media/liboboe/src/core \
+    $(TOP)/frameworks/av/media/liboboe/src/fifo \
+    $(TOP)/frameworks/av/media/liboboe/src/utility
+
+# TODO These could be in a liboboe_common library
+LOCAL_SRC_FILES += \
+    $(LIBOBOE_SRC_DIR)/utility/HandleTracker.cpp \
+    $(LIBOBOE_SRC_DIR)/utility/OboeUtilities.cpp \
+    $(LIBOBOE_SRC_DIR)/fifo/FifoBuffer.cpp \
+    $(LIBOBOE_SRC_DIR)/fifo/FifoControllerBase.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/SharedMemoryParcelable.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/SharedRegionParcelable.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/RingBufferParcelable.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/AudioEndpointParcelable.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/OboeStreamRequest.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/OboeStreamConfiguration.cpp \
+    $(LIBOBOE_SRC_DIR)/binding/IOboeAudioService.cpp \
+    SharedRingBuffer.cpp \
+    FakeAudioHal.cpp \
+    OboeAudioService.cpp \
+    OboeServiceStreamBase.cpp \
+    OboeServiceStreamFakeHal.cpp \
+    OboeServiceMain.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+
+LOCAL_SHARED_LIBRARIES :=  libbinder libcutils libutils liblog libtinyalsa
+
+include $(BUILD_EXECUTABLE)
diff --git a/services/oboeservice/FakeAudioHal.cpp b/services/oboeservice/FakeAudioHal.cpp
new file mode 100644
index 0000000..7fa2eef
--- /dev/null
+++ b/services/oboeservice/FakeAudioHal.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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>
+
+#define __force
+#define __bitwise
+#define __user
+#include <sound/asound.h>
+
+#include "tinyalsa/asoundlib.h"
+
+#include "FakeAudioHal.h"
+
+//using namespace oboe;
+
+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, fake_hal_stream_ptr *streamPP) {
+    int framesPerBurst = FRAMES_PER_BURST_QUALCOMM; // TODO update as needed
+    int periodCount = 32;
+    unsigned int offset1;
+    unsigned int frames1;
+    void *area = nullptr;
+    int mmapAvail = 0;
+
+    // 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
new file mode 100644
index 0000000..d6f28b2
--- /dev/null
+++ b/services/oboeservice/FakeAudioHal.h
@@ -0,0 +1,58 @@
+/*
+ * 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 oboe {
+
+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, 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 oboe */
+
+#endif // FAKE_AUDIO_HAL_H
diff --git a/services/oboeservice/OboeAudioService.cpp b/services/oboeservice/OboeAudioService.cpp
new file mode 100644
index 0000000..caddc1d
--- /dev/null
+++ b/services/oboeservice/OboeAudioService.cpp
@@ -0,0 +1,169 @@
+/*
+ * 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 "OboeService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <time.h>
+#include <pthread.h>
+
+#include <oboe/OboeDefinitions.h>
+
+#include "HandleTracker.h"
+#include "IOboeAudioService.h"
+#include "OboeService.h"
+#include "OboeAudioService.h"
+#include "OboeServiceStreamFakeHal.h"
+
+using namespace android;
+using namespace oboe;
+
+typedef enum
+{
+    OBOE_HANDLE_TYPE_STREAM,
+    OBOE_HANDLE_TYPE_COUNT
+} oboe_service_handle_type_t;
+static_assert(OBOE_HANDLE_TYPE_COUNT <= HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
+
+oboe_handle_t OboeAudioService::openStream(oboe::OboeStreamRequest &request,
+                                                oboe::OboeStreamConfiguration &configuration) {
+    OboeServiceStreamBase *serviceStream =  new OboeServiceStreamFakeHal();
+    ALOGD("OboeAudioService::openStream(): created serviceStream = %p", serviceStream);
+    oboe_result_t result = serviceStream->open(request, configuration);
+    if (result < 0) {
+        ALOGE("OboeAudioService::openStream(): open returned %d", result);
+        return result;
+    } else {
+        OboeStream handle = mHandleTracker.put(OBOE_HANDLE_TYPE_STREAM, serviceStream);
+        ALOGD("OboeAudioService::openStream(): handle = 0x%08X", handle);
+        if (handle < 0) {
+            delete serviceStream;
+        }
+        return handle;
+    }
+}
+
+oboe_result_t OboeAudioService::closeStream(oboe_handle_t streamHandle) {
+    OboeServiceStreamBase *serviceStream = (OboeServiceStreamBase *)
+            mHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM,
+                                  streamHandle);
+    ALOGI("OboeAudioService.closeStream(0x%08X)", streamHandle);
+    if (serviceStream != nullptr) {
+        ALOGD("OboeAudioService::closeStream(): deleting serviceStream = %p", serviceStream);
+        delete serviceStream;
+        return OBOE_OK;
+    }
+    return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OboeServiceStreamBase *OboeAudioService::convertHandleToServiceStream(
+        oboe_handle_t streamHandle) const {
+    return (OboeServiceStreamBase *) mHandleTracker.get(OBOE_HANDLE_TYPE_STREAM,
+                              (oboe_handle_t)streamHandle);
+}
+
+oboe_result_t OboeAudioService::getStreamDescription(
+                oboe_handle_t streamHandle,
+                oboe::AudioEndpointParcelable &parcelable) {
+    ALOGI("OboeAudioService::getStreamDescriptor(), streamHandle = 0x%08x", streamHandle);
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    ALOGI("OboeAudioService::getStreamDescriptor(), serviceStream = %p", serviceStream);
+    if (serviceStream == nullptr) {
+        return OBOE_ERROR_INVALID_HANDLE;
+    }
+    return serviceStream->getDescription(parcelable);
+}
+
+oboe_result_t OboeAudioService::startStream(oboe_handle_t streamHandle) {
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    ALOGI("OboeAudioService::startStream(), serviceStream = %p", serviceStream);
+    if (serviceStream == nullptr) {
+        return OBOE_ERROR_INVALID_HANDLE;
+    }
+    mLatestHandle = streamHandle;
+    return serviceStream->start();
+}
+
+oboe_result_t OboeAudioService::pauseStream(oboe_handle_t streamHandle) {
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    ALOGI("OboeAudioService::pauseStream(), serviceStream = %p", serviceStream);
+    if (serviceStream == nullptr) {
+        return OBOE_ERROR_INVALID_HANDLE;
+    }
+    return serviceStream->pause();
+}
+
+oboe_result_t OboeAudioService::flushStream(oboe_handle_t streamHandle) {
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    ALOGI("OboeAudioService::flushStream(), serviceStream = %p", serviceStream);
+    if (serviceStream == nullptr) {
+        return OBOE_ERROR_INVALID_HANDLE;
+    }
+    return serviceStream->flush();
+}
+
+void OboeAudioService::tickle() {
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(mLatestHandle);
+    //ALOGI("OboeAudioService::tickle(), serviceStream = %p", serviceStream);
+    if (serviceStream != nullptr) {
+        serviceStream->tickle();
+    }
+}
+
+oboe_result_t OboeAudioService::registerAudioThread(oboe_handle_t streamHandle,
+                                                         pid_t clientThreadId,
+                                                         oboe_nanoseconds_t periodNanoseconds) {
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    ALOGI("OboeAudioService::registerAudioThread(), serviceStream = %p", serviceStream);
+    if (serviceStream == nullptr) {
+        ALOGE("OboeAudioService::registerAudioThread(), serviceStream == nullptr");
+        return OBOE_ERROR_INVALID_HANDLE;
+    }
+    if (serviceStream->getRegisteredThread() != OboeServiceStreamBase::ILLEGAL_THREAD_ID) {
+        ALOGE("OboeAudioService::registerAudioThread(), thread already registered");
+        return OBOE_ERROR_INVALID_ORDER;
+    }
+    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);
+    if (err != 0){
+        ALOGE("OboeAudioService::sched_setscheduler() failed, errno = %d, priority = %d",
+              errno, sp.sched_priority);
+        return OBOE_ERROR_INTERNAL;
+    } else {
+        return OBOE_OK;
+    }
+}
+
+oboe_result_t OboeAudioService::unregisterAudioThread(oboe_handle_t streamHandle,
+                                                           pid_t clientThreadId) {
+    OboeServiceStreamBase *serviceStream = convertHandleToServiceStream(streamHandle);
+    ALOGI("OboeAudioService::unregisterAudioThread(), serviceStream = %p", serviceStream);
+    if (serviceStream == nullptr) {
+        ALOGE("OboeAudioService::unregisterAudioThread(), serviceStream == nullptr");
+        return OBOE_ERROR_INVALID_HANDLE;
+    }
+    if (serviceStream->getRegisteredThread() != clientThreadId) {
+        ALOGE("OboeAudioService::unregisterAudioThread(), wrong thread");
+        return OBOE_ERROR_ILLEGAL_ARGUMENT;
+    }
+    serviceStream->setRegisteredThread(0);
+    return OBOE_OK;
+}
diff --git a/services/oboeservice/OboeAudioService.h b/services/oboeservice/OboeAudioService.h
new file mode 100644
index 0000000..df3cbf8
--- /dev/null
+++ b/services/oboeservice/OboeAudioService.h
@@ -0,0 +1,76 @@
+/*
+ * 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 OBOE_OBOE_AUDIO_SERVICE_H
+#define OBOE_OBOE_AUDIO_SERVICE_H
+
+#include <time.h>
+#include <pthread.h>
+
+#include <binder/BinderService.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "HandleTracker.h"
+#include "IOboeAudioService.h"
+#include "OboeService.h"
+#include "OboeServiceStreamBase.h"
+
+using namespace android;
+namespace oboe {
+
+class OboeAudioService :
+    public BinderService<OboeAudioService>,
+    public BnOboeAudioService
+{
+    friend class BinderService<OboeAudioService>;   // for OboeAudioService()
+public:
+// TODO why does this fail?    static const char* getServiceName() ANDROID_API { return "media.audio_oboe"; }
+    static const char* getServiceName() { return "media.audio_oboe"; }
+
+    virtual oboe_handle_t openStream(OboeStreamRequest &request,
+                                     OboeStreamConfiguration &configuration);
+
+    virtual oboe_result_t closeStream(oboe_handle_t streamHandle);
+
+    virtual oboe_result_t getStreamDescription(
+                oboe_handle_t streamHandle,
+                AudioEndpointParcelable &parcelable);
+
+    virtual oboe_result_t startStream(oboe_handle_t streamHandle);
+
+    virtual oboe_result_t pauseStream(oboe_handle_t streamHandle);
+
+    virtual oboe_result_t flushStream(oboe_handle_t streamHandle);
+
+    virtual oboe_result_t registerAudioThread(oboe_handle_t streamHandle,
+                                              pid_t pid, oboe_nanoseconds_t periodNanoseconds) ;
+
+    virtual oboe_result_t unregisterAudioThread(oboe_handle_t streamHandle, pid_t pid);
+
+    virtual void tickle();
+
+private:
+
+    OboeServiceStreamBase *convertHandleToServiceStream(oboe_handle_t streamHandle) const;
+
+    HandleTracker mHandleTracker;
+    oboe_handle_t mLatestHandle = OBOE_ERROR_INVALID_HANDLE; // TODO until we have service threads
+};
+
+} /* namespace oboe */
+
+#endif //OBOE_OBOE_AUDIO_SERVICE_H
diff --git a/services/oboeservice/OboeService.h b/services/oboeservice/OboeService.h
new file mode 100644
index 0000000..a24f525
--- /dev/null
+++ b/services/oboeservice/OboeService.h
@@ -0,0 +1,67 @@
+/*
+ * 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 OBOE_OBOE_SERVICE_H
+#define OBOE_OBOE_SERVICE_H
+
+#include <stdint.h>
+
+#include <oboe/OboeAudio.h>
+
+#include "binding/RingBufferParcelable.h"
+
+namespace oboe {
+
+// TODO move this an "include" folder for the service.
+
+struct OboeMessageTimestamp {
+    oboe_position_frames_t position;
+    int64_t                deviceOffset; // add to client position to get device position
+    oboe_nanoseconds_t     timestamp;
+};
+
+typedef enum oboe_service_event_e : uint32_t {
+    OBOE_SERVICE_EVENT_STARTED,
+    OBOE_SERVICE_EVENT_PAUSED,
+    OBOE_SERVICE_EVENT_FLUSHED,
+    OBOE_SERVICE_EVENT_CLOSED,
+    OBOE_SERVICE_EVENT_DISCONNECTED
+} oboe_service_event_t;
+
+struct OboeMessageEvent {
+    oboe_service_event_t event;
+    int32_t data1;
+    int64_t data2;
+};
+
+typedef struct OboeServiceMessage_s {
+    enum class code : uint32_t {
+        NOTHING,
+        TIMESTAMP,
+        EVENT,
+    };
+
+    code what;
+    union {
+        OboeMessageTimestamp timestamp;
+        OboeMessageEvent event;
+    };
+} OboeServiceMessage;
+
+
+} /* namespace oboe */
+
+#endif //OBOE_OBOE_SERVICE_H
diff --git a/services/oboeservice/OboeServiceMain.cpp b/services/oboeservice/OboeServiceMain.cpp
new file mode 100644
index 0000000..18bcf2b
--- /dev/null
+++ b/services/oboeservice/OboeServiceMain.cpp
@@ -0,0 +1,58 @@
+/*
+ * 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 "OboeService"
+//#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 "OboeService.h"
+#include "IOboeAudioService.h"
+#include "OboeAudioService.h"
+
+using namespace android;
+using namespace oboe;
+
+/**
+ * This is used to test the OboeService as a standalone application.
+ * It is not used when the OboeService is integrated with AudioFlinger.
+ */
+int main(int argc, char **argv) {
+    printf("Test OboeService %s\n", argv[1]);
+    ALOGD("This is the OboeAudioService");
+
+    defaultServiceManager()->addService(String16("OboeAudioService"), new OboeAudioService());
+    android::ProcessState::self()->startThreadPool();
+    printf("OboeAudioService service is now ready\n");
+    IPCThreadState::self()->joinThreadPool();
+    printf("OboeAudioService service thread joined\n");
+
+    return 0;
+}
diff --git a/services/oboeservice/OboeServiceStreamBase.cpp b/services/oboeservice/OboeServiceStreamBase.cpp
new file mode 100644
index 0000000..6b7e4e5
--- /dev/null
+++ b/services/oboeservice/OboeServiceStreamBase.cpp
@@ -0,0 +1,57 @@
+/*
+ * 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 "OboeService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "IOboeAudioService.h"
+#include "OboeService.h"
+#include "OboeServiceStreamBase.h"
+#include "AudioEndpointParcelable.h"
+
+using namespace android;
+using namespace oboe;
+
+/**
+ * Construct the AudioCommandQueues and the AudioDataQueue
+ * and fill in the endpoint parcelable.
+ */
+
+OboeServiceStreamBase::OboeServiceStreamBase()
+        : mUpMessageQueue(nullptr)
+{
+    // TODO could fail so move out of constructor
+    mUpMessageQueue = new SharedRingBuffer();
+    mUpMessageQueue->allocate(sizeof(OboeServiceMessage), QUEUE_UP_CAPACITY_COMMANDS);
+}
+
+OboeServiceStreamBase::~OboeServiceStreamBase() {
+    delete mUpMessageQueue;
+}
+
+void OboeServiceStreamBase::sendServiceEvent(oboe_service_event_t event,
+                              int32_t data1,
+                              int64_t data2) {
+    OboeServiceMessage command;
+    command.what = OboeServiceMessage::code::EVENT;
+    command.event.event = event;
+    command.event.data1 = data1;
+    command.event.data2 = data2;
+    mUpMessageQueue->getFifoBuffer()->write(&command, 1);
+}
+
+
diff --git a/services/oboeservice/OboeServiceStreamBase.h b/services/oboeservice/OboeServiceStreamBase.h
new file mode 100644
index 0000000..736c754
--- /dev/null
+++ b/services/oboeservice/OboeServiceStreamBase.h
@@ -0,0 +1,99 @@
+/*
+ * 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 OBOE_OBOE_SERVICE_STREAM_BASE_H
+#define OBOE_OBOE_SERVICE_STREAM_BASE_H
+
+#include "IOboeAudioService.h"
+#include "OboeService.h"
+#include "AudioStream.h"
+#include "fifo/FifoBuffer.h"
+#include "SharedRingBuffer.h"
+#include "AudioEndpointParcelable.h"
+
+namespace oboe {
+
+// 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 OboeServiceStreamBase  {
+
+public:
+    OboeServiceStreamBase();
+    virtual ~OboeServiceStreamBase();
+
+    enum {
+        ILLEGAL_THREAD_ID = 0
+    };
+
+    /**
+     * Fill in a parcelable description of stream.
+     */
+    virtual oboe_result_t getDescription(oboe::AudioEndpointParcelable &parcelable) = 0;
+
+    /**
+     * Open the device.
+     */
+    virtual oboe_result_t open(oboe::OboeStreamRequest &request,
+                               oboe::OboeStreamConfiguration &configuration) = 0;
+
+    /**
+     * Start the flow of data.
+     */
+    virtual oboe_result_t start() = 0;
+
+    /**
+     * Stop the flow of data such that start() can resume with loss of data.
+     */
+    virtual oboe_result_t pause() = 0;
+
+    /**
+     *  Discard any data held by the underlying HAL or Service.
+     */
+    virtual oboe_result_t flush() = 0;
+
+    virtual oboe_result_t close() = 0;
+
+    virtual void tickle() = 0;
+
+    virtual void sendServiceEvent(oboe_service_event_t event,
+                                  int32_t data1 = 0,
+                                  int64_t data2 = 0);
+
+    virtual void setRegisteredThread(pid_t pid) {
+        mRegisteredClientThread = pid;
+    }
+    virtual pid_t getRegisteredThread() {
+        return mRegisteredClientThread;
+    }
+
+protected:
+
+    pid_t                    mRegisteredClientThread = ILLEGAL_THREAD_ID;
+
+    SharedRingBuffer *       mUpMessageQueue;
+
+    oboe_sample_rate_t       mSampleRate = 0;
+    oboe_size_bytes_t        mBytesPerFrame = 0;
+    oboe_size_frames_t       mFramesPerBurst = 0;
+    oboe_size_frames_t       mCapacityInFrames = 0;
+    oboe_size_bytes_t        mCapacityInBytes = 0;
+};
+
+} /* namespace oboe */
+
+#endif //OBOE_OBOE_SERVICE_STREAM_BASE_H
diff --git a/services/oboeservice/OboeServiceStreamFakeHal.cpp b/services/oboeservice/OboeServiceStreamFakeHal.cpp
new file mode 100644
index 0000000..dbbc860
--- /dev/null
+++ b/services/oboeservice/OboeServiceStreamFakeHal.cpp
@@ -0,0 +1,191 @@
+/*
+ * 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 "OboeService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "AudioClock.h"
+#include "AudioEndpointParcelable.h"
+
+#include "OboeServiceStreamBase.h"
+#include "OboeServiceStreamFakeHal.h"
+
+#include "FakeAudioHal.h"
+
+using namespace android;
+using namespace oboe;
+
+// HACK values for Marlin
+#define CARD_ID              0
+#define DEVICE_ID            19
+
+/**
+ * Construct the audio message queuues and message queues.
+ */
+
+OboeServiceStreamFakeHal::OboeServiceStreamFakeHal()
+        : OboeServiceStreamBase()
+        , mStreamId(nullptr)
+        , mPreviousFrameCounter(0)
+{
+}
+
+OboeServiceStreamFakeHal::~OboeServiceStreamFakeHal() {
+    ALOGD("OboeServiceStreamFakeHal::~OboeServiceStreamFakeHal() call close()");
+    close();
+}
+
+oboe_result_t OboeServiceStreamFakeHal::open(oboe::OboeStreamRequest &request,
+                                             oboe::OboeStreamConfiguration &configuration) {
+    // Open stream on HAL and pass information about the ring buffer to the client.
+    mmap_buffer_info mmapInfo;
+    oboe_result_t error;
+
+    // Open HAL
+    error = fake_hal_open(CARD_ID, DEVICE_ID, &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("OboeServiceStreamFakeHal::open() mmapInfo.burst_size_in_frames = %d",
+         mmapInfo.burst_size_in_frames);
+    ALOGD("OboeServiceStreamFakeHal::open() mmapInfo.buffer_capacity_in_frames = %d",
+         mmapInfo.buffer_capacity_in_frames);
+    ALOGD("OboeServiceStreamFakeHal::open() mmapInfo.buffer_capacity_in_bytes = %d",
+         mmapInfo.buffer_capacity_in_bytes);
+
+    // Fill in OboeStreamConfiguration
+    configuration.setSampleRate(mSampleRate);
+    configuration.setSamplesPerFrame(mmapInfo.channel_count);
+    configuration.setAudioFormat(OBOE_AUDIO_FORMAT_PCM16);
+    return OBOE_OK;
+}
+
+/**
+ * Get an immutable description of the in-memory queues
+ * used to communicate with the underlying HAL or Service.
+ */
+oboe_result_t OboeServiceStreamFakeHal::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 OBOE_OK;
+}
+
+/**
+ * Start the flow of data.
+ */
+oboe_result_t OboeServiceStreamFakeHal::start() {
+    if (mStreamId == nullptr) return OBOE_ERROR_NULL;
+    oboe_result_t result = fake_hal_start(mStreamId);
+    sendServiceEvent(OBOE_SERVICE_EVENT_STARTED);
+    mState = OBOE_STREAM_STATE_STARTED;
+    return result;
+}
+
+/**
+ * Stop the flow of data such that start() can resume with loss of data.
+ */
+oboe_result_t OboeServiceStreamFakeHal::pause() {
+    if (mStreamId == nullptr) return OBOE_ERROR_NULL;
+    sendCurrentTimestamp();
+    oboe_result_t result = fake_hal_pause(mStreamId);
+    sendServiceEvent(OBOE_SERVICE_EVENT_PAUSED);
+    mState = OBOE_STREAM_STATE_PAUSED;
+    mFramesRead.reset32();
+    ALOGD("OboeServiceStreamFakeHal::pause() sent OBOE_SERVICE_EVENT_PAUSED");
+    return result;
+}
+
+/**
+ *  Discard any data held by the underlying HAL or Service.
+ */
+oboe_result_t OboeServiceStreamFakeHal::flush() {
+    if (mStreamId == nullptr) return OBOE_ERROR_NULL;
+    // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
+    ALOGD("OboeServiceStreamFakeHal::pause() send OBOE_SERVICE_EVENT_FLUSHED");
+    sendServiceEvent(OBOE_SERVICE_EVENT_FLUSHED);
+    mState = OBOE_STREAM_STATE_FLUSHED;
+    return OBOE_OK;
+}
+
+oboe_result_t OboeServiceStreamFakeHal::close() {
+    oboe_result_t result = OBOE_OK;
+    if (mStreamId != nullptr) {
+        result = fake_hal_close(mStreamId);
+        mStreamId = nullptr;
+    }
+    return result;
+}
+
+void OboeServiceStreamFakeHal::sendCurrentTimestamp() {
+    int frameCounter = 0;
+    int error = fake_hal_get_frame_counter(mStreamId, &frameCounter);
+    if (error < 0) {
+        ALOGE("OboeServiceStreamFakeHal::sendCurrentTimestamp() error %d",
+                error);
+    } else if (frameCounter != mPreviousFrameCounter) {
+        OboeServiceMessage command;
+        command.what = OboeServiceMessage::code::TIMESTAMP;
+        mFramesRead.update32(frameCounter);
+        command.timestamp.position = mFramesRead.get();
+        ALOGV("OboeServiceStreamFakeHal::sendCurrentTimestamp() HAL frames = %d, pos = %d",
+                frameCounter, (int)mFramesRead.get());
+        command.timestamp.timestamp = AudioClock::getNanoseconds();
+        mUpMessageQueue->getFifoBuffer()->write(&command, 1);
+        mPreviousFrameCounter = frameCounter;
+    }
+}
+
+void OboeServiceStreamFakeHal::tickle() {
+    if (mStreamId != nullptr) {
+        switch (mState) {
+            case OBOE_STREAM_STATE_STARTING:
+            case OBOE_STREAM_STATE_STARTED:
+            case OBOE_STREAM_STATE_PAUSING:
+            case OBOE_STREAM_STATE_STOPPING:
+                sendCurrentTimestamp();
+                break;
+            default:
+                break;
+        }
+    }
+}
+
diff --git a/services/oboeservice/OboeServiceStreamFakeHal.h b/services/oboeservice/OboeServiceStreamFakeHal.h
new file mode 100644
index 0000000..b026d34
--- /dev/null
+++ b/services/oboeservice/OboeServiceStreamFakeHal.h
@@ -0,0 +1,75 @@
+/*
+ * 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 OBOE_OBOE_SERVICE_STREAM_FAKE_HAL_H
+#define OBOE_OBOE_SERVICE_STREAM_FAKE_HAL_H
+
+#include "OboeService.h"
+#include "OboeServiceStreamBase.h"
+#include "FakeAudioHal.h"
+#include "MonotonicCounter.h"
+#include "AudioEndpointParcelable.h"
+
+namespace oboe {
+
+class OboeServiceStreamFakeHal : public OboeServiceStreamBase {
+
+public:
+    OboeServiceStreamFakeHal();
+    virtual ~OboeServiceStreamFakeHal();
+
+    virtual oboe_result_t getDescription(AudioEndpointParcelable &parcelable) override;
+
+    virtual oboe_result_t open(oboe::OboeStreamRequest &request,
+                               oboe::OboeStreamConfiguration &configuration) override;
+
+    /**
+     * Start the flow of data.
+     */
+    virtual oboe_result_t start() override;
+
+    /**
+     * Stop the flow of data such that start() can resume with loss of data.
+     */
+    virtual oboe_result_t pause() override;
+
+    /**
+     *  Discard any data held by the underlying HAL or Service.
+     */
+    virtual oboe_result_t flush() override;
+
+    virtual oboe_result_t close() override;
+
+    virtual void tickle() override;
+
+protected:
+
+    void sendCurrentTimestamp();
+
+private:
+    fake_hal_stream_ptr    mStreamId; // Move to HAL
+
+    MonotonicCounter       mFramesWritten;
+    MonotonicCounter       mFramesRead;
+    int                    mHalFileDescriptor = -1;
+    int                    mPreviousFrameCounter = 0;   // from HAL
+
+    oboe_stream_state_t    mState = OBOE_STREAM_STATE_UNINITIALIZED;
+};
+
+} // namespace oboe
+
+#endif //OBOE_OBOE_SERVICE_STREAM_FAKE_HAL_H
diff --git a/services/oboeservice/SharedRingBuffer.cpp b/services/oboeservice/SharedRingBuffer.cpp
new file mode 100644
index 0000000..c3df5ce
--- /dev/null
+++ b/services/oboeservice/SharedRingBuffer.cpp
@@ -0,0 +1,96 @@
+/*
+ * 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 "OboeService"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "AudioClock.h"
+#include "AudioEndpointParcelable.h"
+
+//#include "OboeServiceStreamBase.h"
+//#include "OboeServiceStreamFakeHal.h"
+
+#include "SharedRingBuffer.h"
+
+using namespace android;
+using namespace oboe;
+
+SharedRingBuffer::~SharedRingBuffer()
+{
+    if (mSharedMemory != nullptr) {
+        delete mFifoBuffer;
+        munmap(mSharedMemory, mSharedMemorySizeInBytes);
+        close(mFileDescriptor);
+        mSharedMemory = nullptr;
+    }
+}
+
+oboe_result_t SharedRingBuffer::allocate(fifo_frames_t   bytesPerFrame,
+                                         fifo_frames_t   capacityInFrames) {
+    mCapacityInFrames = capacityInFrames;
+
+    // Create shared memory large enough to hold the data and the read and write counters.
+    mDataMemorySizeInBytes = bytesPerFrame * capacityInFrames;
+    mSharedMemorySizeInBytes = mDataMemorySizeInBytes + (2 * (sizeof(fifo_counter_t)));
+    mFileDescriptor = ashmem_create_region("OboeSharedRingBuffer", mSharedMemorySizeInBytes);
+    if (mFileDescriptor < 0) {
+        ALOGE("SharedRingBuffer::allocate() ashmem_create_region() failed %d", errno);
+        return OBOE_ERROR_INTERNAL;
+    }
+    int err = ashmem_set_prot_region(mFileDescriptor, PROT_READ|PROT_WRITE); // TODO error handling?
+    if (err < 0) {
+        ALOGE("SharedRingBuffer::allocate() ashmem_set_prot_region() failed %d", errno);
+        close(mFileDescriptor);
+        return OBOE_ERROR_INTERNAL; // TODO convert errno to a better OBOE_ERROR;
+    }
+
+    // Map the fd to memory addresses.
+    mSharedMemory = (uint8_t *) mmap(0, mSharedMemorySizeInBytes,
+                         PROT_READ|PROT_WRITE,
+                         MAP_SHARED,
+                         mFileDescriptor, 0);
+    if (mSharedMemory == MAP_FAILED) {
+        ALOGE("SharedRingBuffer::allocate() mmap() failed %d", errno);
+        close(mFileDescriptor);
+        return OBOE_ERROR_INTERNAL; // TODO convert errno to a better OBOE_ERROR;
+    }
+
+    // Get addresses for our counters and data from the shared memory.
+    fifo_counter_t *readCounterAddress =
+            (fifo_counter_t *) &mSharedMemory[SHARED_RINGBUFFER_READ_OFFSET];
+    fifo_counter_t *writeCounterAddress =
+            (fifo_counter_t *) &mSharedMemory[SHARED_RINGBUFFER_WRITE_OFFSET];
+    uint8_t *dataAddress = &mSharedMemory[SHARED_RINGBUFFER_DATA_OFFSET];
+
+    mFifoBuffer = new(std::nothrow) FifoBuffer(bytesPerFrame, capacityInFrames,
+                                 readCounterAddress, writeCounterAddress, dataAddress);
+    return (mFifoBuffer == nullptr) ? OBOE_ERROR_NO_MEMORY : OBOE_OK;
+}
+
+void SharedRingBuffer::fillParcelable(AudioEndpointParcelable &endpointParcelable,
+                    RingBufferParcelable &ringBufferParcelable) {
+    int fdIndex = endpointParcelable.addFileDescriptor(mFileDescriptor, mSharedMemorySizeInBytes);
+    ringBufferParcelable.setupMemory(fdIndex,
+                                     SHARED_RINGBUFFER_DATA_OFFSET,
+                                     mDataMemorySizeInBytes,
+                                     SHARED_RINGBUFFER_READ_OFFSET,
+                                     SHARED_RINGBUFFER_WRITE_OFFSET,
+                                     sizeof(fifo_counter_t));
+    ringBufferParcelable.setBytesPerFrame(mFifoBuffer->getBytesPerFrame());
+    ringBufferParcelable.setFramesPerBurst(1);
+    ringBufferParcelable.setCapacityInFrames(mCapacityInFrames);
+}
diff --git a/services/oboeservice/SharedRingBuffer.h b/services/oboeservice/SharedRingBuffer.h
new file mode 100644
index 0000000..3cc1c2d
--- /dev/null
+++ b/services/oboeservice/SharedRingBuffer.h
@@ -0,0 +1,64 @@
+/*
+ * 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 OBOE_SHARED_RINGBUFFER_H
+#define OBOE_SHARED_RINGBUFFER_H
+
+#include <stdint.h>
+#include <cutils/ashmem.h>
+#include <sys/mman.h>
+
+#include "fifo/FifoBuffer.h"
+#include "RingBufferParcelable.h"
+#include "AudioEndpointParcelable.h"
+
+namespace oboe {
+
+// Determine the placement of the counters and data in shared memory.
+#define SHARED_RINGBUFFER_READ_OFFSET   0
+#define SHARED_RINGBUFFER_WRITE_OFFSET  sizeof(fifo_counter_t)
+#define SHARED_RINGBUFFER_DATA_OFFSET   (SHARED_RINGBUFFER_WRITE_OFFSET + sizeof(fifo_counter_t))
+
+/**
+ * Atomic FIFO that uses shared memory.
+ */
+class SharedRingBuffer {
+public:
+    SharedRingBuffer() {}
+
+    virtual ~SharedRingBuffer();
+
+    oboe_result_t allocate(fifo_frames_t bytesPerFrame, fifo_frames_t capacityInFrames);
+
+    void fillParcelable(AudioEndpointParcelable &endpointParcelable,
+                        RingBufferParcelable &ringBufferParcelable);
+
+    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;
+};
+
+} /* namespace oboe */
+
+#endif //OBOE_SHARED_RINGBUFFER_H