liboboe: initial checkin of core and legacy files.

Oboe C++ files that calls AudioTrack and AudioRecord.
Main C API implemented by src/core/OboeAudio.cpp

Test: gunit tests for the Legacy mode and handle tracker in tests folder
Bug: 33347409
Change-Id: I50f9fd99377efbd8de6fef1601e9af4c22c6ab46
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/liboboe/src/Android.mk b/media/liboboe/src/Android.mk
new file mode 100644
index 0000000..7b9a906
--- /dev/null
+++ b/media/liboboe/src/Android.mk
@@ -0,0 +1,70 @@
+LOCAL_PATH:= $(call my-dir)
+
+# ======================= STATIC LIBRARY ==========================
+# This is being built because it make Oboe testing very easy with a complete executable.
+# TODO Remove this target later, when not needed.
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := liboboe
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/native/include \
+    system/core/base/include \
+    frameworks/native/media/liboboe/include/include \
+    frameworks/av/media/liboboe/include \
+    $(LOCAL_PATH)/core \
+    $(LOCAL_PATH)/utility \
+    $(LOCAL_PATH)/legacy
+
+LOCAL_SRC_FILES += core/AudioStream.cpp
+LOCAL_SRC_FILES += core/AudioStreamBuilder.cpp
+LOCAL_SRC_FILES += core/OboeAudio.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamRecord.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamTrack.cpp
+LOCAL_SRC_FILES += utility/HandleTracker.cpp
+LOCAL_SRC_FILES += utility/OboeUtilities.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+# By default, all symbols are hidden.
+LOCAL_CFLAGS += -fvisibility=hidden
+# OBOE_API is used to explicitly export a function or a variable as a visible symbol.
+LOCAL_CFLAGS += -DOBOE_API='__attribute__((visibility("default")))'
+
+include $(BUILD_STATIC_LIBRARY)
+
+# ======================= SHARED LIBRARY ==========================
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := liboboe
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/native/include \
+    system/core/base/include \
+    frameworks/native/media/liboboe/include/include \
+    frameworks/av/media/liboboe/include \
+    $(LOCAL_PATH)/core \
+    $(LOCAL_PATH)/utility \
+    $(LOCAL_PATH)/legacy
+
+LOCAL_SRC_FILES += core/AudioStream.cpp
+LOCAL_SRC_FILES += core/AudioStreamBuilder.cpp
+LOCAL_SRC_FILES += core/OboeAudio.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamRecord.cpp
+LOCAL_SRC_FILES += legacy/AudioStreamTrack.cpp
+LOCAL_SRC_FILES += utility/HandleTracker.cpp
+LOCAL_SRC_FILES += utility/OboeUtilities.cpp
+
+LOCAL_CFLAGS += -Wno-unused-parameter
+LOCAL_CFLAGS += -Wall -Werror
+# By default, all symbols are hidden.
+LOCAL_CFLAGS += -fvisibility=hidden
+# OBOE_API is used to explicitly export a function or a variable as a visible symbol.
+LOCAL_CFLAGS += -DOBOE_API='__attribute__((visibility("default")))'
+
+LOCAL_SHARED_LIBRARIES := libaudioclient liblog libutils
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/liboboe/src/core/AudioStream.cpp b/media/liboboe/src/core/AudioStream.cpp
new file mode 100644
index 0000000..f154002
--- /dev/null
+++ b/media/liboboe/src/core/AudioStream.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright 2015 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "AudioClock.h"
+
+using namespace oboe;
+
+/*
+ * AudioStream
+ */
+AudioStream::AudioStream() {
+}
+
+oboe_result_t AudioStream::open(const AudioStreamBuilder& builder)
+{
+    // TODO validate parameters.
+    // Copy parameters from the Builder because the Builder may be deleted after this call.
+    mSamplesPerFrame = builder.getSamplesPerFrame();
+    mSampleRate = builder.getSampleRate();
+    mDeviceId = builder.getDeviceId();
+    mFormat = builder.getFormat();
+    mSharingMode = builder.getSharingMode();
+    return OBOE_OK;
+}
+
+AudioStream::~AudioStream() {
+    close();
+}
+
+oboe_result_t AudioStream::waitForStateTransition(oboe_stream_state_t startingState,
+                                               oboe_stream_state_t endingState,
+                                               oboe_nanoseconds_t timeoutNanoseconds)
+{
+    oboe_stream_state_t state = getState();
+    oboe_stream_state_t nextState = state;
+    if (state == startingState && state != endingState) {
+        oboe_result_t result = waitForStateChange(state, &nextState, timeoutNanoseconds);
+        if (result != OBOE_OK) {
+            return result;
+        }
+    }
+// It's OK if the expected transition has already occurred.
+// But if we reach an unexpected state then that is an error.
+    if (nextState != endingState) {
+        return OBOE_ERROR_UNEXPECTED_STATE;
+    } else {
+        return OBOE_OK;
+    }
+}
+
+oboe_result_t AudioStream::waitForStateChange(oboe_stream_state_t currentState,
+                                                oboe_stream_state_t *nextState,
+                                                oboe_nanoseconds_t timeoutNanoseconds)
+{
+    // TODO replace this when similar functionality added to AudioTrack.cpp
+    oboe_nanoseconds_t durationNanos = 20 * OBOE_NANOS_PER_MILLISECOND;
+    oboe_stream_state_t state = getState();
+    while (state == currentState && timeoutNanoseconds > 0) {
+        if (durationNanos > timeoutNanoseconds) {
+            durationNanos = timeoutNanoseconds;
+        }
+        AudioClock::sleepForNanos(durationNanos);
+        timeoutNanoseconds -= durationNanos;
+
+        oboe_result_t result = updateState();
+        if (result != OBOE_OK) {
+            return result;
+        }
+
+        state = getState();
+    }
+    if (nextState != NULL) {
+        *nextState = state;
+    }
+    return (state == currentState) ? OBOE_ERROR_TIMEOUT : OBOE_OK;
+}
+
+oboe_result_t AudioStream::createThread(oboe_nanoseconds_t periodNanoseconds,
+                                     void *(*startRoutine)(void *), void *arg)
+{
+    if (mHasThread) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    if (startRoutine == NULL) {
+        return OBOE_ERROR_NULL;
+    }
+    int err = pthread_create(&mThread, NULL, startRoutine, arg);
+    if (err != 0) {
+        return OBOE_ERROR_INTERNAL;
+    } else {
+        mHasThread = true;
+        return OBOE_OK;
+    }
+}
+
+oboe_result_t AudioStream::joinThread(void **returnArg, oboe_nanoseconds_t timeoutNanoseconds)
+{
+    if (!mHasThread) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+#if 0
+    // TODO implement equivalent of pthread_timedjoin_np()
+    struct timespec abstime;
+    int err = pthread_timedjoin_np(mThread, returnArg, &abstime);
+#else
+    int err = pthread_join(mThread, returnArg);
+#endif
+    mHasThread = false;
+    // TODO Just leaked a thread?
+    return err ? OBOE_ERROR_INTERNAL : OBOE_OK;
+}
+
diff --git a/media/liboboe/src/core/AudioStream.h b/media/liboboe/src/core/AudioStream.h
new file mode 100644
index 0000000..9cb9b1b
--- /dev/null
+++ b/media/liboboe/src/core/AudioStream.h
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+
+#ifndef OBOE_AUDIOSTREAM_H
+#define OBOE_AUDIOSTREAM_H
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <oboe/OboeAudio.h>
+#include "OboeUtilities.h"
+#include "MonotonicCounter.h"
+
+namespace oboe {
+
+class AudioStreamBuilder;
+
+/**
+ * Oboe audio stream.
+ */
+class AudioStream {
+public:
+
+    AudioStream();
+
+    virtual ~AudioStream();
+
+
+    // =========== Begin ABSTRACT methods ===========================
+
+    /* Asynchronous requests.
+     * Use waitForStateChange() to wait for completion.
+     */
+    virtual oboe_result_t requestStart() = 0;
+    virtual oboe_result_t requestPause() = 0;
+    virtual oboe_result_t requestFlush() = 0;
+    virtual oboe_result_t requestStop() = 0;
+
+    // TODO use oboe_clockid_t all the way down to AudioClock
+    virtual oboe_result_t getTimestamp(clockid_t clockId,
+                                       oboe_position_frames_t *framePosition,
+                                       oboe_nanoseconds_t *timeNanoseconds) = 0;
+
+
+    virtual oboe_result_t updateState() = 0;
+
+
+    // =========== End ABSTRACT methods ===========================
+
+    virtual oboe_result_t waitForStateChange(oboe_stream_state_t currentState,
+                                          oboe_stream_state_t *nextState,
+                                          oboe_nanoseconds_t timeoutNanoseconds);
+
+    /**
+     * Open the stream using the parameters in the builder.
+     * Allocate the necessary resources.
+     */
+    virtual oboe_result_t open(const AudioStreamBuilder& builder);
+
+    /**
+     * Close the stream and deallocate any resources from the open() call.
+     * It is safe to call close() multiple times.
+     */
+    virtual oboe_result_t close() {
+        return OBOE_OK;
+    }
+
+    virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+                                        oboe_size_frames_t *actualFrames) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual oboe_result_t createThread(oboe_nanoseconds_t periodNanoseconds,
+                                     void *(*start_routine)(void *), void *arg);
+
+    virtual oboe_result_t joinThread(void **returnArg, oboe_nanoseconds_t timeoutNanoseconds);
+
+    // ============== Queries ===========================
+
+    virtual oboe_stream_state_t getState() const {
+        return mState;
+    }
+
+    virtual oboe_size_frames_t getBufferSize() const {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual oboe_size_frames_t getBufferCapacity() const {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual oboe_size_frames_t getFramesPerBurst() const {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual int32_t getXRunCount() const {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    bool isPlaying() const {
+        return mState == OBOE_STREAM_STATE_STARTING || mState == OBOE_STREAM_STATE_STARTED;
+    }
+
+    oboe_result_t getSampleRate() const {
+        return mSampleRate;
+    }
+
+    oboe_audio_format_t getFormat()  const {
+        return mFormat;
+    }
+
+    oboe_result_t getSamplesPerFrame() const {
+        return mSamplesPerFrame;
+    }
+
+    OboeDeviceId getDeviceId() const {
+        return mDeviceId;
+    }
+
+    oboe_sharing_mode_t getSharingMode() const {
+        return mSharingMode;
+    }
+
+    oboe_direction_t getDirection() const {
+        return mDirection;
+    }
+
+    oboe_size_bytes_t getBytesPerFrame() const {
+        return mSamplesPerFrame * getBytesPerSample();
+    }
+
+    oboe_size_bytes_t getBytesPerSample() const {
+        return OboeConvert_formatToSizeInBytes(mFormat);
+    }
+
+    virtual oboe_position_frames_t getFramesWritten() {
+        return mFramesWritten.get();
+    }
+
+    virtual oboe_position_frames_t getFramesRead() {
+        return mFramesRead.get();
+    }
+
+
+    // ============== I/O ===========================
+    // A Stream will only implement read() or write() depending on its direction.
+    virtual oboe_result_t write(const void *buffer,
+                             oboe_size_frames_t numFrames,
+                             oboe_nanoseconds_t timeoutNanoseconds) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+    virtual oboe_result_t read(void *buffer,
+                            oboe_size_frames_t numFrames,
+                            oboe_nanoseconds_t timeoutNanoseconds) {
+        return OBOE_ERROR_UNIMPLEMENTED;
+    }
+
+protected:
+
+    virtual oboe_position_frames_t incrementFramesWritten(oboe_size_frames_t frames) {
+        return static_cast<oboe_position_frames_t>(mFramesWritten.increment(frames));
+    }
+
+    virtual oboe_position_frames_t incrementFramesRead(oboe_size_frames_t frames) {
+        return static_cast<oboe_position_frames_t>(mFramesRead.increment(frames));
+    }
+
+    /**
+     * Wait for a transition from one state to another.
+     * @return OBOE_OK if the endingState was observed, or OBOE_ERROR_UNEXPECTED_STATE
+     *   if any state that was not the startingState or endingState was observed
+     *   or OBOE_ERROR_TIMEOUT
+     */
+    virtual oboe_result_t waitForStateTransition(oboe_stream_state_t startingState,
+                                              oboe_stream_state_t endingState,
+                                              oboe_nanoseconds_t timeoutNanoseconds);
+
+    /**
+     * This should not be called after the open() call.
+     */
+    void setSampleRate(oboe_sample_rate_t sampleRate) {
+        mSampleRate = sampleRate;
+    }
+
+    /**
+     * This should not be called after the open() call.
+     */
+    void setSamplesPerFrame(int32_t samplesPerFrame) {
+        mSamplesPerFrame = samplesPerFrame;
+    }
+
+    /**
+     * This should not be called after the open() call.
+     */
+    void setSharingMode(oboe_sharing_mode_t sharingMode) {
+        mSharingMode = sharingMode;
+    }
+
+    /**
+     * This should not be called after the open() call.
+     */
+    void setFormat(oboe_audio_format_t format) {
+        mFormat = format;
+    }
+
+    void setState(oboe_stream_state_t state) {
+        mState = state;
+    }
+
+    MonotonicCounter     mFramesWritten;
+    MonotonicCounter     mFramesRead;
+
+private:
+    // These do not change after open().
+    int32_t              mSamplesPerFrame = OBOE_UNSPECIFIED;
+    oboe_sample_rate_t   mSampleRate = OBOE_UNSPECIFIED;
+    oboe_stream_state_t  mState = OBOE_STREAM_STATE_UNINITIALIZED;
+    OboeDeviceId         mDeviceId = OBOE_UNSPECIFIED;
+    oboe_sharing_mode_t  mSharingMode = OBOE_SHARING_MODE_LEGACY;
+    oboe_audio_format_t  mFormat = OBOE_UNSPECIFIED;
+    oboe_direction_t     mDirection = OBOE_DIRECTION_OUTPUT;
+
+    bool                 mHasThread;
+    pthread_t            mThread;
+};
+
+} /* namespace oboe */
+
+#endif /* OBOE_AUDIOSTREAM_H */
diff --git a/media/liboboe/src/core/AudioStreamBuilder.cpp b/media/liboboe/src/core/AudioStreamBuilder.cpp
new file mode 100644
index 0000000..56e6706
--- /dev/null
+++ b/media/liboboe/src/core/AudioStreamBuilder.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2015 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <sys/types.h>
+#include "AudioStream.h"
+#include "AudioStreamBuilder.h"
+#include "AudioStreamRecord.h"
+#include "AudioStreamTrack.h"
+
+using namespace oboe;
+
+/*
+ * AudioStreamBuilder
+ */
+AudioStreamBuilder::AudioStreamBuilder() {
+}
+
+AudioStreamBuilder::~AudioStreamBuilder() {
+}
+
+oboe_result_t AudioStreamBuilder::build(AudioStream **streamPtr) {
+    // TODO Is there a better place to put the code that decides which class to use?
+    AudioStream *audioStream = nullptr;
+    const oboe_sharing_mode_t sharingMode = getSharingMode();
+    switch (getDirection()) {
+    case OBOE_DIRECTION_INPUT:
+        switch (sharingMode) {
+            case OBOE_SHARING_MODE_LEGACY:
+                audioStream = new AudioStreamRecord();
+                break;
+            default:
+                ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
+                return OBOE_ERROR_ILLEGAL_ARGUMENT;
+                break;
+        }
+        break;
+    case OBOE_DIRECTION_OUTPUT:
+        switch (sharingMode) {
+            case OBOE_SHARING_MODE_LEGACY:
+                audioStream = new AudioStreamTrack();
+                break;
+            default:
+                ALOGE("AudioStreamBuilder(): bad sharing mode = %d", sharingMode);
+                return OBOE_ERROR_ILLEGAL_ARGUMENT;
+                break;
+        }
+        break;
+    default:
+        ALOGE("AudioStreamBuilder(): bad direction = %d", getDirection());
+        return OBOE_ERROR_ILLEGAL_ARGUMENT;
+        break;
+    }
+    if (audioStream == nullptr) {
+        return OBOE_ERROR_NO_MEMORY;
+    }
+    ALOGD("AudioStreamBuilder(): created audioStream = %p", audioStream);
+
+    // TODO maybe move this out of build and pass the builder to the constructors
+    // Open the stream using the parameters from the builder.
+    const oboe_result_t result = audioStream->open(*this);
+    if (result != OBOE_OK) {
+        delete audioStream;
+    } else {
+        *streamPtr = audioStream;
+    }
+    return result;
+}
diff --git a/media/liboboe/src/core/AudioStreamBuilder.h b/media/liboboe/src/core/AudioStreamBuilder.h
new file mode 100644
index 0000000..3f98ebb
--- /dev/null
+++ b/media/liboboe/src/core/AudioStreamBuilder.h
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015 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_AUDIOSTREAMBUILDER_H
+#define OBOE_AUDIOSTREAMBUILDER_H
+
+#include <oboe/OboeAudio.h>
+#include "AudioStream.h"
+
+namespace oboe {
+
+/**
+ * Factory class for an AudioStream.
+ */
+class AudioStreamBuilder {
+public:
+    AudioStreamBuilder();
+
+    ~AudioStreamBuilder();
+
+    int getSamplesPerFrame() const {
+        return mSamplesPerFrame;
+    }
+
+    /**
+     * This is also known as channelCount.
+     */
+    AudioStreamBuilder *setSamplesPerFrame(int samplesPerFrame) {
+        mSamplesPerFrame = samplesPerFrame;
+        return this;
+    }
+
+    oboe_direction_t getDirection() const {
+        return mDirection;
+    }
+
+    AudioStreamBuilder *setDirection(oboe_direction_t direction) {
+        mDirection = direction;
+        return this;
+    }
+
+    oboe_sample_rate_t getSampleRate() const {
+        return mSampleRate;
+    }
+
+    AudioStreamBuilder *setSampleRate(oboe_sample_rate_t sampleRate) {
+        mSampleRate = sampleRate;
+        return this;
+    }
+
+    oboe_audio_format_t getFormat() const {
+        return mFormat;
+    }
+
+    AudioStreamBuilder *setFormat(oboe_audio_format_t format) {
+        mFormat = format;
+        return this;
+    }
+
+    oboe_sharing_mode_t getSharingMode() const {
+        return mSharingMode;
+    }
+
+    AudioStreamBuilder *setSharingMode(oboe_sharing_mode_t sharingMode) {
+        mSharingMode = sharingMode;
+        return this;
+    }
+
+    OboeDeviceId getDeviceId() const {
+        return mDeviceId;
+    }
+
+    AudioStreamBuilder *setDeviceId(OboeDeviceId deviceId) {
+        mDeviceId = deviceId;
+        return this;
+    }
+
+    oboe_result_t build(AudioStream **streamPtr);
+
+private:
+    int32_t              mSamplesPerFrame = OBOE_UNSPECIFIED;
+    oboe_sample_rate_t   mSampleRate = OBOE_UNSPECIFIED;
+    OboeDeviceId         mDeviceId = OBOE_UNSPECIFIED; // TODO need better default
+    oboe_sharing_mode_t  mSharingMode = OBOE_SHARING_MODE_LEGACY;
+    oboe_audio_format_t  mFormat = OBOE_UNSPECIFIED;
+    oboe_direction_t     mDirection = OBOE_DIRECTION_OUTPUT;
+};
+
+} /* namespace oboe */
+
+#endif /* OBOE_AUDIOSTREAMBUILDER_H */
diff --git a/media/liboboe/src/core/OboeAudio.cpp b/media/liboboe/src/core/OboeAudio.cpp
new file mode 100644
index 0000000..fc1b021
--- /dev/null
+++ b/media/liboboe/src/core/OboeAudio.cpp
@@ -0,0 +1,555 @@
+/*
+ * 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <time.h>
+#include <pthread.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "AudioClock.h"
+#include "HandleTracker.h"
+
+// temporary, as I stage in the MMAP/NOIRQ support, do not review
+#ifndef OBOE_SUPPORT_MMAP
+#define OBOE_SUPPORT_MMAP 0
+#endif
+
+#if OBOE_SUPPORT_MMAP
+#include "AudioStreamInternal.h"
+#include "OboeServiceGateway.h"
+#endif
+
+using namespace oboe;
+
+// This is not the maximum theoretic possible number of handles that the HandlerTracker
+// class could support; instead it is the maximum number of handles that we are configuring
+// for our HandleTracker instance (sHandleTracker).
+#define OBOE_MAX_HANDLES  64
+
+// Macros for common code that includes a return.
+// TODO Consider using do{}while(0) construct. I tried but it hung AndroidStudio
+#define CONVERT_BUILDER_HANDLE_OR_RETURN() \
+    convertOboeBuilderToStreamBuilder(builder); \
+    if (streamBuilder == nullptr) { \
+        return OBOE_ERROR_INVALID_HANDLE; \
+    }
+
+#define COMMON_GET_FROM_BUILDER_OR_RETURN(resultPtr) \
+    CONVERT_BUILDER_HANDLE_OR_RETURN() \
+    if ((resultPtr) == nullptr) { \
+        return OBOE_ERROR_NULL; \
+    }
+
+#define CONVERT_STREAM_HANDLE_OR_RETURN() \
+    convertOboeStreamToAudioStream(stream); \
+    if (audioStream == nullptr) { \
+        return OBOE_ERROR_INVALID_HANDLE; \
+    }
+
+#define COMMON_GET_FROM_STREAM_OR_RETURN(resultPtr) \
+    CONVERT_STREAM_HANDLE_OR_RETURN(); \
+    if ((resultPtr) == nullptr) { \
+        return OBOE_ERROR_NULL; \
+    }
+
+static HandleTracker sHandleTracker(OBOE_MAX_HANDLES);
+
+typedef enum
+{
+    OBOE_HANDLE_TYPE_STREAM,
+    OBOE_HANDLE_TYPE_STREAM_BUILDER,
+    OBOE_HANDLE_TYPE_COUNT
+} oboe_handle_type_t;
+static_assert(OBOE_HANDLE_TYPE_COUNT <= HANDLE_TRACKER_MAX_TYPES, "Too many handle types.");
+
+#if OBOE_SUPPORT_MMAP
+static OboeServiceGateway sOboeServiceGateway;
+#endif
+
+#define OBOE_CASE_ENUM(name) case name: return #name
+
+OBOE_API const char * Oboe_convertResultToText(oboe_result_t returnCode) {
+    switch (returnCode) {
+        OBOE_CASE_ENUM(OBOE_OK);
+        OBOE_CASE_ENUM(OBOE_ERROR_ILLEGAL_ARGUMENT);
+        OBOE_CASE_ENUM(OBOE_ERROR_INCOMPATIBLE);
+        OBOE_CASE_ENUM(OBOE_ERROR_INTERNAL);
+        OBOE_CASE_ENUM(OBOE_ERROR_INVALID_STATE);
+        OBOE_CASE_ENUM(OBOE_ERROR_INVALID_HANDLE);
+        OBOE_CASE_ENUM(OBOE_ERROR_INVALID_QUERY);
+        OBOE_CASE_ENUM(OBOE_ERROR_UNIMPLEMENTED);
+        OBOE_CASE_ENUM(OBOE_ERROR_UNAVAILABLE);
+        OBOE_CASE_ENUM(OBOE_ERROR_NO_FREE_HANDLES);
+        OBOE_CASE_ENUM(OBOE_ERROR_NO_MEMORY);
+        OBOE_CASE_ENUM(OBOE_ERROR_NULL);
+        OBOE_CASE_ENUM(OBOE_ERROR_TIMEOUT);
+        OBOE_CASE_ENUM(OBOE_ERROR_WOULD_BLOCK);
+        OBOE_CASE_ENUM(OBOE_ERROR_INVALID_ORDER);
+        OBOE_CASE_ENUM(OBOE_ERROR_OUT_OF_RANGE);
+    }
+    return "Unrecognized Oboe error.";
+}
+
+OBOE_API const char * Oboe_convertStreamStateToText(oboe_stream_state_t state) {
+    switch (state) {
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_UNINITIALIZED);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_OPEN);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_STARTING);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_STARTED);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_PAUSING);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_PAUSED);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_FLUSHING);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_FLUSHED);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_STOPPING);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_STOPPED);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_CLOSING);
+        OBOE_CASE_ENUM(OBOE_STREAM_STATE_CLOSED);
+    }
+    return "Unrecognized Oboe state.";
+}
+
+#undef OBOE_CASE_ENUM
+
+static AudioStream *convertOboeStreamToAudioStream(OboeStream stream)
+{
+    return (AudioStream *) sHandleTracker.get(OBOE_HANDLE_TYPE_STREAM,
+                                              (oboe_handle_t) stream);
+}
+
+static AudioStreamBuilder *convertOboeBuilderToStreamBuilder(OboeStreamBuilder builder)
+{
+    return (AudioStreamBuilder *) sHandleTracker.get(OBOE_HANDLE_TYPE_STREAM_BUILDER,
+                                                     (oboe_handle_t) builder);
+}
+
+OBOE_API oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder)
+{
+    ALOGD("Oboe_createStreamBuilder(): check sHandleTracker.isInitialized ()");
+    if (!sHandleTracker.isInitialized()) {
+        return OBOE_ERROR_NO_MEMORY;
+    }
+    AudioStreamBuilder *audioStreamBuilder =  new AudioStreamBuilder();
+    if (audioStreamBuilder == nullptr) {
+        return OBOE_ERROR_NO_MEMORY;
+    }
+    ALOGD("Oboe_createStreamBuilder(): created AudioStreamBuilder = %p", audioStreamBuilder);
+    // TODO protect the put() with a Mutex
+    OboeStreamBuilder handle = sHandleTracker.put(OBOE_HANDLE_TYPE_STREAM_BUILDER,
+            audioStreamBuilder);
+    if (handle < 0) {
+        delete audioStreamBuilder;
+        return static_cast<oboe_result_t>(handle);
+    } else {
+        *builder = handle;
+    }
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder,
+                                                     OboeDeviceId deviceId)
+{
+    AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+    streamBuilder->setDeviceId(deviceId);
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+                                              oboe_sample_rate_t sampleRate)
+{
+    AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+    streamBuilder->setSampleRate(sampleRate);
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+                                              oboe_sample_rate_t *sampleRate)
+{
+    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(sampleRate);
+    *sampleRate = streamBuilder->getSampleRate();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+                                                   int32_t samplesPerFrame)
+{
+    AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+    streamBuilder->setSamplesPerFrame(samplesPerFrame);
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+                                                   int32_t *samplesPerFrame)
+{
+    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(samplesPerFrame);
+    *samplesPerFrame = streamBuilder->getSamplesPerFrame();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+                                             oboe_direction_t direction)
+{
+    AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+    streamBuilder->setDirection(direction);
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+                                             oboe_direction_t *direction)
+{
+    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(direction);
+    *direction = streamBuilder->getDirection();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder,
+                                                   oboe_audio_format_t format)
+{
+    AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+    streamBuilder->setFormat(format);
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder,
+                                                   oboe_audio_format_t *format)
+{
+    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(format);
+    *format = streamBuilder->getFormat();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+                                                        oboe_sharing_mode_t sharingMode)
+{
+    AudioStreamBuilder *streamBuilder = CONVERT_BUILDER_HANDLE_OR_RETURN();
+    if ((sharingMode < 0) || (sharingMode >= OBOE_SHARING_MODE_COUNT)) {
+        return OBOE_ERROR_ILLEGAL_ARGUMENT;
+    } else {
+        streamBuilder->setSharingMode(sharingMode);
+        return OBOE_OK;
+    }
+}
+
+OBOE_API oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+                                                        oboe_sharing_mode_t *sharingMode)
+{
+    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(sharingMode);
+    *sharingMode = streamBuilder->getSharingMode();
+    return OBOE_OK;
+}
+
+static oboe_result_t  OboeInternal_openStream(AudioStreamBuilder *streamBuilder,
+                                              OboeStream *streamPtr)
+{
+    AudioStream *audioStream = nullptr;
+    oboe_result_t result = streamBuilder->build(&audioStream);
+    if (result != OBOE_OK) {
+        return result;
+    } else {
+        // Create a handle for referencing the object.
+        // TODO protect the put() with a Mutex
+        OboeStream handle = sHandleTracker.put(OBOE_HANDLE_TYPE_STREAM, audioStream);
+        if (handle < 0) {
+            delete audioStream;
+            return static_cast<oboe_result_t>(handle);
+        }
+        *streamPtr = handle;
+        return OBOE_OK;
+    }
+}
+
+OBOE_API oboe_result_t  OboeStreamBuilder_openStream(OboeStreamBuilder builder,
+                                                     OboeStream *streamPtr)
+{
+    ALOGD("OboeStreamBuilder_openStream(): builder = 0x%08X", builder);
+    AudioStreamBuilder *streamBuilder = COMMON_GET_FROM_BUILDER_OR_RETURN(streamPtr);
+    return OboeInternal_openStream(streamBuilder, streamPtr);
+}
+
+OBOE_API oboe_result_t  OboeStreamBuilder_delete(OboeStreamBuilder builder)
+{
+    // TODO protect the remove() with a Mutex
+    AudioStreamBuilder *streamBuilder = (AudioStreamBuilder *)
+            sHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM_BUILDER, builder);
+    if (streamBuilder != nullptr) {
+        delete streamBuilder;
+        return OBOE_OK;
+    }
+    return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OBOE_API oboe_result_t  OboeStream_close(OboeStream stream)
+{
+    // TODO protect the remove() with a Mutex
+    AudioStream *audioStream = (AudioStream *)
+            sHandleTracker.remove(OBOE_HANDLE_TYPE_STREAM, (oboe_handle_t)stream);
+    if (audioStream != nullptr) {
+        audioStream->close();
+        delete audioStream;
+        return OBOE_OK;
+    }
+    return OBOE_ERROR_INVALID_HANDLE;
+}
+
+OBOE_API oboe_result_t  OboeStream_requestStart(OboeStream stream)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    ALOGD("OboeStream_requestStart(0x%08X), audioStream = %p", stream, audioStream);
+    return audioStream->requestStart();
+}
+
+OBOE_API oboe_result_t  OboeStream_requestPause(OboeStream stream)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    ALOGD("OboeStream_requestPause(0x%08X), audioStream = %p", stream, audioStream);
+    return audioStream->requestPause();
+}
+
+OBOE_API oboe_result_t  OboeStream_requestFlush(OboeStream stream)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    ALOGD("OboeStream_requestFlush(0x%08X), audioStream = %p", stream, audioStream);
+    return audioStream->requestFlush();
+}
+
+OBOE_API oboe_result_t  OboeStream_requestStop(OboeStream stream)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    ALOGD("OboeStream_requestStop(0x%08X), audioStream = %p", stream, audioStream);
+    return audioStream->requestStop();
+}
+
+OBOE_API oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+                                            oboe_stream_state_t inputState,
+                                            oboe_stream_state_t *nextState,
+                                            oboe_nanoseconds_t timeoutNanoseconds)
+{
+
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    return audioStream->waitForStateChange(inputState, nextState, timeoutNanoseconds);
+}
+
+// ============================================================
+// Stream - non-blocking I/O
+// ============================================================
+
+OBOE_API oboe_result_t OboeStream_read(OboeStream stream,
+                               void *buffer,
+                               oboe_size_frames_t numFrames,
+                               oboe_nanoseconds_t timeoutNanoseconds)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    if (buffer == nullptr) {
+        return OBOE_ERROR_NULL;
+    }
+    if (numFrames < 0) {
+        return OBOE_ERROR_ILLEGAL_ARGUMENT;
+    } else if (numFrames == 0) {
+        return 0;
+    }
+
+    oboe_result_t result = audioStream->read(buffer, numFrames, timeoutNanoseconds);
+    // ALOGD("OboeStream_read(): read returns %d", result);
+
+    return result;
+}
+
+OBOE_API oboe_result_t OboeStream_write(OboeStream stream,
+                               const void *buffer,
+                               oboe_size_frames_t numFrames,
+                               oboe_nanoseconds_t timeoutNanoseconds)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    if (buffer == nullptr) {
+        return OBOE_ERROR_NULL;
+    }
+    if (numFrames < 0) {
+        return OBOE_ERROR_ILLEGAL_ARGUMENT;
+    } else if (numFrames == 0) {
+        return 0;
+    }
+
+    oboe_result_t result = audioStream->write(buffer, numFrames, timeoutNanoseconds);
+    // ALOGD("OboeStream_write(): write returns %d", result);
+
+    return result;
+}
+
+// ============================================================
+// Miscellaneous
+// ============================================================
+
+OBOE_API oboe_result_t OboeStream_createThread(OboeStream stream,
+                                     oboe_nanoseconds_t periodNanoseconds,
+                                     void *(*startRoutine)(void *), void *arg)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    return audioStream->createThread(periodNanoseconds, startRoutine, arg);
+}
+
+OBOE_API oboe_result_t Oboe_joinThread(OboeStream stream,
+                                   void **returnArg,
+                                   oboe_nanoseconds_t timeoutNanoseconds)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    return audioStream->joinThread(returnArg, timeoutNanoseconds);
+}
+
+// ============================================================
+// Stream - queries
+// ============================================================
+
+// TODO Use oboe_clockid_t all the way down through the C++ streams.
+static clockid_t OboeConvert_fromOboeClockId(oboe_clockid_t clockid)
+{
+    clockid_t hostClockId;
+    switch (clockid) {
+        case OBOE_CLOCK_MONOTONIC:
+            hostClockId = CLOCK_MONOTONIC;
+            break;
+        case OBOE_CLOCK_BOOTTIME:
+            hostClockId = CLOCK_BOOTTIME;
+            break;
+        default:
+            hostClockId = 0; // TODO review
+    }
+    return hostClockId;
+}
+
+oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid)
+{
+    clockid_t hostClockId = OboeConvert_fromOboeClockId(clockid);
+   return AudioClock::getNanoseconds(hostClockId);
+}
+
+OBOE_API oboe_result_t OboeStream_getSampleRate(OboeStream stream, oboe_sample_rate_t *sampleRate)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(sampleRate);
+    *sampleRate = audioStream->getSampleRate();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(samplesPerFrame);
+    *samplesPerFrame = audioStream->getSamplesPerFrame();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(state);
+    *state = audioStream->getState();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(format);
+    *format = audioStream->getFormat();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_setBufferSize(OboeStream stream,
+                                                oboe_size_frames_t requestedFrames,
+                                                oboe_size_frames_t *actualFrames)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    return audioStream->setBufferSize(requestedFrames, actualFrames);
+}
+
+OBOE_API oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+    *frames = audioStream->getBufferSize();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getDirection(OboeStream stream, int32_t *direction)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(direction);
+    *direction = audioStream->getDirection();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream,
+                                                    oboe_size_frames_t *framesPerBurst)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(framesPerBurst);
+    *framesPerBurst = audioStream->getFramesPerBurst();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getBufferCapacity(OboeStream stream,
+                                           oboe_size_frames_t *capacity)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(capacity);
+    *capacity = audioStream->getBufferCapacity();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(xRunCount);
+    *xRunCount = audioStream->getXRunCount();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+                                                 oboe_sharing_mode_t *sharingMode)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(sharingMode);
+    *sharingMode = audioStream->getSharingMode();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesWritten(OboeStream stream,
+                                                   oboe_position_frames_t *frames)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+    *frames = audioStream->getFramesWritten();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames)
+{
+    AudioStream *audioStream = COMMON_GET_FROM_STREAM_OR_RETURN(frames);
+    *frames = audioStream->getFramesRead();
+    return OBOE_OK;
+}
+
+OBOE_API oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+                                      oboe_clockid_t clockid,
+                                      oboe_position_frames_t *framePosition,
+                                      oboe_nanoseconds_t *timeNanoseconds)
+{
+    AudioStream *audioStream = CONVERT_STREAM_HANDLE_OR_RETURN();
+    if (framePosition == nullptr) {
+        return OBOE_ERROR_NULL;
+    } else if (timeNanoseconds == nullptr) {
+        return OBOE_ERROR_NULL;
+    } else if (clockid != OBOE_CLOCK_MONOTONIC && clockid != OBOE_CLOCK_BOOTTIME) {
+        return OBOE_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    clockid_t hostClockId = OboeConvert_fromOboeClockId(clockid);
+    return audioStream->getTimestamp(hostClockId, framePosition, timeNanoseconds);
+}
diff --git a/media/liboboe/src/core/README.md b/media/liboboe/src/core/README.md
new file mode 100644
index 0000000..dd99286
--- /dev/null
+++ b/media/liboboe/src/core/README.md
@@ -0,0 +1,2 @@
+The core folder contains the essential Oboe files common to all implementations.
+The OboeAudio.cpp contains the 'C' API.
diff --git a/media/liboboe/src/core/VersionExperiment.txt b/media/liboboe/src/core/VersionExperiment.txt
new file mode 100644
index 0000000..071239b
--- /dev/null
+++ b/media/liboboe/src/core/VersionExperiment.txt
@@ -0,0 +1,55 @@
+
+// TODO Experiment with versioning. This may be removed or changed dramatically.
+// Please ignore for now. Do not review.
+#define OBOE_VERSION_EXPERIMENT  0
+#if OBOE_VERSION_EXPERIMENT
+
+#define OBOE_EARLIEST_SUPPORTED_VERSION  1
+#define OBOE_CURRENT_VERSION  2
+
+typedef struct OboeInterface_s {
+    int32_t size; // do not use size_t because its size can vary
+    int32_t version;
+    int32_t reserved1;
+    void *  reserved2;
+    oboe_result_t (*createStreamBuilder)(OboeStreamBuilder *);
+} OboeInterface_t;
+
+OboeInterface_t s_oboe_template = {
+        .size = sizeof(OboeInterface_t),
+        .version = OBOE_CURRENT_VERSION,
+        .reserved1 = 0,
+        .reserved2 = NULL,
+        .createStreamBuilder = Oboe_createStreamBuilder
+};
+
+oboe_result_t Oboe_Unimplemented(OboeInterface_t *oboe) {
+    (void) oboe;
+    return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+typedef oboe_result_t (*OboeFunction_t)(OboeInterface_t *oboe);
+
+int32_t Oboe_Initialize(OboeInterface_t *oboe, uint32_t flags) {
+    if (oboe->version < OBOE_EARLIEST_SUPPORTED_VERSION) {
+        return OBOE_ERROR_INCOMPATIBLE;
+    }
+    // Fill in callers vector table.
+    uint8_t *start = (uint8_t*)&oboe->reserved1;
+    uint8_t *end;
+    if (oboe->size <= s_oboe_template.size) {
+        end = ((uint8_t *)oboe) + oboe->size;
+    } else {
+        end = ((uint8_t *)oboe) + s_oboe_template.size;
+        // Assume the rest of the structure is vectors.
+        // Point them all to OboeInternal_Unimplemented()
+        // Point to first vector past end of the known structure.
+        OboeFunction_t *next = (OboeFunction_t*)end;
+        while ((((uint8_t *)next) - ((uint8_t *)oboe)) < oboe->size) {
+            *next++ = Oboe_Unimplemented;
+        }
+    }
+    memcpy(&oboe->reserved1, &s_oboe_template.reserved1, end - start);
+    return OBOE_OK;
+}
+#endif /* OBOE_VERSION_EXPERIMENT -------------------------- */
diff --git a/media/liboboe/src/legacy/AudioStreamRecord.cpp b/media/liboboe/src/legacy/AudioStreamRecord.cpp
new file mode 100644
index 0000000..f130cad
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamRecord.cpp
@@ -0,0 +1,230 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioStreamRecord"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <utils/String16.h>
+#include <media/AudioRecord.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioClock.h"
+#include "AudioStreamRecord.h"
+
+using namespace android;
+using namespace oboe;
+
+AudioStreamRecord::AudioStreamRecord()
+    : AudioStream()
+{
+}
+
+AudioStreamRecord::~AudioStreamRecord()
+{
+    const oboe_stream_state_t state = getState();
+    bool bad = !(state == OBOE_STREAM_STATE_UNINITIALIZED || state == OBOE_STREAM_STATE_CLOSED);
+    ALOGE_IF(bad, "stream not closed, in state %d", state);
+}
+
+oboe_result_t AudioStreamRecord::open(const AudioStreamBuilder& builder)
+{
+    oboe_result_t result = OBOE_OK;
+
+    result = AudioStream::open(builder);
+    if (result != OBOE_OK) {
+        return result;
+    }
+
+    // Try to create an AudioRecord
+
+    // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+    int32_t samplesPerFrame = (getSamplesPerFrame() == OBOE_UNSPECIFIED)
+                              ? 2 : getSamplesPerFrame();
+    audio_channel_mask_t channelMask = audio_channel_in_mask_from_count(samplesPerFrame);
+
+    AudioRecord::callback_t callback = NULL;
+    audio_input_flags_t flags = (audio_input_flags_t) AUDIO_INPUT_FLAG_NONE;
+
+    // TODO implement an unspecified Android format then use that.
+    audio_format_t format = (getFormat() == OBOE_UNSPECIFIED)
+            ? AUDIO_FORMAT_PCM_FLOAT
+            : OboeConvert_oboeToAndroidDataFormat(getFormat());
+
+    mAudioRecord = new AudioRecord(
+            AUDIO_SOURCE_DEFAULT,
+            getSampleRate(),
+            format,
+            channelMask,
+
+            mOpPackageName, // const String16& opPackageName TODO does not compile
+
+            0,    //    size_t frameCount = 0,
+            callback,
+            NULL, //    void* user = NULL,
+            0,    //    uint32_t notificationFrames = 0,
+            AUDIO_SESSION_ALLOCATE,
+            AudioRecord::TRANSFER_DEFAULT,
+            flags
+             //   int uid = -1,
+             //   pid_t pid = -1,
+             //   const audio_attributes_t* pAttributes = NULL
+             );
+
+    // Did we get a valid track?
+    status_t status = mAudioRecord->initCheck();
+    if (status != OK) {
+        close();
+        ALOGE("AudioStreamRecord::open(), initCheck() returned %d", status);
+        return OboeConvert_androidToOboeError(status);
+    }
+
+    // Get the actual rate.
+    setSampleRate(mAudioRecord->getSampleRate());
+    setSamplesPerFrame(mAudioRecord->channelCount());
+    setFormat(OboeConvert_androidToOboeDataFormat(mAudioRecord->format()));
+
+    setState(OBOE_STREAM_STATE_OPEN);
+
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::close()
+{
+    // TODO add close() or release() to AudioRecord API then call it from here
+    if (getState() != OBOE_STREAM_STATE_CLOSED) {
+        mAudioRecord.clear();
+        setState(OBOE_STREAM_STATE_CLOSED);
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::requestStart()
+{
+    if (mAudioRecord.get() == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    // Get current position so we can detect when the track is playing.
+    status_t err = mAudioRecord->getPosition(&mPositionWhenStarting);
+    if (err != OK) {
+        return OboeConvert_androidToOboeError(err);
+    }
+    err = mAudioRecord->start();
+    if (err != OK) {
+        return OboeConvert_androidToOboeError(err);
+    } else {
+        setState(OBOE_STREAM_STATE_STARTING);
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::requestPause()
+{
+    return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+oboe_result_t AudioStreamRecord::requestFlush() {
+    return OBOE_ERROR_UNIMPLEMENTED;
+}
+
+oboe_result_t AudioStreamRecord::requestStop() {
+    if (mAudioRecord.get() == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    setState(OBOE_STREAM_STATE_STOPPING);
+    mAudioRecord->stop();
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamRecord::updateState()
+{
+    oboe_result_t result = OBOE_OK;
+    oboe_wrapping_frames_t position;
+    status_t err;
+    switch (getState()) {
+    // TODO add better state visibility to AudioRecord
+    case OBOE_STREAM_STATE_STARTING:
+        err = mAudioRecord->getPosition(&position);
+        if (err != OK) {
+            result = OboeConvert_androidToOboeError(err);
+        } else if (position != mPositionWhenStarting) {
+            setState(OBOE_STREAM_STATE_STARTED);
+        }
+        break;
+    case OBOE_STREAM_STATE_STOPPING:
+        if (mAudioRecord->stopped()) {
+            setState(OBOE_STREAM_STATE_STOPPED);
+        }
+        break;
+    default:
+        break;
+    }
+    return result;
+}
+
+oboe_result_t AudioStreamRecord::read(void *buffer,
+                                      oboe_size_frames_t numFrames,
+                                      oboe_nanoseconds_t timeoutNanoseconds)
+{
+    oboe_size_frames_t bytesPerFrame = getBytesPerFrame();
+    oboe_size_bytes_t numBytes;
+    oboe_result_t result = OboeConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+    if (result != OBOE_OK) {
+        return result;
+    }
+
+    // TODO add timeout to AudioRecord
+    bool blocking = (timeoutNanoseconds > 0);
+    ssize_t bytesRead = mAudioRecord->read(buffer, numBytes, blocking);
+    if (bytesRead == WOULD_BLOCK) {
+        return 0;
+    } else if (bytesRead < 0) {
+        return OboeConvert_androidToOboeError(bytesRead);
+    }
+    oboe_size_frames_t framesRead = (oboe_size_frames_t)(bytesRead / bytesPerFrame);
+    return (oboe_result_t) framesRead;
+}
+
+oboe_result_t AudioStreamRecord::setBufferSize(oboe_size_frames_t requestedFrames,
+                                             oboe_size_frames_t *actualFrames)
+{
+    *actualFrames = getBufferCapacity();
+    return OBOE_OK;
+}
+
+oboe_size_frames_t AudioStreamRecord::getBufferSize() const
+{
+    return getBufferCapacity(); // TODO implement in AudioRecord?
+}
+
+oboe_size_frames_t AudioStreamRecord::getBufferCapacity() const
+{
+    return static_cast<oboe_size_frames_t>(mAudioRecord->frameCount());
+}
+
+int32_t AudioStreamRecord::getXRunCount() const
+{
+    return OBOE_ERROR_UNIMPLEMENTED; // TODO implement when AudioRecord supports it
+}
+
+oboe_size_frames_t AudioStreamRecord::getFramesPerBurst() const
+{
+    return 192; // TODO add query to AudioRecord.cpp
+}
+
+// TODO implement getTimestamp
+
diff --git a/media/liboboe/src/legacy/AudioStreamRecord.h b/media/liboboe/src/legacy/AudioStreamRecord.h
new file mode 100644
index 0000000..02ff220
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamRecord.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef LEGACY_AUDIOSTREAMRECORD_H
+#define LEGACY_AUDIOSTREAMRECORD_H
+
+#include <media/AudioRecord.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "OboeLegacy.h"
+
+namespace oboe {
+
+/**
+ * Internal stream that uses the legacy AudioTrack path.
+ */
+class AudioStreamRecord : public AudioStream {
+public:
+    AudioStreamRecord();
+
+    virtual ~AudioStreamRecord();
+
+    virtual oboe_result_t open(const AudioStreamBuilder & builder) override;
+    virtual oboe_result_t close() override;
+
+    virtual oboe_result_t requestStart() override;
+    virtual oboe_result_t requestPause() override;
+    virtual oboe_result_t requestFlush() override;
+    virtual oboe_result_t requestStop() override;
+
+    virtual oboe_result_t getTimestamp(clockid_t clockId,
+                                       oboe_position_frames_t *framePosition,
+                                       oboe_nanoseconds_t *timeNanoseconds) override {
+        return OBOE_ERROR_UNIMPLEMENTED; // TODO
+    }
+
+    virtual oboe_result_t read(void *buffer,
+                             oboe_size_frames_t numFrames,
+                             oboe_nanoseconds_t timeoutNanoseconds) override;
+
+    virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+                                             oboe_size_frames_t *actualFrames) override;
+
+    virtual oboe_size_frames_t getBufferSize() const override;
+
+    virtual oboe_size_frames_t getBufferCapacity() const override;
+
+    virtual int32_t getXRunCount() const override;
+
+    virtual oboe_size_frames_t getFramesPerBurst() const override;
+
+    virtual oboe_result_t updateState() override;
+
+private:
+    android::sp<android::AudioRecord> mAudioRecord;
+    // TODO add 64-bit position reporting to AudioRecord and use it.
+    oboe_wrapping_frames_t   mPositionWhenStarting = 0;
+    android::String16        mOpPackageName;
+};
+
+} /* namespace oboe */
+
+#endif /* LEGACY_AUDIOSTREAMRECORD_H */
diff --git a/media/liboboe/src/legacy/AudioStreamTrack.cpp b/media/liboboe/src/legacy/AudioStreamTrack.cpp
new file mode 100644
index 0000000..5205fc5
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamTrack.cpp
@@ -0,0 +1,296 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioStreamTrack"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <media/AudioTrack.h>
+
+#include <oboe/OboeAudio.h>
+#include "AudioClock.h"
+#include "AudioStreamTrack.h"
+
+
+using namespace android;
+using namespace oboe;
+
+/*
+ * Create a stream that uses the AudioTrack.
+ */
+AudioStreamTrack::AudioStreamTrack()
+    : AudioStream()
+{
+}
+
+AudioStreamTrack::~AudioStreamTrack()
+{
+    const oboe_stream_state_t state = getState();
+    bool bad = !(state == OBOE_STREAM_STATE_UNINITIALIZED || state == OBOE_STREAM_STATE_CLOSED);
+    ALOGE_IF(bad, "stream not closed, in state %d", state);
+}
+
+oboe_result_t AudioStreamTrack::open(const AudioStreamBuilder& builder)
+{
+    oboe_result_t result = OBOE_OK;
+
+    result = AudioStream::open(builder);
+    if (result != OK) {
+        return result;
+    }
+
+    // Try to create an AudioTrack
+    // TODO Support UNSPECIFIED in AudioTrack. For now, use stereo if unspecified.
+    int32_t samplesPerFrame = (getSamplesPerFrame() == OBOE_UNSPECIFIED)
+                              ? 2 : getSamplesPerFrame();
+    audio_channel_mask_t channelMask = audio_channel_out_mask_from_count(samplesPerFrame);
+    ALOGE("AudioStreamTrack::open(), samplesPerFrame = %d, channelMask = 0x%08x",
+            samplesPerFrame, channelMask);
+
+    AudioTrack::callback_t callback = NULL;
+    // TODO add more performance options
+    audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
+    size_t frameCount = 0;
+    // TODO implement an unspecified AudioTrack format then use that.
+    audio_format_t format = (getFormat() == OBOE_UNSPECIFIED)
+            ? AUDIO_FORMAT_PCM_FLOAT
+            : OboeConvert_oboeToAndroidDataFormat(getFormat());
+
+    mAudioTrack = new AudioTrack(
+            (audio_stream_type_t) AUDIO_STREAM_MUSIC,
+            getSampleRate(),
+            format,
+            channelMask,
+            frameCount,
+            flags,
+            callback,
+            NULL,    // user callback data
+            0,       // notificationFrames
+            AUDIO_SESSION_ALLOCATE,
+            AudioTrack::transfer_type::TRANSFER_SYNC // TODO - this does not allow FAST
+            );
+
+    // Did we get a valid track?
+    status_t status = mAudioTrack->initCheck();
+    // FIXME - this should work - if (status != NO_ERROR) {
+    //         But initCheck() is returning 1 !
+    if (status < 0) {
+        close();
+        ALOGE("AudioStreamTrack::open(), initCheck() returned %d", status);
+        return OboeConvert_androidToOboeError(status);
+    }
+
+    // Get the actual values from the AudioTrack.
+    setSamplesPerFrame(mAudioTrack->channelCount());
+    setSampleRate(mAudioTrack->getSampleRate());
+    setFormat(OboeConvert_androidToOboeDataFormat(mAudioTrack->format()));
+
+    setState(OBOE_STREAM_STATE_OPEN);
+
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::close()
+{
+    // TODO maybe add close() or release() to AudioTrack API then call it from here
+    if (getState() != OBOE_STREAM_STATE_CLOSED) {
+        mAudioTrack.clear(); // TODO is this right?
+        setState(OBOE_STREAM_STATE_CLOSED);
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestStart()
+{
+    if (mAudioTrack.get() == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    // Get current position so we can detect when the track is playing.
+    status_t err = mAudioTrack->getPosition(&mPositionWhenStarting);
+    if (err != OK) {
+        return OboeConvert_androidToOboeError(err);
+    }
+    err = mAudioTrack->start();
+    if (err != OK) {
+        return OboeConvert_androidToOboeError(err);
+    } else {
+        setState(OBOE_STREAM_STATE_STARTING);
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestPause()
+{
+    if (mAudioTrack.get() == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    } else if (getState() != OBOE_STREAM_STATE_STARTING
+            && getState() != OBOE_STREAM_STATE_STARTED) {
+        ALOGE("requestPause(), called when state is %s", Oboe_convertStreamStateToText(getState()));
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    setState(OBOE_STREAM_STATE_PAUSING);
+    mAudioTrack->pause();
+    status_t err = mAudioTrack->getPosition(&mPositionWhenPausing);
+    if (err != OK) {
+        return OboeConvert_androidToOboeError(err);
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestFlush() {
+    if (mAudioTrack.get() == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    } else if (getState() != OBOE_STREAM_STATE_PAUSED) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    setState(OBOE_STREAM_STATE_FLUSHING);
+    incrementFramesRead(getFramesWritten() - getFramesRead());
+    mAudioTrack->flush();
+    mFramesWritten.reset32();
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::requestStop() {
+    if (mAudioTrack.get() == NULL) {
+        return OBOE_ERROR_INVALID_STATE;
+    }
+    setState(OBOE_STREAM_STATE_STOPPING);
+    incrementFramesRead(getFramesWritten() - getFramesRead()); // TODO review
+    mAudioTrack->stop();
+    mFramesWritten.reset32();
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::updateState()
+{
+    status_t err;
+    oboe_wrapping_frames_t position;
+    switch (getState()) {
+    // TODO add better state visibility to AudioTrack
+    case OBOE_STREAM_STATE_STARTING:
+        if (mAudioTrack->hasStarted()) {
+            setState(OBOE_STREAM_STATE_STARTED);
+        }
+        break;
+    case OBOE_STREAM_STATE_PAUSING:
+        if (mAudioTrack->stopped()) {
+            err = mAudioTrack->getPosition(&position);
+            if (err != OK) {
+                return OboeConvert_androidToOboeError(err);
+            } else if (position == mPositionWhenPausing) {
+                // Has stream really stopped advancing?
+                setState(OBOE_STREAM_STATE_PAUSED);
+            }
+            mPositionWhenPausing = position;
+        }
+        break;
+    case OBOE_STREAM_STATE_FLUSHING:
+        {
+            err = mAudioTrack->getPosition(&position);
+            if (err != OK) {
+                return OboeConvert_androidToOboeError(err);
+            } else if (position == 0) {
+                // Advance frames read to match written.
+                setState(OBOE_STREAM_STATE_FLUSHED);
+            }
+        }
+        break;
+    case OBOE_STREAM_STATE_STOPPING:
+        if (mAudioTrack->stopped()) {
+            setState(OBOE_STREAM_STATE_STOPPED);
+        }
+        break;
+    default:
+        break;
+    }
+    return OBOE_OK;
+}
+
+oboe_result_t AudioStreamTrack::write(const void *buffer,
+                                      oboe_size_frames_t numFrames,
+                                      oboe_nanoseconds_t timeoutNanoseconds)
+{
+    oboe_size_frames_t bytesPerFrame = getBytesPerFrame();
+    oboe_size_bytes_t numBytes;
+    oboe_result_t result = OboeConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+    if (result != OBOE_OK) {
+        return result;
+    }
+
+    // TODO add timeout to AudioTrack
+    bool blocking = timeoutNanoseconds > 0;
+    ssize_t bytesWritten = mAudioTrack->write(buffer, numBytes, blocking);
+    if (bytesWritten == WOULD_BLOCK) {
+        return 0;
+    } else if (bytesWritten < 0) {
+        ALOGE("invalid write, returned %d", (int)bytesWritten);
+        return OboeConvert_androidToOboeError(bytesWritten);
+    }
+    oboe_size_frames_t framesWritten = (oboe_size_frames_t)(bytesWritten / bytesPerFrame);
+    incrementFramesWritten(framesWritten);
+    return framesWritten;
+}
+
+oboe_result_t AudioStreamTrack::setBufferSize(oboe_size_frames_t requestedFrames,
+                                             oboe_size_frames_t *actualFrames)
+{
+    ssize_t result = mAudioTrack->setBufferSizeInFrames(requestedFrames);
+    if (result != OK) {
+        return OboeConvert_androidToOboeError(result);
+    } else {
+        *actualFrames = result;
+        return OBOE_OK;
+    }
+}
+
+oboe_size_frames_t AudioStreamTrack::getBufferSize() const
+{
+    return static_cast<oboe_size_frames_t>(mAudioTrack->getBufferSizeInFrames());
+}
+
+oboe_size_frames_t AudioStreamTrack::getBufferCapacity() const
+{
+    return static_cast<oboe_size_frames_t>(mAudioTrack->frameCount());
+}
+
+int32_t AudioStreamTrack::getXRunCount() const
+{
+    return static_cast<int32_t>(mAudioTrack->getUnderrunCount());
+}
+
+int32_t AudioStreamTrack::getFramesPerBurst() const
+{
+    return 192; // TODO add query to AudioTrack.cpp
+}
+
+oboe_position_frames_t AudioStreamTrack::getFramesRead() {
+    oboe_wrapping_frames_t position;
+    status_t result;
+    switch (getState()) {
+    case OBOE_STREAM_STATE_STARTING:
+    case OBOE_STREAM_STATE_STARTED:
+    case OBOE_STREAM_STATE_STOPPING:
+        result = mAudioTrack->getPosition(&position);
+        if (result == OK) {
+            mFramesRead.update32(position);
+        }
+        break;
+    default:
+        break;
+    }
+    return AudioStream::getFramesRead();
+}
diff --git a/media/liboboe/src/legacy/AudioStreamTrack.h b/media/liboboe/src/legacy/AudioStreamTrack.h
new file mode 100644
index 0000000..8c40884
--- /dev/null
+++ b/media/liboboe/src/legacy/AudioStreamTrack.h
@@ -0,0 +1,78 @@
+/*
+ * 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.
+ */
+
+#ifndef LEGACY_AUDIOSTREAMTRACK_H
+#define LEGACY_AUDIOSTREAMTRACK_H
+
+#include <media/AudioTrack.h>
+#include <oboe/OboeAudio.h>
+
+#include "AudioStreamBuilder.h"
+#include "AudioStream.h"
+#include "OboeLegacy.h"
+
+namespace oboe {
+
+
+/**
+ * Internal stream that uses the legacy AudioTrack path.
+ */
+class AudioStreamTrack : public AudioStream {
+public:
+    AudioStreamTrack();
+
+    virtual ~AudioStreamTrack();
+
+
+    virtual oboe_result_t open(const AudioStreamBuilder & builder) override;
+    virtual oboe_result_t close() override;
+
+    virtual oboe_result_t requestStart() override;
+    virtual oboe_result_t requestPause() override;
+    virtual oboe_result_t requestFlush() override;
+    virtual oboe_result_t requestStop() override;
+
+    virtual oboe_result_t getTimestamp(clockid_t clockId,
+                                       oboe_position_frames_t *framePosition,
+                                       oboe_nanoseconds_t *timeNanoseconds) override {
+        return OBOE_ERROR_UNIMPLEMENTED; // TODO call getTimestamp(ExtendedTimestamp *timestamp);
+    }
+
+    virtual oboe_result_t write(const void *buffer,
+                             oboe_size_frames_t numFrames,
+                             oboe_nanoseconds_t timeoutNanoseconds) override;
+
+    virtual oboe_result_t setBufferSize(oboe_size_frames_t requestedFrames,
+                                             oboe_size_frames_t *actualFrames) override;
+    virtual oboe_size_frames_t getBufferSize() const override;
+    virtual oboe_size_frames_t getBufferCapacity() const override;
+    virtual oboe_size_frames_t getFramesPerBurst()const  override;
+    virtual int32_t getXRunCount() const override;
+
+    virtual oboe_position_frames_t getFramesRead() override;
+
+    virtual oboe_result_t updateState() override;
+
+private:
+    android::sp<android::AudioTrack> mAudioTrack;
+    // TODO add 64-bit position reporting to AudioRecord and use it.
+    oboe_wrapping_frames_t           mPositionWhenStarting = 0;
+    oboe_wrapping_frames_t           mPositionWhenPausing = 0;
+};
+
+} /* namespace oboe */
+
+#endif /* LEGACY_AUDIOSTREAMTRACK_H */
diff --git a/media/liboboe/src/legacy/OboeLegacy.h b/media/liboboe/src/legacy/OboeLegacy.h
new file mode 100644
index 0000000..6803837
--- /dev/null
+++ b/media/liboboe/src/legacy/OboeLegacy.h
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+
+#ifndef OBOE_LEGACY_H
+#define OBOE_LEGACY_H
+
+#include <stdint.h>
+#include <oboe/OboeAudio.h>
+
+/**
+ * Common code for legacy classes.
+ */
+
+/* AudioTrack uses a 32-bit frame counter that can wrap around in about a day. */
+typedef uint32_t oboe_wrapping_frames_t;
+
+#endif /* OBOE_LEGACY_H */
diff --git a/media/liboboe/src/legacy/README.md b/media/liboboe/src/legacy/README.md
new file mode 100644
index 0000000..b51c44b
--- /dev/null
+++ b/media/liboboe/src/legacy/README.md
@@ -0,0 +1,2 @@
+The legacy folder contains the classes that implement Oboe AudioStream on top of
+Android AudioTrack and AudioRecord.
diff --git a/media/liboboe/src/utility/AudioClock.h b/media/liboboe/src/utility/AudioClock.h
new file mode 100644
index 0000000..da2f74a
--- /dev/null
+++ b/media/liboboe/src/utility/AudioClock.h
@@ -0,0 +1,98 @@
+/*
+ * 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 UTILITY_AUDIOCLOCK_H
+#define UTILITY_AUDIOCLOCK_H
+
+#include <sys/types.h>
+#include <time.h>
+#include "oboe/OboeDefinitions.h"
+#include "oboe/OboeAudio.h"
+
+class AudioClock {
+public:
+    static oboe_nanoseconds_t getNanoseconds(clockid_t clockId = CLOCK_MONOTONIC) {
+        struct timespec time;
+        int result = clock_gettime(clockId, &time);
+        if (result < 0) {
+            return -errno;
+        }
+        return (time.tv_sec * OBOE_NANOS_PER_SECOND) + time.tv_nsec;
+    }
+
+    /**
+     * Sleep until the specified absolute time.
+     * Return immediately with OBOE_ERROR_ILLEGAL_ARGUMENT if a negative
+     * nanoTime is specified.
+     *
+     * @param nanoTime time to wake up
+     * @param clockId CLOCK_MONOTONIC is default
+     * @return 0, a negative error, or 1 if the call is interrupted by a signal handler (EINTR)
+     */
+    static int sleepUntilNanoTime(oboe_nanoseconds_t nanoTime,
+                                  clockid_t clockId = CLOCK_MONOTONIC) {
+        if (nanoTime > 0) {
+            struct timespec time;
+            time.tv_sec = nanoTime / OBOE_NANOS_PER_SECOND;
+            // Calculate the fractional nanoseconds. Avoids expensive % operation.
+            time.tv_nsec = nanoTime - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+            int err = clock_nanosleep(clockId, TIMER_ABSTIME, &time, nullptr);
+            switch (err) {
+            case EINTR:
+                return 1;
+            case 0:
+                return 0;
+            default:
+                // Subtract because clock_nanosleep() returns a positive error number!
+                return 0 - err;
+            }
+        } else {
+            return OBOE_ERROR_ILLEGAL_ARGUMENT;
+        }
+    }
+
+    /**
+     * Sleep for the specified number of relative nanoseconds in real-time.
+     * Return immediately with 0 if a negative nanoseconds is specified.
+     *
+     * @param nanoseconds time to sleep
+     * @param clockId CLOCK_MONOTONIC is default
+     * @return 0, a negative error, or 1 if the call is interrupted by a signal handler (EINTR)
+     */
+    static int sleepForNanos(oboe_nanoseconds_t nanoseconds, clockid_t clockId = CLOCK_MONOTONIC) {
+        if (nanoseconds > 0) {
+            struct timespec time;
+            time.tv_sec = nanoseconds / OBOE_NANOS_PER_SECOND;
+            // Calculate the fractional nanoseconds. Avoids expensive % operation.
+            time.tv_nsec = nanoseconds - (time.tv_sec * OBOE_NANOS_PER_SECOND);
+            const int flags = 0; // documented as relative sleep
+            int err = clock_nanosleep(clockId, flags, &time, nullptr);
+            switch (err) {
+            case EINTR:
+                return 1;
+            case 0:
+                return 0;
+            default:
+                // Subtract because clock_nanosleep() returns a positive error number!
+                return 0 - err;
+            }
+        }
+        return 0;
+    }
+};
+
+
+#endif // UTILITY_AUDIOCLOCK_H
diff --git a/media/liboboe/src/utility/HandleTracker.cpp b/media/liboboe/src/utility/HandleTracker.cpp
new file mode 100644
index 0000000..be2a64c
--- /dev/null
+++ b/media/liboboe/src/utility/HandleTracker.cpp
@@ -0,0 +1,213 @@
+/*
+ * 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 "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <assert.h>
+
+#include <oboe/OboeDefinitions.h>
+#include "HandleTracker.h"
+
+// Handle format is: tgggiiii
+// where each letter is 4 bits, t=type, g=generation, i=index
+
+#define TYPE_SIZE           4
+#define GENERATION_SIZE    12
+#define INDEX_SIZE         16
+
+#define GENERATION_INVALID  0
+#define GENERATION_SHIFT    INDEX_SIZE
+
+#define TYPE_MASK           ((1 << TYPE_SIZE) - 1)
+#define GENERATION_MASK     ((1 << GENERATION_SIZE) - 1)
+#define INDEX_MASK          ((1 << INDEX_SIZE) - 1)
+
+#define SLOT_UNAVAILABLE    (-1)
+
+// Error if handle is negative so type is limited to bottom half.
+#define HANDLE_INVALID_TYPE TYPE_MASK
+
+static_assert(HANDLE_TRACKER_MAX_TYPES == (1 << (TYPE_SIZE - 1)),
+    "Mismatch between header and cpp.");
+static_assert(HANDLE_TRACKER_MAX_HANDLES == (1 << (INDEX_SIZE)),
+    "Mismatch between header and cpp.");
+
+HandleTracker::HandleTracker(uint32_t maxHandles)
+        : mMaxHandleCount(maxHandles)
+        , mHandleAddresses(nullptr)
+        , mHandleHeaders(nullptr)
+{
+    assert(maxHandles <= HANDLE_TRACKER_MAX_HANDLES);
+    // Allocate arrays to hold addresses and validation info.
+    mHandleAddresses = (handle_tracker_address_t *) new handle_tracker_address_t[maxHandles];
+    if (mHandleAddresses != nullptr) {
+        mHandleHeaders = new handle_tracker_header_t[maxHandles];
+        if (mHandleHeaders != nullptr) {
+            // Initialize linked list of free nodes. NULL terminated.
+            for (uint32_t i = 0; i < (maxHandles - 1); i++) {
+                mHandleAddresses[i] = &mHandleAddresses[i + 1]; // point to next node
+                mHandleHeaders[i] = 0;
+            }
+            mNextFreeAddress = &mHandleAddresses[0];
+            mHandleAddresses[maxHandles - 1] = nullptr;
+            mHandleHeaders[maxHandles - 1] = 0;
+        } else {
+            delete[] mHandleAddresses; // so the class appears uninitialized
+        }
+    }
+}
+
+HandleTracker::~HandleTracker()
+{
+    delete[] mHandleAddresses;
+    delete[] mHandleHeaders;
+}
+
+bool HandleTracker::isInitialized() const {
+    return mHandleAddresses != nullptr;
+}
+
+handle_tracker_slot_t HandleTracker::allocateSlot() {
+    void **allocated = mNextFreeAddress;
+    if (allocated == nullptr) {
+        return SLOT_UNAVAILABLE;
+    }
+    // Remove this slot from the head of the linked list.
+    mNextFreeAddress = (void **) *allocated;
+    return (allocated - mHandleAddresses);
+}
+
+handle_tracker_generation_t HandleTracker::nextGeneration(handle_tracker_slot_t index) {
+    handle_tracker_generation_t generation = (mHandleHeaders[index] + 1) & GENERATION_MASK;
+    // Avoid generation zero so that 0x0 is not a valid handle.
+    if (generation == GENERATION_INVALID) {
+        generation++;
+    }
+    return generation;
+}
+
+oboe_handle_t HandleTracker::put(handle_tracker_type_t type, void *address)
+{
+    if (type < 0 || type >= HANDLE_TRACKER_MAX_TYPES) {
+        return static_cast<oboe_handle_t>(OBOE_ERROR_OUT_OF_RANGE);
+    }
+    if (!isInitialized()) {
+        return static_cast<oboe_handle_t>(OBOE_ERROR_NO_MEMORY);
+    }
+
+    // Find an empty slot.
+    handle_tracker_slot_t index = allocateSlot();
+    if (index == SLOT_UNAVAILABLE) {
+        ALOGE("HandleTracker::put() no room for more handles");
+        return static_cast<oboe_handle_t>(OBOE_ERROR_NO_FREE_HANDLES);
+    }
+
+    // Cycle the generation counter so stale handles can be detected.
+    handle_tracker_generation_t generation = nextGeneration(index); // reads header table
+    handle_tracker_header_t inputHeader = buildHeader(type, generation);
+
+    // These two writes may need to be observed by other threads or cores during get().
+    mHandleHeaders[index] = inputHeader;
+    mHandleAddresses[index] = address;
+    // TODO use store release to enforce memory order with get()
+
+    // Generate a handle.
+    oboe_handle_t handle = buildHandle(inputHeader, index);
+
+    //ALOGD("HandleTracker::put(%p) returns 0x%08x", address, handle);
+    return handle;
+}
+
+handle_tracker_slot_t HandleTracker::handleToIndex(handle_tracker_type_t type,
+                                                   oboe_handle_t handle) const
+{
+    // Validate the handle.
+    handle_tracker_slot_t index = extractIndex(handle);
+    if (index >= mMaxHandleCount) {
+        ALOGE("HandleTracker::handleToIndex() invalid handle = 0x%08X", handle);
+        return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
+    }
+    handle_tracker_generation_t handleGeneration = extractGeneration(handle);
+    handle_tracker_header_t inputHeader = buildHeader(type, handleGeneration);
+    if (inputHeader != mHandleHeaders[index]) {
+        ALOGE("HandleTracker::handleToIndex() inputHeader = 0x%08x != mHandleHeaders[%d] = 0x%08x",
+             inputHeader, index, mHandleHeaders[index]);
+        return static_cast<oboe_handle_t>(OBOE_ERROR_INVALID_HANDLE);
+    }
+    return index;
+}
+
+handle_tracker_address_t HandleTracker::get(handle_tracker_type_t type, oboe_handle_t handle) const
+{
+    if (!isInitialized()) {
+        return nullptr;
+    }
+    handle_tracker_slot_t index = handleToIndex(type, handle);
+    if (index >= 0) {
+        return mHandleAddresses[index];
+    } else {
+        return nullptr;
+    }
+}
+
+handle_tracker_address_t HandleTracker::remove(handle_tracker_type_t type, oboe_handle_t handle) {
+    if (!isInitialized()) {
+        return nullptr;
+    }
+    handle_tracker_slot_t index = handleToIndex(type,handle);
+    if (index >= 0) {
+        handle_tracker_address_t address = mHandleAddresses[index];
+
+        // Invalidate the header type but preserve the generation count.
+        handle_tracker_generation_t generation = mHandleHeaders[index] & GENERATION_MASK;
+        handle_tracker_header_t inputHeader = buildHeader(
+                (handle_tracker_type_t) HANDLE_INVALID_TYPE, generation);
+        mHandleHeaders[index] = inputHeader;
+
+        // Add this slot to the head of the linked list.
+        mHandleAddresses[index] = mNextFreeAddress;
+        mNextFreeAddress = (handle_tracker_address_t *) &mHandleAddresses[index];
+        return address;
+    } else {
+        return nullptr;
+    }
+}
+
+oboe_handle_t HandleTracker::buildHandle(handle_tracker_header_t typeGeneration,
+                                         handle_tracker_slot_t index) {
+    return (oboe_handle_t)((typeGeneration << GENERATION_SHIFT) | (index & INDEX_MASK));
+}
+
+handle_tracker_header_t HandleTracker::buildHeader(handle_tracker_type_t type,
+                                    handle_tracker_generation_t generation)
+{
+    return (handle_tracker_header_t) (((type & TYPE_MASK) << GENERATION_SIZE)
+        | (generation & GENERATION_MASK));
+}
+
+handle_tracker_slot_t HandleTracker::extractIndex(oboe_handle_t handle)
+{
+    return handle & INDEX_MASK;
+}
+
+handle_tracker_generation_t HandleTracker::extractGeneration(oboe_handle_t handle)
+{
+    return (handle >> GENERATION_SHIFT) & GENERATION_MASK;
+}
diff --git a/media/liboboe/src/utility/HandleTracker.h b/media/liboboe/src/utility/HandleTracker.h
new file mode 100644
index 0000000..da5b654
--- /dev/null
+++ b/media/liboboe/src/utility/HandleTracker.h
@@ -0,0 +1,139 @@
+/*
+ * 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.
+ */
+
+#ifndef UTILITY_HANDLETRACKER_H
+#define UTILITY_HANDLETRACKER_H
+
+#include <stdint.h>
+
+typedef int32_t  handle_tracker_type_t;       // what kind of handle
+typedef int32_t  handle_tracker_slot_t;       // index in allocation table
+typedef int32_t  handle_tracker_generation_t; // incremented when slot used
+typedef uint16_t handle_tracker_header_t;     // combines type and generation
+typedef void    *handle_tracker_address_t;    // address of something that is stored here
+
+#define HANDLE_TRACKER_MAX_TYPES    (1 << 3)
+#define HANDLE_TRACKER_MAX_HANDLES  (1 << 16)
+
+/**
+ * Represent Objects using an integer handle that can be used with Java.
+ * This also makes the 'C' ABI more robust.
+ *
+ * Note that this should only be called from a single thread.
+ * If you call it from more than one thread then you need to use your own mutex.
+ */
+class HandleTracker {
+
+public:
+    /**
+     * @param maxHandles cannot exceed HANDLE_TRACKER_MAX_HANDLES
+     */
+    HandleTracker(uint32_t maxHandles);
+    virtual ~HandleTracker();
+
+    /**
+     * Don't use if this returns false;
+     * @return true if the internal allocation succeeded
+     */
+    bool isInitialized() const;
+
+    /**
+     * Store a pointer and return a handle that can be used to retrieve the pointer.
+     *
+     * @param type the type of the object to be tracked
+     * @param address pointer to be converted to a handle
+     * @return a valid handle or a negative error
+     */
+    oboe_handle_t put(handle_tracker_type_t expectedType, handle_tracker_address_t address);
+
+    /**
+     * Get the original pointer associated with the handle.
+     * The handle will be validated to prevent stale handles from being reused.
+     * Note that the validation is designed to prevent common coding errors and not
+     * to prevent deliberate hacking.
+     *
+     * @param expectedType shouldmatch the type we passed to put()
+     * @param handle to be converted to a pointer
+     * @return address associated with handle or nullptr
+     */
+    handle_tracker_address_t get(handle_tracker_type_t expectedType, oboe_handle_t handle) const;
+
+    /**
+     * Free up the storage associated with the handle.
+     * Subsequent attempts to use the handle will fail.
+     *
+     * @param expectedType shouldmatch the type we passed to put()
+     * @param handle to be removed from tracking
+     * @return address associated with handle or nullptr if not found
+     */
+    handle_tracker_address_t remove(handle_tracker_type_t expectedType, oboe_handle_t handle);
+
+private:
+    const int32_t               mMaxHandleCount;   // size of array
+    // This is const after initialization.
+    handle_tracker_address_t  * mHandleAddresses;  // address of objects or a free linked list node
+    // This is const after initialization.
+    handle_tracker_header_t   * mHandleHeaders;    // combination of type and generation
+    handle_tracker_address_t  * mNextFreeAddress; // head of the linked list of free nodes in mHandleAddresses
+
+    /**
+     * Pull slot off of a list of empty slots.
+     * @return index or a negative error
+     */
+    handle_tracker_slot_t allocateSlot();
+
+    /**
+     * Validate the handle and return the corresponding index.
+     * @return slot index or a negative error
+     */
+    handle_tracker_slot_t handleToIndex(oboe_handle_t handle, handle_tracker_type_t type) const;
+
+    /**
+     * Construct a handle from a header and an index.
+     * @param header combination of a type and a generation
+     * @param index slot index returned from allocateSlot
+     * @return handle or a negative error
+     */
+    oboe_handle_t buildHandle(handle_tracker_header_t header, handle_tracker_slot_t index);
+
+    /**
+     * Combine a type and a generation field into a header.
+     */
+    static handle_tracker_header_t buildHeader(handle_tracker_type_t type,
+                handle_tracker_generation_t generation);
+
+    /**
+     * Extract the index from a handle.
+     * Does not validate the handle.
+     * @return index associated with a handle
+     */
+    static handle_tracker_slot_t extractIndex(oboe_handle_t handle);
+
+    /**
+     * Extract the generation from a handle.
+     * Does not validate the handle.
+     * @return generation associated with a handle
+     */
+    static handle_tracker_generation_t extractGeneration(oboe_handle_t handle);
+
+    /**
+     * Increment the generation for the slot, avoiding zero.
+     */
+    handle_tracker_generation_t nextGeneration(handle_tracker_slot_t index);
+
+};
+
+#endif //UTILITY_HANDLETRACKER_H
diff --git a/media/liboboe/src/utility/MonotonicCounter.h b/media/liboboe/src/utility/MonotonicCounter.h
new file mode 100644
index 0000000..befad21
--- /dev/null
+++ b/media/liboboe/src/utility/MonotonicCounter.h
@@ -0,0 +1,91 @@
+/*
+ * 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.
+ */
+
+#ifndef UTILITY_MONOTONICCOUNTER_H
+#define UTILITY_MONOTONICCOUNTER_H
+
+#include <stdint.h>
+
+/**
+ * Maintain a 64-bit monotonic counter.
+ * Can be used to track a 32-bit counter that wraps or gets reset.
+ *
+ * Note that this is not atomic and has no interior locks.
+ * A caller will need to provide their own exterior locking
+ * if they need to use it from multiple threads.
+ */
+class MonotonicCounter {
+
+public:
+    MonotonicCounter() {};
+    virtual ~MonotonicCounter() {};
+
+    /**
+     * @return current value of the counter
+     */
+    int64_t get() const {
+        return mCounter64;
+    }
+
+    /**
+     * Advance the counter if delta is positive.
+     * @return current value of the counter
+     */
+    int64_t increment(int64_t delta) {
+        if (delta > 0) {
+            mCounter64 += delta;
+        }
+        return mCounter64;
+    }
+
+    /**
+     * Advance the 64-bit counter if (current32 - previousCurrent32) > 0.
+     * This can be used to convert a 32-bit counter that may be wrapping into
+     * a monotonic 64-bit counter.
+     *
+     * This counter32 should NOT be allowed to advance by more than 0x7FFFFFFF between calls.
+     * Think of the wrapping counter like a sine wave. If the frequency of the signal
+     * is more than half the sampling rate (Nyquist rate) then you cannot measure it properly.
+     * If the counter wraps around every 24 hours then we should measure it with a period
+     * of less than 12 hours.
+     *
+     * @return current value of the 64-bit counter
+     */
+    int64_t update32(int32_t counter32) {
+        int32_t delta = counter32 - mCounter32;
+        // protect against the mCounter64 going backwards
+        if (delta > 0) {
+            mCounter64 += delta;
+            mCounter32 = counter32;
+        }
+        return mCounter64;
+    }
+
+    /**
+     * Reset the stored value of the 32-bit counter.
+     * This is used if your counter32 has been reset to zero.
+     */
+    void reset32() {
+        mCounter32 = 0;
+    }
+
+private:
+    int64_t mCounter64 = 0;
+    int32_t mCounter32 = 0;
+};
+
+
+#endif //UTILITY_MONOTONICCOUNTER_H
diff --git a/media/liboboe/src/utility/OboeUtilities.cpp b/media/liboboe/src/utility/OboeUtilities.cpp
new file mode 100644
index 0000000..b28f7c7
--- /dev/null
+++ b/media/liboboe/src/utility/OboeUtilities.cpp
@@ -0,0 +1,160 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "OboeAudio"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+#include <utils/Errors.h>
+
+#include "oboe/OboeDefinitions.h"
+#include "OboeUtilities.h"
+
+using namespace android;
+
+oboe_size_bytes_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format) {
+    oboe_datatype_t dataType = OBOE_AUDIO_FORMAT_DATA_TYPE(format);
+    oboe_size_bytes_t size;
+    switch (dataType) {
+        case OBOE_AUDIO_DATATYPE_UINT8:
+            size = sizeof(uint8_t);
+            break;
+        case OBOE_AUDIO_DATATYPE_INT16:
+            size = sizeof(int16_t);
+            break;
+        case OBOE_AUDIO_DATATYPE_INT32:
+        case OBOE_AUDIO_DATATYPE_INT824:
+            size = sizeof(int32_t);
+            break;
+        case OBOE_AUDIO_DATATYPE_FLOAT32:
+            size = sizeof(float);
+            break;
+        default:
+            size = OBOE_ERROR_ILLEGAL_ARGUMENT;
+            break;
+    }
+    return size;
+}
+
+// TODO This similar to a function in audio_utils. Consider using that instead.
+void OboeConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination) {
+    for (int i = 0; i < numSamples; i++) {
+        float fval = source[i];
+        fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation
+        fval *= 32768.0f;
+        int32_t sample = (int32_t) fval;
+        // clip to 16-bit range
+        if (sample < 0) sample = 0;
+        else if (sample > 0x0FFFF) sample = 0x0FFFF;
+        sample -= 32768; // center at zero
+        destination[i] = (int16_t) sample;
+    }
+}
+
+void OboeConvert_pcm16ToFloat(const float *source, int32_t numSamples, int16_t *destination) {
+    for (int i = 0; i < numSamples; i++) {
+        destination[i] = source[i] * (1.0f / 32768.0f);
+    }
+}
+
+oboe_result_t OboeConvert_androidToOboeError(status_t error) {
+    if (error >= 0) {
+        return error;
+    }
+    oboe_result_t result;
+    switch (error) {
+    case OK:
+        result = OBOE_OK;
+        break;
+    case INVALID_OPERATION:
+        result = OBOE_ERROR_INVALID_STATE;
+        break;
+    case BAD_VALUE:
+        result = OBOE_ERROR_UNEXPECTED_VALUE;
+        break;
+    case WOULD_BLOCK:
+        result = OBOE_ERROR_WOULD_BLOCK;
+        break;
+    // TODO add more error codes
+    default:
+        result = OBOE_ERROR_INTERNAL;
+        break;
+    }
+    return result;
+}
+
+audio_format_t OboeConvert_oboeToAndroidDataFormat(oboe_audio_format_t oboeFormat) {
+    audio_format_t androidFormat;
+    switch (oboeFormat) {
+    case OBOE_AUDIO_FORMAT_PCM16:
+        androidFormat = AUDIO_FORMAT_PCM_16_BIT;
+        break;
+    case OBOE_AUDIO_FORMAT_PCM_FLOAT:
+        androidFormat = AUDIO_FORMAT_PCM_FLOAT;
+        break;
+    case OBOE_AUDIO_FORMAT_PCM824:
+        androidFormat = AUDIO_FORMAT_PCM_8_24_BIT;
+        break;
+    case OBOE_AUDIO_FORMAT_PCM32:
+        androidFormat = AUDIO_FORMAT_PCM_32_BIT;
+        break;
+    default:
+        androidFormat = AUDIO_FORMAT_DEFAULT;
+        ALOGE("OboeConvert_oboeToAndroidDataFormat 0x%08X unrecognized", oboeFormat);
+        break;
+    }
+    return androidFormat;
+}
+
+oboe_audio_format_t OboeConvert_androidToOboeDataFormat(audio_format_t androidFormat) {
+    oboe_audio_format_t oboeFormat = OBOE_AUDIO_FORMAT_INVALID;
+    switch (androidFormat) {
+    case AUDIO_FORMAT_PCM_16_BIT:
+        oboeFormat = OBOE_AUDIO_FORMAT_PCM16;
+        break;
+    case AUDIO_FORMAT_PCM_FLOAT:
+        oboeFormat = OBOE_AUDIO_FORMAT_PCM_FLOAT;
+        break;
+    case AUDIO_FORMAT_PCM_32_BIT:
+        oboeFormat = OBOE_AUDIO_FORMAT_PCM32;
+        break;
+    case AUDIO_FORMAT_PCM_8_24_BIT:
+        oboeFormat = OBOE_AUDIO_FORMAT_PCM824;
+        break;
+    default:
+        oboeFormat = OBOE_AUDIO_FORMAT_INVALID;
+        ALOGE("OboeConvert_androidToOboeDataFormat 0x%08X unrecognized", androidFormat);
+        break;
+    }
+    return oboeFormat;
+}
+
+oboe_size_bytes_t OboeConvert_framesToBytes(oboe_size_frames_t numFrames,
+                                            oboe_size_bytes_t bytesPerFrame,
+                                            oboe_size_bytes_t *sizeInBytes) {
+    // TODO implement more elegantly
+    const int32_t maxChannels = 256; // ridiculously large
+    const oboe_size_frames_t maxBytesPerFrame = maxChannels * sizeof(float);
+    // Prevent overflow by limiting multiplicands.
+    if (bytesPerFrame > maxBytesPerFrame || numFrames > (0x3FFFFFFF / maxBytesPerFrame)) {
+        ALOGE("size overflow, numFrames = %d, frameSize = %zd", numFrames, bytesPerFrame);
+        return OBOE_ERROR_OUT_OF_RANGE;
+    }
+    *sizeInBytes = numFrames * bytesPerFrame;
+    return OBOE_OK;
+}
diff --git a/media/liboboe/src/utility/OboeUtilities.h b/media/liboboe/src/utility/OboeUtilities.h
new file mode 100644
index 0000000..974ccf6
--- /dev/null
+++ b/media/liboboe/src/utility/OboeUtilities.h
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#ifndef UTILITY_OBOEUTILITIES_H
+#define UTILITY_OBOEUTILITIES_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <hardware/audio.h>
+
+#include "oboe/OboeDefinitions.h"
+
+oboe_result_t OboeConvert_androidToOboeError(android::status_t error);
+
+void OboeConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination);
+
+void OboeConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination);
+
+/**
+ * Calculate the number of bytes and prevent numeric overflow.
+ * @param numFrames frame count
+ * @param bytesPerFrame size of a frame in bytes
+ * @param sizeInBytes total size in bytes
+ * @return OBOE_OK or negative error, eg. OBOE_ERROR_OUT_OF_RANGE
+ */
+oboe_size_bytes_t OboeConvert_framesToBytes(oboe_size_frames_t numFrames,
+                                            oboe_size_bytes_t bytesPerFrame,
+                                            oboe_size_bytes_t *sizeInBytes);
+
+audio_format_t OboeConvert_oboeToAndroidDataFormat(oboe_audio_format_t oboe_format);
+
+oboe_audio_format_t OboeConvert_androidToOboeDataFormat(audio_format_t format);
+
+/**
+ * @return the size of a sample of the given format in bytes or OBOE_ERROR_ILLEGAL_ARGUMENT
+ */
+oboe_size_bytes_t OboeConvert_formatToSizeInBytes(oboe_audio_format_t format);
+
+#endif //UTILITY_OBOEUTILITIES_H
diff --git a/media/liboboe/src/utility/README.md b/media/liboboe/src/utility/README.md
new file mode 100644
index 0000000..9db926a
--- /dev/null
+++ b/media/liboboe/src/utility/README.md
@@ -0,0 +1,3 @@
+The utility folder contains things that may be shared between the Oboe client and server.
+They might also be handy outside Oboe.
+They generally do not depend on Oboe functionality.