liboboe: oboe MMAP client interface

Bug: 33347409
Test: test_oboe_api
Change-Id: I2ff3e9b57c91839c6debe91903b3e4b92e82d681
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/liboboe/src/binding/AudioEndpointParcelable.cpp b/media/liboboe/src/binding/AudioEndpointParcelable.cpp
new file mode 100644
index 0000000..096a819
--- /dev/null
+++ b/media/liboboe/src/binding/AudioEndpointParcelable.cpp
@@ -0,0 +1,141 @@
+/*
+ * Copyright 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.
+ */
+
+#include <stdint.h>
+
+#include <sys/mman.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include "binding/OboeServiceDefinitions.h"
+#include "binding/RingBufferParcelable.h"
+#include "binding/AudioEndpointParcelable.h"
+
+using android::NO_ERROR;
+using android::status_t;
+using android::Parcel;
+using android::Parcelable;
+
+using namespace oboe;
+
+/**
+ * Container for information about the message queues plus
+ * general stream information needed by Oboe clients.
+ * It contains no addresses, just sizes, offsets and file descriptors for
+ * shared memory that can be passed through Binder.
+ */
+AudioEndpointParcelable::AudioEndpointParcelable() {}
+
+AudioEndpointParcelable::~AudioEndpointParcelable() {}
+
+/**
+ * Add the file descriptor to the table.
+ * @return index in table or negative error
+ */
+int32_t AudioEndpointParcelable::addFileDescriptor(int fd, int32_t sizeInBytes) {
+    if (mNumSharedMemories >= MAX_SHARED_MEMORIES) {
+        return OBOE_ERROR_OUT_OF_RANGE;
+    }
+    int32_t index = mNumSharedMemories++;
+    mSharedMemories[index].setup(fd, sizeInBytes);
+    return index;
+}
+
+/**
+ * The read and write must be symmetric.
+ */
+status_t AudioEndpointParcelable::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mNumSharedMemories);
+    for (int i = 0; i < mNumSharedMemories; i++) {
+        mSharedMemories[i].writeToParcel(parcel);
+    }
+    mUpMessageQueueParcelable.writeToParcel(parcel);
+    mDownMessageQueueParcelable.writeToParcel(parcel);
+    mUpDataQueueParcelable.writeToParcel(parcel);
+    mDownDataQueueParcelable.writeToParcel(parcel);
+    return NO_ERROR; // TODO check for errors above
+}
+
+status_t AudioEndpointParcelable::readFromParcel(const Parcel* parcel) {
+    parcel->readInt32(&mNumSharedMemories);
+    for (int i = 0; i < mNumSharedMemories; i++) {
+        mSharedMemories[i].readFromParcel(parcel);
+    }
+    mUpMessageQueueParcelable.readFromParcel(parcel);
+    mDownMessageQueueParcelable.readFromParcel(parcel);
+    mUpDataQueueParcelable.readFromParcel(parcel);
+    mDownDataQueueParcelable.readFromParcel(parcel);
+    return NO_ERROR; // TODO check for errors above
+}
+
+oboe_result_t AudioEndpointParcelable::resolve(EndpointDescriptor *descriptor) {
+    // TODO error check
+    mUpMessageQueueParcelable.resolve(mSharedMemories, &descriptor->upMessageQueueDescriptor);
+    mDownMessageQueueParcelable.resolve(mSharedMemories,
+                                        &descriptor->downMessageQueueDescriptor);
+    mUpDataQueueParcelable.resolve(mSharedMemories, &descriptor->upDataQueueDescriptor);
+    mDownDataQueueParcelable.resolve(mSharedMemories, &descriptor->downDataQueueDescriptor);
+    return OBOE_OK;
+}
+
+oboe_result_t AudioEndpointParcelable::validate() {
+    oboe_result_t result;
+    if (mNumSharedMemories < 0 || mNumSharedMemories >= MAX_SHARED_MEMORIES) {
+        ALOGE("AudioEndpointParcelable invalid mNumSharedMemories = %d", mNumSharedMemories);
+        return OBOE_ERROR_INTERNAL;
+    }
+    for (int i = 0; i < mNumSharedMemories; i++) {
+        result = mSharedMemories[i].validate();
+        if (result != OBOE_OK) {
+            return result;
+        }
+    }
+    if ((result = mUpMessageQueueParcelable.validate()) != OBOE_OK) {
+        ALOGE("AudioEndpointParcelable invalid mUpMessageQueueParcelable = %d", result);
+        return result;
+    }
+    if ((result = mDownMessageQueueParcelable.validate()) != OBOE_OK) {
+        ALOGE("AudioEndpointParcelable invalid mDownMessageQueueParcelable = %d", result);
+        return result;
+    }
+    if ((result = mUpDataQueueParcelable.validate()) != OBOE_OK) {
+        ALOGE("AudioEndpointParcelable invalid mUpDataQueueParcelable = %d", result);
+        return result;
+    }
+    if ((result = mDownDataQueueParcelable.validate()) != OBOE_OK) {
+        ALOGE("AudioEndpointParcelable invalid mDownDataQueueParcelable = %d", result);
+        return result;
+    }
+    return OBOE_OK;
+}
+
+void AudioEndpointParcelable::dump() {
+    ALOGD("AudioEndpointParcelable ======================================= BEGIN");
+    ALOGD("AudioEndpointParcelable mNumSharedMemories = %d", mNumSharedMemories);
+    for (int i = 0; i < mNumSharedMemories; i++) {
+        mSharedMemories[i].dump();
+    }
+    ALOGD("AudioEndpointParcelable mUpMessageQueueParcelable =========");
+    mUpMessageQueueParcelable.dump();
+    ALOGD("AudioEndpointParcelable mDownMessageQueueParcelable =======");
+    mDownMessageQueueParcelable.dump();
+    ALOGD("AudioEndpointParcelable mUpDataQueueParcelable ============");
+    mUpDataQueueParcelable.dump();
+    ALOGD("AudioEndpointParcelable mDownDataQueueParcelable ==========");
+    mDownDataQueueParcelable.dump();
+    ALOGD("AudioEndpointParcelable ======================================= END");
+}
+
diff --git a/media/liboboe/src/binding/IOboeAudioService.cpp b/media/liboboe/src/binding/IOboeAudioService.cpp
new file mode 100644
index 0000000..a3437b2
--- /dev/null
+++ b/media/liboboe/src/binding/IOboeAudioService.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#include <oboe/OboeDefinitions.h>
+
+#include "binding/AudioEndpointParcelable.h"
+#include "binding/OboeStreamRequest.h"
+#include "binding/OboeStreamConfiguration.h"
+#include "binding/IOboeAudioService.h"
+
+namespace android {
+
+/**
+ * This is used by the Oboe Client to talk to the Oboe Service.
+ *
+ * The order of parameters in the Parcels must match with code in OboeAudioService.cpp.
+ */
+class BpOboeAudioService : public BpInterface<IOboeAudioService>
+{
+public:
+    explicit BpOboeAudioService(const sp<IBinder>& impl)
+        : BpInterface<IOboeAudioService>(impl)
+    {
+    }
+
+    virtual oboe_handle_t openStream(oboe::OboeStreamRequest &request,
+                                     oboe::OboeStreamConfiguration &configuration) override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        request.writeToParcel(&data);
+        status_t err = remote()->transact(OPEN_STREAM, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_handle_t stream;
+        reply.readInt32(&stream);
+        configuration.readFromParcel(&reply);
+        return stream;
+    }
+
+    virtual oboe_result_t closeStream(int32_t streamHandle) override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        status_t err = remote()->transact(CLOSE_STREAM, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
+    virtual oboe_result_t getStreamDescription(oboe_handle_t streamHandle,
+                                               AudioEndpointParcelable &parcelable)   {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        status_t err = remote()->transact(GET_STREAM_DESCRIPTION, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        parcelable.readFromParcel(&reply);
+        parcelable.dump();
+        oboe_result_t result = parcelable.validate();
+        if (result != OBOE_OK) {
+            return result;
+        }
+        reply.readInt32(&result);
+        return result;
+    }
+
+    // TODO should we wait for a reply?
+    virtual oboe_result_t startStream(oboe_handle_t streamHandle) override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        status_t err = remote()->transact(START_STREAM, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
+    virtual oboe_result_t pauseStream(oboe_handle_t streamHandle) override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        status_t err = remote()->transact(PAUSE_STREAM, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
+    virtual oboe_result_t flushStream(oboe_handle_t streamHandle) override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        status_t err = remote()->transact(FLUSH_STREAM, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
+    virtual void tickle() override { // TODO remove after service thread implemented
+        Parcel data;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        remote()->transact(TICKLE, data, nullptr);
+    }
+
+    virtual oboe_result_t registerAudioThread(oboe_handle_t streamHandle, pid_t clientThreadId,
+                                              oboe_nanoseconds_t periodNanoseconds)
+    override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        data.writeInt32((int32_t) clientThreadId);
+        data.writeInt64(periodNanoseconds);
+        status_t err = remote()->transact(REGISTER_AUDIO_THREAD, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
+    virtual oboe_result_t unregisterAudioThread(oboe_handle_t streamHandle, pid_t clientThreadId)
+    override {
+        Parcel data, reply;
+        // send command
+        data.writeInterfaceToken(IOboeAudioService::getInterfaceDescriptor());
+        data.writeInt32(streamHandle);
+        data.writeInt32((int32_t) clientThreadId);
+        status_t err = remote()->transact(UNREGISTER_AUDIO_THREAD, data, &reply);
+        if (err != NO_ERROR) {
+            return OBOE_ERROR_INTERNAL; // TODO consider another error
+        }
+        // parse reply
+        oboe_result_t res;
+        reply.readInt32(&res);
+        return res;
+    }
+
+};
+
+// Implement an interface to the service.
+// This is here so that you don't have to link with liboboe static library.
+IMPLEMENT_META_INTERFACE(OboeAudioService, "IOboeAudioService");
+
+// The order of parameters in the Parcels must match with code in BpOboeAudioService
+
+status_t BnOboeAudioService::onTransact(uint32_t code, const Parcel& data,
+                                        Parcel* reply, uint32_t flags) {
+    OboeStream stream;
+    OboeStreamRequest request;
+    OboeStreamConfiguration configuration;
+    pid_t pid;
+    oboe_nanoseconds_t nanoseconds;
+    oboe_result_t result;
+    ALOGV("BnOboeAudioService::onTransact(%i) %i", code, flags);
+    data.checkInterface(this);
+
+    switch(code) {
+        case OPEN_STREAM: {
+            request.readFromParcel(&data);
+            stream = openStream(request, configuration);
+            ALOGD("BnOboeAudioService::onTransact OPEN_STREAM 0x%08X", stream);
+            reply->writeInt32(stream);
+            configuration.writeToParcel(reply);
+            return NO_ERROR;
+        } break;
+
+        case CLOSE_STREAM: {
+            data.readInt32(&stream);
+            ALOGD("BnOboeAudioService::onTransact CLOSE_STREAM 0x%08X", stream);
+            result = closeStream(stream);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case GET_STREAM_DESCRIPTION: {
+            data.readInt32(&stream);
+            ALOGD("BnOboeAudioService::onTransact GET_STREAM_DESCRIPTION 0x%08X", stream);
+            oboe::AudioEndpointParcelable parcelable;
+            result = getStreamDescription(stream, parcelable);
+            if (result != OBOE_OK) {
+                return -1; // FIXME
+            }
+            parcelable.dump();
+            result = parcelable.validate();
+            if (result != OBOE_OK) {
+                return -1; // FIXME
+            }
+            parcelable.writeToParcel(reply);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case START_STREAM: {
+            data.readInt32(&stream);
+            result = startStream(stream);
+            ALOGD("BnOboeAudioService::onTransact START_STREAM 0x%08X, result = %d",
+                    stream, result);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case PAUSE_STREAM: {
+            data.readInt32(&stream);
+            result = pauseStream(stream);
+            ALOGD("BnOboeAudioService::onTransact PAUSE_STREAM 0x%08X, result = %d",
+                    stream, result);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case FLUSH_STREAM: {
+            data.readInt32(&stream);
+            result = flushStream(stream);
+            ALOGD("BnOboeAudioService::onTransact FLUSH_STREAM 0x%08X, result = %d",
+                    stream, result);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case REGISTER_AUDIO_THREAD: {
+            data.readInt32(&stream);
+            data.readInt32(&pid);
+            data.readInt64(&nanoseconds);
+            result = registerAudioThread(stream, pid, nanoseconds);
+            ALOGD("BnOboeAudioService::onTransact REGISTER_AUDIO_THREAD 0x%08X, result = %d",
+                    stream, result);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case UNREGISTER_AUDIO_THREAD: {
+            data.readInt32(&stream);
+            data.readInt32(&pid);
+            result = unregisterAudioThread(stream, pid);
+            ALOGD("BnOboeAudioService::onTransact UNREGISTER_AUDIO_THREAD 0x%08X, result = %d",
+                    stream, result);
+            reply->writeInt32(result);
+            return NO_ERROR;
+        } break;
+
+        case TICKLE: {
+            ALOGV("BnOboeAudioService::onTransact TICKLE");
+            tickle();
+            return NO_ERROR;
+        } break;
+
+        default:
+            // ALOGW("BnOboeAudioService::onTransact not handled %u", code);
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+} /* namespace android */
diff --git a/media/liboboe/src/binding/OboeServiceDefinitions.h b/media/liboboe/src/binding/OboeServiceDefinitions.h
index 958f7a7..ad00fe2 100644
--- a/media/liboboe/src/binding/OboeServiceDefinitions.h
+++ b/media/liboboe/src/binding/OboeServiceDefinitions.h
@@ -27,6 +27,22 @@
 using android::NO_ERROR;
 using android::IBinder;
 
+namespace android {
+
+enum oboe_commands_t {
+    OPEN_STREAM = IBinder::FIRST_CALL_TRANSACTION,
+    CLOSE_STREAM,
+    GET_STREAM_DESCRIPTION,
+    START_STREAM,
+    PAUSE_STREAM,
+    FLUSH_STREAM,
+    REGISTER_AUDIO_THREAD,
+    UNREGISTER_AUDIO_THREAD,
+    TICKLE
+};
+
+} // namespace android
+
 namespace oboe {
 
 enum oboe_commands_t {
diff --git a/media/liboboe/src/binding/OboeServiceMessage.h b/media/liboboe/src/binding/OboeServiceMessage.h
new file mode 100644
index 0000000..aa13571
--- /dev/null
+++ b/media/liboboe/src/binding/OboeServiceMessage.h
@@ -0,0 +1,65 @@
+/*
+ * 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_MESSAGE_H
+#define OBOE_OBOE_SERVICE_MESSAGE_H
+
+#include <stdint.h>
+
+#include <oboe/OboeDefinitions.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_MESSAGE_H
diff --git a/media/liboboe/src/binding/OboeStreamConfiguration.cpp b/media/liboboe/src/binding/OboeStreamConfiguration.cpp
new file mode 100644
index 0000000..4b8b5b2
--- /dev/null
+++ b/media/liboboe/src/binding/OboeStreamConfiguration.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 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.
+ */
+
+#include <stdint.h>
+
+#include <sys/mman.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <oboe/OboeDefinitions.h>
+
+#include "binding/OboeStreamConfiguration.h"
+
+using android::NO_ERROR;
+using android::status_t;
+using android::Parcel;
+using android::Parcelable;
+
+using namespace oboe;
+
+OboeStreamConfiguration::OboeStreamConfiguration() {}
+OboeStreamConfiguration::~OboeStreamConfiguration() {}
+
+status_t OboeStreamConfiguration::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mDeviceId);
+    parcel->writeInt32(mSampleRate);
+    parcel->writeInt32(mSamplesPerFrame);
+    parcel->writeInt32((int32_t) mAudioFormat);
+    return NO_ERROR; // TODO check for errors above
+}
+
+status_t OboeStreamConfiguration::readFromParcel(const Parcel* parcel) {
+    int32_t temp;
+    parcel->readInt32(&mDeviceId);
+    parcel->readInt32(&mSampleRate);
+    parcel->readInt32(&mSamplesPerFrame);
+    parcel->readInt32(&temp);
+    mAudioFormat = (oboe_audio_format_t) temp;
+    return NO_ERROR; // TODO check for errors above
+}
+
+oboe_result_t OboeStreamConfiguration::validate() {
+    // Validate results of the open.
+    if (mSampleRate < 0 || mSampleRate >= 8 * 48000) { // TODO review limits
+        ALOGE("OboeStreamConfiguration.validate(): invalid sampleRate = %d", mSampleRate);
+        return OBOE_ERROR_INTERNAL;
+    }
+
+    if (mSamplesPerFrame < 1 || mSamplesPerFrame >= 32) { // TODO review limits
+        ALOGE("OboeStreamConfiguration.validate() invalid samplesPerFrame = %d", mSamplesPerFrame);
+        return OBOE_ERROR_INTERNAL;
+    }
+
+    switch (mAudioFormat) {
+    case OBOE_AUDIO_FORMAT_PCM16:
+    case OBOE_AUDIO_FORMAT_PCM_FLOAT:
+    case OBOE_AUDIO_FORMAT_PCM824:
+    case OBOE_AUDIO_FORMAT_PCM32:
+        break;
+    default:
+        ALOGE("OboeStreamConfiguration.validate() invalid audioFormat = %d", mAudioFormat);
+        return OBOE_ERROR_INTERNAL;
+    }
+    return OBOE_OK;
+}
+
+void OboeStreamConfiguration::dump() {
+    ALOGD("OboeStreamConfiguration mSampleRate = %d -----", mSampleRate);
+    ALOGD("OboeStreamConfiguration mSamplesPerFrame = %d", mSamplesPerFrame);
+    ALOGD("OboeStreamConfiguration mAudioFormat = %d", (int)mAudioFormat);
+}
diff --git a/media/liboboe/src/binding/OboeStreamRequest.cpp b/media/liboboe/src/binding/OboeStreamRequest.cpp
new file mode 100644
index 0000000..5d521d0
--- /dev/null
+++ b/media/liboboe/src/binding/OboeStreamRequest.cpp
@@ -0,0 +1,66 @@
+/*
+ * Copyright 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.
+ */
+
+#include <stdint.h>
+
+#include <sys/mman.h>
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+
+#include <oboe/OboeDefinitions.h>
+
+#include "binding/OboeStreamConfiguration.h"
+#include "binding/OboeStreamRequest.h"
+
+using android::NO_ERROR;
+using android::status_t;
+using android::Parcel;
+using android::Parcelable;
+
+using namespace oboe;
+
+OboeStreamRequest::OboeStreamRequest()
+    : mConfiguration()
+    {}
+
+OboeStreamRequest::~OboeStreamRequest() {}
+
+status_t OboeStreamRequest::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32((int32_t) mUserId);
+    parcel->writeInt32((int32_t) mProcessId);
+    mConfiguration.writeToParcel(parcel);
+    return NO_ERROR; // TODO check for errors above
+}
+
+status_t OboeStreamRequest::readFromParcel(const Parcel* parcel) {
+    int32_t temp;
+    parcel->readInt32(&temp);
+    mUserId = (uid_t) temp;
+    parcel->readInt32(&temp);
+    mProcessId = (pid_t) temp;
+    mConfiguration.readFromParcel(parcel);
+    return NO_ERROR; // TODO check for errors above
+}
+
+oboe_result_t OboeStreamRequest::validate() {
+    return mConfiguration.validate();
+}
+
+void OboeStreamRequest::dump() {
+    ALOGD("OboeStreamRequest mUserId = %d -----", mUserId);
+    ALOGD("OboeStreamRequest mProcessId = %d", mProcessId);
+    mConfiguration.dump();
+}
diff --git a/media/liboboe/src/binding/RingBufferParcelable.cpp b/media/liboboe/src/binding/RingBufferParcelable.cpp
new file mode 100644
index 0000000..f097655
--- /dev/null
+++ b/media/liboboe/src/binding/RingBufferParcelable.cpp
@@ -0,0 +1,170 @@
+/*
+ * Copyright 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.
+ */
+
+#include <stdint.h>
+
+#include <binder/Parcelable.h>
+
+#include "binding/OboeServiceDefinitions.h"
+#include "binding/SharedRegionParcelable.h"
+#include "binding/RingBufferParcelable.h"
+
+using namespace oboe;
+
+RingBufferParcelable::RingBufferParcelable() {}
+RingBufferParcelable::~RingBufferParcelable() {}
+
+// TODO This assumes that all three use the same SharedMemoryParcelable
+void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex,
+                 int32_t dataMemoryOffset,
+                 int32_t dataSizeInBytes,
+                 int32_t readCounterOffset,
+                 int32_t writeCounterOffset,
+                 int32_t counterSizeBytes) {
+    mReadCounterParcelable.setup(sharedMemoryIndex, readCounterOffset, counterSizeBytes);
+    mWriteCounterParcelable.setup(sharedMemoryIndex, writeCounterOffset, counterSizeBytes);
+    mDataParcelable.setup(sharedMemoryIndex, dataMemoryOffset, dataSizeInBytes);
+}
+
+void RingBufferParcelable::setupMemory(int32_t sharedMemoryIndex,
+                 int32_t dataMemoryOffset,
+                 int32_t dataSizeInBytes) {
+    mReadCounterParcelable.setup(sharedMemoryIndex, 0, 0);
+    mWriteCounterParcelable.setup(sharedMemoryIndex, 0, 0);
+    mDataParcelable.setup(sharedMemoryIndex, dataMemoryOffset, dataSizeInBytes);
+}
+
+int32_t RingBufferParcelable::getBytesPerFrame() {
+    return mBytesPerFrame;
+}
+
+void RingBufferParcelable::setBytesPerFrame(int32_t bytesPerFrame) {
+    mBytesPerFrame = bytesPerFrame;
+}
+
+int32_t RingBufferParcelable::getFramesPerBurst() {
+    return mFramesPerBurst;
+}
+
+void RingBufferParcelable::setFramesPerBurst(int32_t framesPerBurst) {
+    mFramesPerBurst = framesPerBurst;
+}
+
+int32_t RingBufferParcelable::getCapacityInFrames() {
+    return mCapacityInFrames;
+}
+
+void RingBufferParcelable::setCapacityInFrames(int32_t capacityInFrames) {
+    mCapacityInFrames = capacityInFrames;
+}
+
+/**
+ * The read and write must be symmetric.
+ */
+status_t RingBufferParcelable::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mCapacityInFrames);
+    if (mCapacityInFrames > 0) {
+        parcel->writeInt32(mBytesPerFrame);
+        parcel->writeInt32(mFramesPerBurst);
+        parcel->writeInt32(mFlags);
+        mReadCounterParcelable.writeToParcel(parcel);
+        mWriteCounterParcelable.writeToParcel(parcel);
+        mDataParcelable.writeToParcel(parcel);
+    }
+    return NO_ERROR; // TODO check for errors above
+}
+
+status_t RingBufferParcelable::readFromParcel(const Parcel* parcel) {
+    parcel->readInt32(&mCapacityInFrames);
+    if (mCapacityInFrames > 0) {
+        parcel->readInt32(&mBytesPerFrame);
+        parcel->readInt32(&mFramesPerBurst);
+        parcel->readInt32((int32_t *)&mFlags);
+        mReadCounterParcelable.readFromParcel(parcel);
+        mWriteCounterParcelable.readFromParcel(parcel);
+        mDataParcelable.readFromParcel(parcel);
+    }
+    return NO_ERROR; // TODO check for errors above
+}
+
+oboe_result_t RingBufferParcelable::resolve(SharedMemoryParcelable *memoryParcels, RingBufferDescriptor *descriptor) {
+    oboe_result_t result;
+
+    result = mReadCounterParcelable.resolve(memoryParcels,
+                                            (void **) &descriptor->readCounterAddress);
+    if (result != OBOE_OK) {
+        return result;
+    }
+
+    result = mWriteCounterParcelable.resolve(memoryParcels,
+                                             (void **) &descriptor->writeCounterAddress);
+    if (result != OBOE_OK) {
+        return result;
+    }
+
+    result = mDataParcelable.resolve(memoryParcels, (void **) &descriptor->dataAddress);
+    if (result != OBOE_OK) {
+        return result;
+    }
+
+    descriptor->bytesPerFrame = mBytesPerFrame;
+    descriptor->framesPerBurst = mFramesPerBurst;
+    descriptor->capacityInFrames = mCapacityInFrames;
+    descriptor->flags = mFlags;
+    return OBOE_OK;
+}
+
+oboe_result_t RingBufferParcelable::validate() {
+    oboe_result_t result;
+    if (mCapacityInFrames < 0 || mCapacityInFrames >= 32 * 1024) {
+        ALOGE("RingBufferParcelable invalid mCapacityInFrames = %d", mCapacityInFrames);
+        return OBOE_ERROR_INTERNAL;
+    }
+    if (mBytesPerFrame < 0 || mBytesPerFrame >= 256) {
+        ALOGE("RingBufferParcelable invalid mBytesPerFrame = %d", mBytesPerFrame);
+        return OBOE_ERROR_INTERNAL;
+    }
+    if (mFramesPerBurst < 0 || mFramesPerBurst >= 1024) {
+        ALOGE("RingBufferParcelable invalid mFramesPerBurst = %d", mFramesPerBurst);
+        return OBOE_ERROR_INTERNAL;
+    }
+    if ((result = mReadCounterParcelable.validate()) != OBOE_OK) {
+        ALOGE("RingBufferParcelable invalid mReadCounterParcelable = %d", result);
+        return result;
+    }
+    if ((result = mWriteCounterParcelable.validate()) != OBOE_OK) {
+        ALOGE("RingBufferParcelable invalid mWriteCounterParcelable = %d", result);
+        return result;
+    }
+    if ((result = mDataParcelable.validate()) != OBOE_OK) {
+        ALOGE("RingBufferParcelable invalid mDataParcelable = %d", result);
+        return result;
+    }
+    return OBOE_OK;
+}
+
+
+void RingBufferParcelable::dump() {
+    ALOGD("RingBufferParcelable mCapacityInFrames = %d ---------", mCapacityInFrames);
+    if (mCapacityInFrames > 0) {
+        ALOGD("RingBufferParcelable mBytesPerFrame = %d", mBytesPerFrame);
+        ALOGD("RingBufferParcelable mFramesPerBurst = %d", mFramesPerBurst);
+        ALOGD("RingBufferParcelable mFlags = %u", mFlags);
+        mReadCounterParcelable.dump();
+        mWriteCounterParcelable.dump();
+        mDataParcelable.dump();
+    }
+}
diff --git a/media/liboboe/src/binding/SharedMemoryParcelable.cpp b/media/liboboe/src/binding/SharedMemoryParcelable.cpp
new file mode 100644
index 0000000..5b739c0
--- /dev/null
+++ b/media/liboboe/src/binding/SharedMemoryParcelable.cpp
@@ -0,0 +1,107 @@
+/*
+ * Copyright 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.
+ */
+
+#include <stdint.h>
+
+#include <sys/mman.h>
+#include <oboe/OboeDefinitions.h>
+
+#include <binder/Parcelable.h>
+
+#include "binding/SharedMemoryParcelable.h"
+
+using android::NO_ERROR;
+using android::status_t;
+using android::Parcel;
+using android::Parcelable;
+
+using namespace oboe;
+
+SharedMemoryParcelable::SharedMemoryParcelable() {}
+SharedMemoryParcelable::~SharedMemoryParcelable() {};
+
+void SharedMemoryParcelable::setup(int fd, int32_t sizeInBytes) {
+    mFd = fd;
+    mSizeInBytes = sizeInBytes;
+}
+
+status_t SharedMemoryParcelable::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mSizeInBytes);
+    if (mSizeInBytes > 0) {
+        parcel->writeDupFileDescriptor(mFd);
+    }
+    return NO_ERROR; // TODO check for errors above
+}
+
+status_t SharedMemoryParcelable::readFromParcel(const Parcel* parcel) {
+    parcel->readInt32(&mSizeInBytes);
+    if (mSizeInBytes > 0) {
+        mFd = dup(parcel->readFileDescriptor());
+    }
+    return NO_ERROR; // TODO check for errors above
+}
+
+// TODO Add code to unmmap()
+
+oboe_result_t SharedMemoryParcelable::resolve(int32_t offsetInBytes, int32_t sizeInBytes,
+                                              void **regionAddressPtr) {
+    if (offsetInBytes < 0) {
+        ALOGE("SharedMemoryParcelable illegal offsetInBytes = %d", offsetInBytes);
+        return OBOE_ERROR_OUT_OF_RANGE;
+    } else if ((offsetInBytes + sizeInBytes) > mSizeInBytes) {
+        ALOGE("SharedMemoryParcelable out of range, offsetInBytes = %d, "
+              "sizeInBytes = %d, mSizeInBytes = %d",
+              offsetInBytes, sizeInBytes, mSizeInBytes);
+        return OBOE_ERROR_OUT_OF_RANGE;
+    }
+    if (mResolvedAddress == nullptr) {
+        mResolvedAddress = (uint8_t *) mmap(0, mSizeInBytes, PROT_READ|PROT_WRITE,
+                                          MAP_SHARED, mFd, 0);
+        if (mResolvedAddress == nullptr) {
+            ALOGE("SharedMemoryParcelable mmap failed for fd = %d", mFd);
+            return OBOE_ERROR_INTERNAL;
+        }
+    }
+    *regionAddressPtr = mResolvedAddress + offsetInBytes;
+    ALOGD("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
+    ALOGD("SharedMemoryParcelable offset by %d, *regionAddressPtr = %p",
+          offsetInBytes, *regionAddressPtr);
+    return OBOE_OK;
+}
+
+int32_t SharedMemoryParcelable::getSizeInBytes() {
+    return mSizeInBytes;
+}
+
+oboe_result_t SharedMemoryParcelable::validate() {
+    if (mSizeInBytes < 0 || mSizeInBytes >= MAX_MMAP_SIZE) {
+        ALOGE("SharedMemoryParcelable invalid mSizeInBytes = %d", mSizeInBytes);
+        return OBOE_ERROR_INTERNAL;
+    }
+    if (mSizeInBytes > 0) {
+        if (mFd == -1) {
+            ALOGE("SharedMemoryParcelable uninitialized mFd = %d", mFd);
+            return OBOE_ERROR_INTERNAL;
+        }
+    }
+    return OBOE_OK;
+}
+
+void SharedMemoryParcelable::dump() {
+    ALOGD("SharedMemoryParcelable mFd = %d", mFd);
+    ALOGD("SharedMemoryParcelable mSizeInBytes = %d", mSizeInBytes);
+    ALOGD("SharedMemoryParcelable mResolvedAddress = %p", mResolvedAddress);
+}
diff --git a/media/liboboe/src/binding/SharedRegionParcelable.cpp b/media/liboboe/src/binding/SharedRegionParcelable.cpp
new file mode 100644
index 0000000..86ce8f3
--- /dev/null
+++ b/media/liboboe/src/binding/SharedRegionParcelable.cpp
@@ -0,0 +1,101 @@
+/*
+ * Copyright 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.
+ */
+
+#include <stdint.h>
+
+#include <sys/mman.h>
+#include <binder/Parcelable.h>
+
+#include <oboe/OboeDefinitions.h>
+
+#include "binding/SharedMemoryParcelable.h"
+#include "binding/SharedRegionParcelable.h"
+
+using android::NO_ERROR;
+using android::status_t;
+using android::Parcel;
+using android::Parcelable;
+
+using namespace oboe;
+
+SharedRegionParcelable::SharedRegionParcelable() {}
+SharedRegionParcelable::~SharedRegionParcelable() {}
+
+void SharedRegionParcelable::setup(int32_t sharedMemoryIndex,
+                                   int32_t offsetInBytes,
+                                   int32_t sizeInBytes) {
+    mSharedMemoryIndex = sharedMemoryIndex;
+    mOffsetInBytes = offsetInBytes;
+    mSizeInBytes = sizeInBytes;
+}
+
+status_t SharedRegionParcelable::writeToParcel(Parcel* parcel) const {
+    parcel->writeInt32(mSizeInBytes);
+    if (mSizeInBytes > 0) {
+        parcel->writeInt32(mSharedMemoryIndex);
+        parcel->writeInt32(mOffsetInBytes);
+    }
+    return NO_ERROR; // TODO check for errors above
+}
+
+status_t SharedRegionParcelable::readFromParcel(const Parcel* parcel) {
+    parcel->readInt32(&mSizeInBytes);
+    if (mSizeInBytes > 0) {
+        parcel->readInt32(&mSharedMemoryIndex);
+        parcel->readInt32(&mOffsetInBytes);
+    }
+    return NO_ERROR; // TODO check for errors above
+}
+
+oboe_result_t SharedRegionParcelable::resolve(SharedMemoryParcelable *memoryParcels,
+                                              void **regionAddressPtr) {
+    if (mSizeInBytes == 0) {
+        *regionAddressPtr = nullptr;
+        return OBOE_OK;
+    }
+    if (mSharedMemoryIndex < 0) {
+        ALOGE("SharedRegionParcelable invalid mSharedMemoryIndex = %d", mSharedMemoryIndex);
+        return OBOE_ERROR_INTERNAL;
+    }
+    SharedMemoryParcelable *memoryParcel = &memoryParcels[mSharedMemoryIndex];
+    return memoryParcel->resolve(mOffsetInBytes, mSizeInBytes, regionAddressPtr);
+}
+
+oboe_result_t SharedRegionParcelable::validate() {
+    if (mSizeInBytes < 0 || mSizeInBytes >= MAX_MMAP_SIZE) {
+        ALOGE("SharedRegionParcelable invalid mSizeInBytes = %d", mSizeInBytes);
+        return OBOE_ERROR_INTERNAL;
+    }
+    if (mSizeInBytes > 0) {
+        if (mOffsetInBytes < 0 || mOffsetInBytes >= MAX_MMAP_OFFSET) {
+            ALOGE("SharedRegionParcelable invalid mOffsetInBytes = %d", mOffsetInBytes);
+            return OBOE_ERROR_INTERNAL;
+        }
+        if (mSharedMemoryIndex < 0 || mSharedMemoryIndex >= MAX_SHARED_MEMORIES) {
+            ALOGE("SharedRegionParcelable invalid mSharedMemoryIndex = %d", mSharedMemoryIndex);
+            return OBOE_ERROR_INTERNAL;
+        }
+    }
+    return OBOE_OK;
+}
+
+void SharedRegionParcelable::dump() {
+    ALOGD("SharedRegionParcelable mSizeInBytes = %d -----", mSizeInBytes);
+    if (mSizeInBytes > 0) {
+        ALOGD("SharedRegionParcelable mSharedMemoryIndex = %d", mSharedMemoryIndex);
+        ALOGD("SharedRegionParcelable mOffsetInBytes = %d", mOffsetInBytes);
+    }
+}