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/Android.bp b/media/liboboe/Android.bp
new file mode 100644
index 0000000..0d22e65
--- /dev/null
+++ b/media/liboboe/Android.bp
@@ -0,0 +1,27 @@
+// 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.
+
+ndk_headers {
+    name: "libOboe_headers",
+    from: "include",
+    to: "",
+    srcs: ["include/oboe/*.h"],
+    license: "include/oboe/NOTICE",
+}
+
+ndk_library {
+    name: "liboboe.ndk",
+    symbol_file: "liboboe.map.txt",
+    first_version: "26",
+}
diff --git a/media/liboboe/Android.mk b/media/liboboe/Android.mk
new file mode 100644
index 0000000..5053e7d
--- /dev/null
+++ b/media/liboboe/Android.mk
@@ -0,0 +1 @@
+include $(call all-subdir-makefiles)
diff --git a/media/liboboe/include/oboe/NOTICE b/media/liboboe/include/oboe/NOTICE
new file mode 100644
index 0000000..d6c0922
--- /dev/null
+++ b/media/liboboe/include/oboe/NOTICE
@@ -0,0 +1,13 @@
+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.
diff --git a/media/liboboe/include/oboe/OboeAudio.h b/media/liboboe/include/oboe/OboeAudio.h
index 6804ce8..788cf5f 100644
--- a/media/liboboe/include/oboe/OboeAudio.h
+++ b/media/liboboe/include/oboe/OboeAudio.h
@@ -29,11 +29,14 @@
 typedef int32_t OboeDeviceId;
 typedef oboe_handle_t OboeStream;
 typedef oboe_handle_t OboeStreamBuilder;
-typedef oboe_handle_t OboeThread;
 
 #define OBOE_STREAM_NONE         ((OboeStream)OBOE_HANDLE_INVALID)
 #define OBOE_STREAM_BUILDER_NONE ((OboeStreamBuilder)OBOE_HANDLE_INVALID)
 
+/* OBOE_API will probably get defined in a Makefile for a specific platform. */
+#ifndef OBOE_API
+#define OBOE_API /* for exporting symbols */
+#endif
 
 // ============================================================
 // Audio System
@@ -42,7 +45,7 @@
 /**
  * @return time in the same clock domain as the timestamps
  */
-oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
+OBOE_API oboe_nanoseconds_t Oboe_getNanoseconds(oboe_clockid_t clockid);
 
 /**
  * The text is the ASCII symbol corresponding to the returnCode,
@@ -52,7 +55,7 @@
  *
  * @return pointer to a text representation of an Oboe result code.
  */
-const char * Oboe_convertResultToText(oboe_result_t returnCode);
+OBOE_API const char * Oboe_convertResultToText(oboe_result_t returnCode);
 
 /**
  * The text is the ASCII symbol corresponding to the stream state,
@@ -62,7 +65,7 @@
  *
  * @return pointer to a text representation of an Oboe state.
  */
-const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
+OBOE_API const char * Oboe_convertStreamStateToText(oboe_stream_state_t state);
 
 // ============================================================
 // StreamBuilder
@@ -80,7 +83,7 @@
  *
  * OboeStreamBuilder_delete() must be called when you are done using the builder.
  */
-oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
+OBOE_API oboe_result_t Oboe_createStreamBuilder(OboeStreamBuilder *builder);
 
 /**
  * Request an audio device identified device using an ID.
@@ -91,7 +94,8 @@
  *
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder, OboeDeviceId deviceId);
+OBOE_API oboe_result_t OboeStreamBuilder_setDeviceId(OboeStreamBuilder builder,
+                                                     OboeDeviceId deviceId);
 
 /**
  * Request a sample rate in Hz.
@@ -106,14 +110,14 @@
  *
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_setSampleRate(OboeStreamBuilder builder,
                                               oboe_sample_rate_t sampleRate);
 
 /**
  * Returns sample rate in Hertz (samples per second).
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_getSampleRate(OboeStreamBuilder builder,
                                               oboe_sample_rate_t *sampleRate);
 
 
@@ -128,7 +132,7 @@
  *
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_setSamplesPerFrame(OboeStreamBuilder builder,
                                                    int32_t samplesPerFrame);
 
 /**
@@ -138,7 +142,7 @@
  * @param samplesPerFrame pointer to a variable to be set to samplesPerFrame.
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
+OBOE_API oboe_result_t OboeStreamBuilder_getSamplesPerFrame(OboeStreamBuilder builder,
                                                    int32_t *samplesPerFrame);
 
 
@@ -148,12 +152,14 @@
  *
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder, oboe_audio_format_t format);
+OBOE_API oboe_result_t OboeStreamBuilder_setFormat(OboeStreamBuilder builder,
+                                                   oboe_audio_format_t format);
 
 /**
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder, oboe_audio_format_t *format);
+OBOE_API oboe_result_t OboeStreamBuilder_getFormat(OboeStreamBuilder builder,
+                                                   oboe_audio_format_t *format);
 
 /**
  * Request a mode for sharing the device.
@@ -164,15 +170,15 @@
  * @param sharingMode OBOE_SHARING_MODE_LEGACY or OBOE_SHARING_MODE_EXCLUSIVE
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
-                                               oboe_sharing_mode_t sharingMode);
+OBOE_API oboe_result_t OboeStreamBuilder_setSharingMode(OboeStreamBuilder builder,
+                                                        oboe_sharing_mode_t sharingMode);
 
 /**
  * Return requested sharing mode.
  * @return OBOE_OK or a negative error
  */
-oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
-                                               oboe_sharing_mode_t *sharingMode);
+OBOE_API oboe_result_t OboeStreamBuilder_getSharingMode(OboeStreamBuilder builder,
+                                                        oboe_sharing_mode_t *sharingMode);
 
 /**
  * Request the direction for a stream. The default is OBOE_DIRECTION_OUTPUT.
@@ -181,16 +187,16 @@
  * @param direction OBOE_DIRECTION_OUTPUT or OBOE_DIRECTION_INPUT
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
-                                             oboe_direction_t direction);
+OBOE_API oboe_result_t OboeStreamBuilder_setDirection(OboeStreamBuilder builder,
+                                                      oboe_direction_t direction);
 
 /**
  * @param builder handle provided by Oboe_createStreamBuilder()
  * @param direction pointer to a variable to be set to the currently requested direction.
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
-                                             oboe_direction_t *direction);
+OBOE_API oboe_result_t OboeStreamBuilder_getDirection(OboeStreamBuilder builder,
+                                                      oboe_direction_t *direction);
 
 /**
  * Open a stream based on the options in the StreamBuilder.
@@ -202,7 +208,8 @@
  * @param stream pointer to a variable to receive the new stream handle
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStreamBuilder_openStream(OboeStreamBuilder builder, OboeStream *stream);
+OBOE_API oboe_result_t  OboeStreamBuilder_openStream(OboeStreamBuilder builder,
+                                                     OboeStream *stream);
 
 /**
  * Delete the resources associated with the StreamBuilder.
@@ -210,7 +217,7 @@
  * @param builder handle provided by Oboe_createStreamBuilder()
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStreamBuilder_delete(OboeStreamBuilder builder);
+OBOE_API oboe_result_t  OboeStreamBuilder_delete(OboeStreamBuilder builder);
 
 // ============================================================
 // Stream Control
@@ -222,7 +229,7 @@
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStream_close(OboeStream stream);
+OBOE_API oboe_result_t  OboeStream_close(OboeStream stream);
 
 /**
  * Asynchronously request to start playing the stream. For output streams, one should
@@ -233,7 +240,7 @@
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStream_requestStart(OboeStream stream);
+OBOE_API oboe_result_t  OboeStream_requestStart(OboeStream stream);
 
 /**
  * Asynchronous request for the stream to pause.
@@ -244,7 +251,7 @@
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStream_requestPause(OboeStream stream);
+OBOE_API oboe_result_t  OboeStream_requestPause(OboeStream stream);
 
 /**
  * Asynchronous request for the stream to flush.
@@ -256,7 +263,7 @@
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStream_requestFlush(OboeStream stream);
+OBOE_API oboe_result_t  OboeStream_requestFlush(OboeStream stream);
 
 /**
  * Asynchronous request for the stream to stop.
@@ -266,7 +273,7 @@
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t  OboeStream_requestStop(OboeStream stream);
+OBOE_API oboe_result_t  OboeStream_requestStop(OboeStream stream);
 
 /**
  * Query the current state, eg. OBOE_STREAM_STATE_PAUSING
@@ -275,7 +282,7 @@
  * @param state pointer to a variable that will be set to the current state
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
+OBOE_API oboe_result_t OboeStream_getState(OboeStream stream, oboe_stream_state_t *state);
 
 /**
  * Wait until the current state no longer matches the input state.
@@ -295,7 +302,7 @@
  * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_waitForStateChange(OboeStream stream,
                                             oboe_stream_state_t inputState,
                                             oboe_stream_state_t *nextState,
                                             oboe_nanoseconds_t timeoutNanoseconds);
@@ -322,9 +329,9 @@
  * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
  * @return The number of frames actually written or a negative error.
  */
-oboe_result_t OboeStream_read(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_read(OboeStream stream,
                                void *buffer,
-                               int32_t numFrames,
+                               oboe_size_frames_t numFrames,
                                oboe_nanoseconds_t timeoutNanoseconds);
 
 /**
@@ -345,9 +352,9 @@
  * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
  * @return The number of frames actually written or a negative error.
  */
-oboe_result_t OboeStream_write(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_write(OboeStream stream,
                                const void *buffer,
-                               int32_t numFrames,
+                               oboe_size_frames_t numFrames,
                                oboe_nanoseconds_t timeoutNanoseconds);
 
 
@@ -356,31 +363,33 @@
 // ============================================================
 
 /**
- * Create a thread with special properties for low latency audio performance.
- * This thread can be used to implement a callback API.
+ * Create a thread associated with a stream. The thread has special properties for
+ * low latency audio performance. This thread can be used to implement a callback API.
+ *
+ * Only one thread may be associated with a stream.
  *
  * Note that this API is in flux.
  *
- * @param threadHandlePtr a pointer to receive a thread handle
+ * @param stream A stream created using OboeStreamBuilder_openStream().
  * @param periodNanoseconds the estimated period at which the audio thread will need to wake up
  * @param start_routine your thread entry point
  * @param arg an argument that will be passed to your thread entry point
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t Oboe_createAudioThread(OboeThread *threadHandlePtr,
+OBOE_API oboe_result_t OboeStream_createThread(OboeStream stream,
                                      oboe_nanoseconds_t periodNanoseconds,
-                                     void *(*start_routine)(void *), void *arg);
+                                     void *(*startRoutine)(void *), void *arg);
 
 /**
  * Wait until the thread exits or an error occurs.
  * The thread handle will be deleted.
  *
- * @param thread the thread handle passed back from Oboe_createAudioThread()
+ * @param stream A stream created using OboeStreamBuilder_openStream().
  * @param returnArg a pointer to a variable to receive the return value
  * @param timeoutNanoseconds Maximum number of nanoseconds to wait for completion.
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t Oboe_joinAudioThread(OboeThread thread,
+OBOE_API oboe_result_t OboeStream_joinThread(OboeStream stream,
                                    void **returnArg,
                                    oboe_nanoseconds_t timeoutNanoseconds);
 
@@ -398,10 +407,13 @@
  * This cannot be set higher than OboeStream_getBufferCapacity().
  *
  * @param stream handle provided by OboeStreamBuilder_openStream()
- * @param frames requested number of frames that can be filled without blocking
- * @return actual number of frames or a negative error
+ * @param requestedFrames requested number of frames that can be filled without blocking
+ * @return actualFrames receives final number of frames
+ * @return OBOE_OK or a negative error
  */
-oboe_result_t OboeStream_setBufferSize(OboeStream stream, oboe_size_frames_t frames);
+OBOE_API oboe_result_t OboeStream_setBufferSize(OboeStream stream,
+                                                oboe_size_frames_t requestedFrames,
+                                                oboe_size_frames_t *actualFrames);
 
 /**
  * Query the maximum number of frames that can be filled without blocking.
@@ -410,7 +422,7 @@
  * @param frames pointer to variable to receive the buffer size
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getBufferSize(OboeStream stream, oboe_size_frames_t *frames);
 
 /**
  * Query the number of frames that are read or written by the endpoint at one time.
@@ -419,7 +431,7 @@
  * @param frames pointer to variable to receive the burst size
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesPerBurst(OboeStream stream, oboe_size_frames_t *frames);
 
 /**
  * Query maximum buffer capacity in frames.
@@ -428,7 +440,7 @@
  * @param frames pointer to variable to receive the buffer capacity
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getBufferCapacity(OboeStream stream, oboe_size_frames_t *frames);
 
 /**
  * An XRun is an Underrun or an Overrun.
@@ -443,14 +455,14 @@
  * @param xRunCount pointer to variable to receive the underrun or overrun count
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
+OBOE_API oboe_result_t OboeStream_getXRunCount(OboeStream stream, int32_t *xRunCount);
 
 /**
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @param sampleRate pointer to variable to receive the actual sample rate
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getSampleRate(OboeStream stream, int32_t *sampleRate);
+OBOE_API oboe_result_t OboeStream_getSampleRate(OboeStream stream, oboe_sample_rate_t *sampleRate);
 
 /**
  * The samplesPerFrame is also known as channelCount.
@@ -459,14 +471,14 @@
  * @param samplesPerFrame pointer to variable to receive the actual samples per frame
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
+OBOE_API oboe_result_t OboeStream_getSamplesPerFrame(OboeStream stream, int32_t *samplesPerFrame);
 
 /**
  * @param stream handle provided by OboeStreamBuilder_openStream()
  * @param format pointer to variable to receive the actual data format
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
+OBOE_API oboe_result_t OboeStream_getFormat(OboeStream stream, oboe_audio_format_t *format);
 
 /**
  * Provide actual sharing mode.
@@ -474,7 +486,7 @@
  * @param sharingMode pointer to variable to receive the actual sharing mode
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getSharingMode(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_getSharingMode(OboeStream stream,
                                         oboe_sharing_mode_t *sharingMode);
 
 /**
@@ -482,7 +494,7 @@
  * @param direction pointer to a variable to be set to the current direction.
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getDirection(OboeStream stream, uint32_t *direction);
+OBOE_API oboe_result_t OboeStream_getDirection(OboeStream stream, oboe_direction_t *direction);
 
 /**
  * Passes back the number of frames that have been written since the stream was created.
@@ -495,7 +507,8 @@
  * @param frames pointer to variable to receive the frames written
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getFramesWritten(OboeStream stream, oboe_position_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesWritten(OboeStream stream,
+                                                   oboe_position_frames_t *frames);
 
 /**
  * Passes back the number of frames that have been read since the stream was created.
@@ -508,7 +521,7 @@
  * @param frames pointer to variable to receive the frames written
  * @return OBOE_OK or a negative error.
  */
-oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
+OBOE_API oboe_result_t OboeStream_getFramesRead(OboeStream stream, oboe_position_frames_t *frames);
 
 /**
  * Passes back the time at which a particular frame was presented.
@@ -527,12 +540,12 @@
  * The position and time passed back are monotonically increasing.
  *
  * @param stream A handle provided by OboeStreamBuilder_openStream()
- * @param clockId OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
+ * @param clockid OBOE_CLOCK_MONOTONIC or OBOE_CLOCK_BOOTTIME
  * @param framePosition pointer to a variable to receive the position
  * @param timeNanoseconds pointer to a variable to receive the time
  * @return OBOE_OK or a negative error
  */
-oboe_result_t OboeStream_getTimestamp(OboeStream stream,
+OBOE_API oboe_result_t OboeStream_getTimestamp(OboeStream stream,
                                       oboe_clockid_t clockid,
                                       oboe_position_frames_t *framePosition,
                                       oboe_nanoseconds_t *timeNanoseconds);
diff --git a/media/liboboe/include/oboe/OboeDefinitions.h b/media/liboboe/include/oboe/OboeDefinitions.h
index 79fef3a..d80c958 100644
--- a/media/liboboe/include/oboe/OboeDefinitions.h
+++ b/media/liboboe/include/oboe/OboeDefinitions.h
@@ -28,7 +28,10 @@
 typedef int32_t  oboe_sample_rate_t;
 /** This is used for small quantities such as the number of frames in a buffer. */
 typedef int32_t  oboe_size_frames_t;
-/** This is used for large quantities, such as the number of frames that have
+/** This is used for small quantities such as the number of bytes in a frame. */
+typedef int32_t  oboe_size_bytes_t;
+/**
+ * This is used for large quantities, such as the number of frames that have
  * been played since a stream was started.
  * At 48000 Hz, a 32-bit integer would wrap around in just over 12 hours.
  */
@@ -85,7 +88,8 @@
 
 /**
  * Fields packed into oboe_audio_format_t, from most to least significant bits.
- *   Reserved:8
+ *   Invalid:1
+ *   Reserved:7
  *   Wrapper:8
  *   Content:8
  *   Data Type:8
@@ -97,7 +101,7 @@
                 OBOE_AUDIO_FORMAT(dataType, content, OBOE_AUDIO_WRAPPER_NONE)
 
 #define OBOE_AUDIO_FORMAT_DATA_TYPE(format) \
-    (format & 0x0FF)
+    ((oboe_datatype_t)(format & 0x0FF))
 
 // Define some common formats.
 #define OBOE_AUDIO_FORMAT_PCM16  \
@@ -106,6 +110,9 @@
                 OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_FLOAT32, OBOE_AUDIO_CONTENT_PCM)
 #define OBOE_AUDIO_FORMAT_PCM824 \
                 OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT824, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_PCM32 \
+                OBOE_AUDIO_FORMAT_RAW(OBOE_AUDIO_DATATYPE_INT32, OBOE_AUDIO_CONTENT_PCM)
+#define OBOE_AUDIO_FORMAT_INVALID ((oboe_audio_format_t)-1)
 
 enum {
     OBOE_OK,
@@ -126,7 +133,8 @@
     OBOE_ERROR_NULL,
     OBOE_ERROR_TIMEOUT,
     OBOE_ERROR_WOULD_BLOCK,
-    OBOE_ERROR_INVALID_ORDER
+    OBOE_ERROR_INVALID_ORDER,
+    OBOE_ERROR_OUT_OF_RANGE
 };
 
 typedef enum {
diff --git a/media/liboboe/liboboe.map.txt b/media/liboboe/liboboe.map.txt
new file mode 100644
index 0000000..229038f
--- /dev/null
+++ b/media/liboboe/liboboe.map.txt
@@ -0,0 +1,46 @@
+LIBOBOE {
+  global:
+    Oboe_getNanoseconds;
+    Oboe_convertResultToText;
+    Oboe_convertStreamStateToText;
+    Oboe_createStreamBuilder;
+    OboeStreamBuilder_setDeviceId;
+    OboeStreamBuilder_setSampleRate;
+    OboeStreamBuilder_getSampleRate;
+    OboeStreamBuilder_setSamplesPerFrame;
+    OboeStreamBuilder_getSamplesPerFrame;
+    OboeStreamBuilder_setFormat;
+    OboeStreamBuilder_getFormat;
+    OboeStreamBuilder_setSharingMode;
+    OboeStreamBuilder_getSharingMode;
+    OboeStreamBuilder_setDirection;
+    OboeStreamBuilder_getDirection;
+    OboeStreamBuilder_openStream;
+    OboeStreamBuilder_delete;
+    OboeStream_close;
+    OboeStream_requestStart;
+    OboeStream_requestPause;
+    OboeStream_requestFlush;
+    OboeStream_requestStop;
+    OboeStream_getState;
+    OboeStream_waitForStateChange;
+    OboeStream_read;
+    OboeStream_write;
+    Oboe_createAudioThread;
+    Oboe_joinAudioThread;
+    OboeStream_setBufferSize;
+    OboeStream_getBufferSize;
+    OboeStream_getFramesPerBurst;
+    OboeStream_getBufferCapacity;
+    OboeStream_getXRunCount;
+    OboeStream_getSampleRate;
+    OboeStream_getSamplesPerFrame;
+    OboeStream_getFormat;
+    OboeStream_getSharingMode;
+    OboeStream_getDirection;
+    OboeStream_getFramesWritten;
+    OboeStream_getFramesRead;
+    OboeStream_getTimestamp;
+  local:
+    *;
+};
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.
diff --git a/media/liboboe/tests/Android.mk b/media/liboboe/tests/Android.mk
new file mode 100644
index 0000000..f2c65d9
--- /dev/null
+++ b/media/liboboe/tests/Android.mk
@@ -0,0 +1,27 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/av/media/liboboe/include \
+    frameworks/av/media/liboboe/src/core \
+    frameworks/av/media/liboboe/src/utility
+LOCAL_SRC_FILES:= test_oboe_api.cpp
+LOCAL_SHARED_LIBRARIES := libaudioutils libmedia \
+                          libbinder libcutils libutils \
+                          libaudioclient liblog
+LOCAL_STATIC_LIBRARIES := liboboe
+LOCAL_MODULE := test_oboe_api
+include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+    $(call include-path-for, audio-utils) \
+    frameworks/av/media/liboboe/include \
+    frameworks/av/media/liboboe/src/core \
+    frameworks/av/media/liboboe/src/utility
+LOCAL_SRC_FILES:= test_handle_tracker.cpp
+LOCAL_SHARED_LIBRARIES := libbinder libcutils libutils liblog
+LOCAL_STATIC_LIBRARIES := liboboe
+LOCAL_MODULE := test_handle_tracker
+include $(BUILD_NATIVE_TEST)
diff --git a/media/liboboe/tests/test_handle_tracker.cpp b/media/liboboe/tests/test_handle_tracker.cpp
new file mode 100644
index 0000000..ae7384e
--- /dev/null
+++ b/media/liboboe/tests/test_handle_tracker.cpp
@@ -0,0 +1,117 @@
+/*
+ * 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.
+ */
+
+// Unit tests for Oboe Handle Tracker
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <gtest/gtest.h>
+
+#include <oboe/OboeDefinitions.h>
+#include "HandleTracker.h"
+
+// Test adding one address.
+TEST(test_handle_tracker, oboe_handle_tracker) {
+    const int MAX_HANDLES = 4;
+    HandleTracker tracker(MAX_HANDLES);
+    handle_tracker_type_t type = 3; // arbitrary generic type
+    int data; // something that has an address we can use
+    handle_tracker_address_t found;
+
+    // repeat the test several times to see if it breaks
+    const int SEVERAL = 5; // arbitrary
+    for (int i = 0; i < SEVERAL; i++) {
+        // should fail to find a bogus handle
+        found = tracker.get(type, 0);  // bad handle
+        EXPECT_EQ(nullptr, found);
+
+        // create a valid handle and use it to lookup the object again
+        oboe_handle_t dataHandle = tracker.put(type, &data);
+        ASSERT_TRUE(dataHandle > 0);
+        found = tracker.get(type, dataHandle);
+        EXPECT_EQ(&data, found);
+        found = tracker.get(type, 0); // bad handle
+        EXPECT_EQ(nullptr, found);
+
+        // wrong type
+        found = tracker.get(type+1, dataHandle);
+        EXPECT_EQ(nullptr, found);
+
+        // remove from storage
+        found = tracker.remove(type, dataHandle);
+        EXPECT_EQ(&data, found);
+        // should fail the second time
+        found = tracker.remove(type, dataHandle);
+        EXPECT_EQ(NULL, found);
+    }
+}
+
+// Test filling the tracker.
+TEST(test_handle_tracker, oboe_full_up) {
+    const int MAX_HANDLES = 5;
+    HandleTracker tracker(MAX_HANDLES);
+    handle_tracker_type_t type = 4; // arbitrary generic type
+    int data[MAX_HANDLES];
+    oboe_handle_t handles[MAX_HANDLES];
+    handle_tracker_address_t found;
+
+    // repeat the test several times to see if it breaks
+    const int SEVERAL = 5; // arbitrary
+    for (int i = 0; i < SEVERAL; i++) {
+        for (int i = 0; i < MAX_HANDLES; i++) {
+            // add a handle
+            handles[i] = tracker.put(type, &data[i]);
+            ASSERT_TRUE(handles[i] > 0);
+            found = tracker.get(type, handles[i]);
+            EXPECT_EQ(&data[i], found);
+        }
+
+        // Now that it is full, try to add one more.
+        oboe_handle_t handle = tracker.put(type, &data[0]);
+        EXPECT_TRUE(handle < 0);
+
+        for (int i = 0; i < MAX_HANDLES; i++) {
+            // look up each handle
+            found = tracker.get(type, handles[i]);
+            EXPECT_EQ(&data[i], found);
+        }
+
+        // remove one from storage
+        found = tracker.remove(type, handles[2]);
+        EXPECT_EQ(&data[2], found);
+        // now try to look up the same handle and fail
+        found = tracker.get(type, handles[2]);
+        EXPECT_EQ(nullptr, found);
+
+        // add that same one back
+        handle = tracker.put(type, &data[2]);
+        ASSERT_TRUE(handle > 0);
+        found = tracker.get(type, handle);
+        EXPECT_EQ(&data[2], found);
+        // now use a stale handle again with a valid index and fail
+        found = tracker.get(type, handles[2]);
+        EXPECT_EQ(nullptr, found);
+
+        // remove them all
+        handles[2] = handle;
+        for (int i = 0; i < MAX_HANDLES; i++) {
+            // look up each handle
+            found = tracker.remove(type, handles[i]);
+            EXPECT_EQ(&data[i], found);
+        }
+    }
+}
diff --git a/media/liboboe/tests/test_oboe_api.cpp b/media/liboboe/tests/test_oboe_api.cpp
new file mode 100644
index 0000000..b3fb24b
--- /dev/null
+++ b/media/liboboe/tests/test_oboe_api.cpp
@@ -0,0 +1,287 @@
+/*
+ * 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.
+ */
+
+// Unit tests for Oboe 'C' API.
+
+#include <stdlib.h>
+#include <math.h>
+
+#include <gtest/gtest.h>
+
+#include <oboe/OboeDefinitions.h>
+#include <oboe/OboeAudio.h>
+#include "OboeUtilities.h"
+
+#define DEFAULT_STATE_TIMEOUT  (500 * OBOE_NANOS_PER_MILLISECOND)
+
+// Test OboeStreamBuilder
+TEST(test_oboe_api, oboe_stream_builder) {
+    const oboe_sample_rate_t requestedSampleRate1 = 48000;
+    const oboe_sample_rate_t requestedSampleRate2 = 44100;
+    const int32_t requestedSamplesPerFrame = 2;
+    const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_DATATYPE_INT16;
+
+    oboe_sample_rate_t sampleRate = 0;
+    int32_t samplesPerFrame = 0;
+    oboe_audio_format_t actualDataFormat;
+    OboeStreamBuilder oboeBuilder1;
+    OboeStreamBuilder oboeBuilder2;
+
+    oboe_result_t result = OBOE_OK;
+
+    // Use an OboeStreamBuilder to define the stream.
+    result = Oboe_createStreamBuilder(&oboeBuilder1);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Request stream properties.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder1, requestedSampleRate1));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder1, requestedSamplesPerFrame));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder1, requestedDataFormat));
+
+    // Check to make sure builder saved the properties.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+    EXPECT_EQ(requestedSampleRate1, sampleRate);
+
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSamplesPerFrame(oboeBuilder1, &samplesPerFrame));
+    EXPECT_EQ(requestedSamplesPerFrame, samplesPerFrame);
+
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getFormat(oboeBuilder1, &actualDataFormat));
+    EXPECT_EQ(requestedDataFormat, actualDataFormat);
+
+    result = OboeStreamBuilder_getSampleRate(0x0BADCAFE, &sampleRate); // ridiculous token
+    EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+    // Create a second builder and make sure they do not collide.
+    ASSERT_EQ(OBOE_OK, Oboe_createStreamBuilder(&oboeBuilder2));
+    ASSERT_NE(oboeBuilder1, oboeBuilder2);
+
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder2, requestedSampleRate2));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate));
+    EXPECT_EQ(requestedSampleRate1, sampleRate);
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+    EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+    // Delete the builder.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder1));
+
+    // Now it should no longer be valid.
+    // Note that test assumes we are using the HandleTracker. If we use plain pointers
+    // then it will be difficult to detect this kind of error.
+    result = OboeStreamBuilder_getSampleRate(oboeBuilder1, &sampleRate); // stale token
+    EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, result);
+
+    // Second builder should still be valid.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+    EXPECT_EQ(requestedSampleRate2, sampleRate);
+
+    // Delete the second builder.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder2));
+
+    // Now it should no longer be valid. Assumes HandlerTracker used.
+    EXPECT_EQ(OBOE_ERROR_INVALID_HANDLE, OboeStreamBuilder_getSampleRate(oboeBuilder2, &sampleRate));
+}
+
+
+// Test creating a default stream with everything unspecified.
+TEST(test_oboe_api, oboe_stream_unspecified) {
+    OboeStreamBuilder oboeBuilder;
+    OboeStream oboeStream;
+    oboe_result_t result = OBOE_OK;
+
+    // Use an OboeStreamBuilder to define the stream.
+    result = Oboe_createStreamBuilder(&oboeBuilder);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Create an OboeStream using the Builder.
+    ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+
+    // Cleanup
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+    EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}
+
+// Test Writing to an OboeStream
+TEST(test_oboe_api, oboe_stream) {
+    const oboe_sample_rate_t requestedSampleRate = 48000;
+    const oboe_sample_rate_t requestedSamplesPerFrame = 2;
+    const oboe_audio_format_t requestedDataFormat = OBOE_AUDIO_DATATYPE_INT16;
+    //const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_EXCLUSIVE; // MMAP NOIRQ
+    const oboe_sharing_mode_t requestedSharingMode = OBOE_SHARING_MODE_LEGACY; // AudioTrack
+
+    oboe_sample_rate_t actualSampleRate = -1;
+    int32_t actualSamplesPerFrame = -1;
+    oboe_audio_format_t actualDataFormat = OBOE_AUDIO_FORMAT_PCM824;
+    oboe_sharing_mode_t actualSharingMode;
+    oboe_size_frames_t framesPerBurst = -1;
+
+    oboe_size_frames_t framesWritten = 0;
+    oboe_size_frames_t framesPrimed = 0;
+    oboe_position_frames_t framesTotal = 0;
+    oboe_position_frames_t oboeFramesRead = 0;
+    oboe_position_frames_t oboeFramesRead1 = 0;
+    oboe_position_frames_t oboeFramesRead2 = 0;
+    oboe_position_frames_t oboeFramesWritten = 0;
+
+    oboe_nanoseconds_t timeoutNanos;
+
+    oboe_stream_state_t state = OBOE_STREAM_STATE_UNINITIALIZED;
+    OboeStreamBuilder oboeBuilder;
+    OboeStream oboeStream;
+
+    oboe_result_t result = OBOE_OK;
+
+    // Use an OboeStreamBuilder to define the stream.
+    result = Oboe_createStreamBuilder(&oboeBuilder);
+    ASSERT_EQ(OBOE_OK, result);
+
+    // Request stream properties.
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSampleRate(oboeBuilder, requestedSampleRate));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSamplesPerFrame(oboeBuilder, requestedSamplesPerFrame));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setFormat(oboeBuilder, requestedDataFormat));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_setSharingMode(oboeBuilder, requestedSharingMode));
+
+    // Create an OboeStream using the Builder.
+    ASSERT_EQ(OBOE_OK, OboeStreamBuilder_openStream(oboeBuilder, &oboeStream));
+    EXPECT_EQ(OBOE_OK, OboeStreamBuilder_delete(oboeBuilder));
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getState(oboeStream, &state));
+    EXPECT_EQ(OBOE_STREAM_STATE_OPEN, state);
+
+    // Check to see what kind of stream we actually got.
+    EXPECT_EQ(OBOE_OK, OboeStream_getSampleRate(oboeStream, &actualSampleRate));
+    EXPECT_TRUE(actualSampleRate >= 44100 && actualSampleRate <= 96000);  // TODO what is range?
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getSamplesPerFrame(oboeStream, &actualSamplesPerFrame));
+    EXPECT_TRUE(actualSamplesPerFrame >= 1 && actualSamplesPerFrame <= 16); // TODO what is max?
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getSharingMode(oboeStream, &actualSharingMode));
+    EXPECT_TRUE(actualSharingMode == OBOE_SHARING_MODE_EXCLUSIVE
+            || actualSharingMode == OBOE_SHARING_MODE_LEGACY);
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesPerBurst(oboeStream, &framesPerBurst));
+    EXPECT_TRUE(framesPerBurst >= 16 && framesPerBurst <= 1024); // TODO what is min/max?
+
+    // Allocate a buffer for the audio data.
+    int16_t *data = new int16_t[framesPerBurst * actualSamplesPerFrame];
+    ASSERT_TRUE(NULL != data);
+
+    timeoutNanos = 0;
+    do {
+        framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+        // There should be some room for priming the buffer.
+        framesTotal += framesWritten;
+        ASSERT_GE(framesWritten, 0);
+        ASSERT_LE(framesWritten, framesPerBurst);
+    } while(framesWritten > 0);
+    ASSERT_TRUE(framesTotal > 0);
+
+    // Start and wait for server to respond.
+    ASSERT_EQ(OBOE_OK, OboeStream_requestStart(oboeStream));
+    ASSERT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+                                                     OBOE_STREAM_STATE_STARTING,
+                                                     &state,
+                                                     DEFAULT_STATE_TIMEOUT));
+    EXPECT_EQ(OBOE_STREAM_STATE_STARTED, state);
+
+    // Write some data while we are running. Read counter should be advancing.
+    int loops = 1 * actualSampleRate / framesPerBurst; // 1 second
+    ASSERT_LT(2, loops); // detect absurdly high framesPerBurst
+    timeoutNanos = 10 * OBOE_NANOS_PER_SECOND * framesPerBurst / actualSampleRate; // bursts
+    framesWritten = 1;
+    ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+    oboeFramesRead1 = oboeFramesRead;
+    oboe_nanoseconds_t beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+    do {
+        framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+        ASSERT_GE(framesWritten, 0);
+        ASSERT_LE(framesWritten, framesPerBurst);
+
+        framesTotal += framesWritten;
+        EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+        EXPECT_EQ(framesTotal, oboeFramesWritten);
+
+        // Try to get a more accurate measure of the sample rate.
+        if (beginTime == 0) {
+            EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+            if (oboeFramesRead > oboeFramesRead1) { // is read pointer advancing
+                beginTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+                oboeFramesRead1 = oboeFramesRead;
+            }
+        }
+    } while (framesWritten > 0 && loops-- > 0);
+
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+    oboe_nanoseconds_t endTime = Oboe_getNanoseconds(OBOE_CLOCK_MONOTONIC);
+    ASSERT_GT(oboeFramesRead2, 0);
+    ASSERT_GT(oboeFramesRead2, oboeFramesRead1);
+    ASSERT_LE(oboeFramesRead2, oboeFramesWritten);
+
+    // TODO why is legacy so inaccurate?
+    const double rateTolerance = 200.0; // arbitrary tolerance for sample rate
+    if (requestedSharingMode != OBOE_SHARING_MODE_LEGACY) {
+        // Calculate approximate sample rate and compare with stream rate.
+        double seconds = (endTime - beginTime) / (double) OBOE_NANOS_PER_SECOND;
+        double measuredRate = (oboeFramesRead2 - oboeFramesRead1) / seconds;
+        ASSERT_NEAR(actualSampleRate, measuredRate, rateTolerance);
+    }
+
+    // Request async pause and wait for server to say that it has completed the pause.
+    ASSERT_EQ(OBOE_OK, OboeStream_requestPause(oboeStream));
+    EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+                                            OBOE_STREAM_STATE_PAUSING,
+                                            &state,
+                                            DEFAULT_STATE_TIMEOUT));
+    EXPECT_EQ(OBOE_STREAM_STATE_PAUSED, state);
+
+    // Make sure the read counter is not advancing when we are paused.
+    ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+    ASSERT_GE(oboeFramesRead, oboeFramesRead2); // monotonic increase
+
+    // Use this to sleep by waiting for something that won't happen.
+    OboeStream_waitForStateChange(oboeStream, OBOE_STREAM_STATE_PAUSED, &state, timeoutNanos);
+    ASSERT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead2));
+    EXPECT_EQ(oboeFramesRead, oboeFramesRead2);
+
+    // Fill up the buffer.
+    timeoutNanos = 0;
+    loops = 100;
+    do {
+        framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+        framesTotal += framesWritten;
+    } while (framesWritten > 0 && loops-- > 0);
+    EXPECT_EQ(0, framesWritten);
+
+    // Flush and wait for server to respond.
+    ASSERT_EQ(OBOE_OK, OboeStream_requestFlush(oboeStream));
+    EXPECT_EQ(OBOE_OK, OboeStream_waitForStateChange(oboeStream,
+                                                     OBOE_STREAM_STATE_FLUSHING,
+                                                     &state,
+                                                     DEFAULT_STATE_TIMEOUT));
+    EXPECT_EQ(OBOE_STREAM_STATE_FLUSHED, state);
+
+    // After a flush, the read counter should be caught up with the write counter.
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesWritten(oboeStream, &oboeFramesWritten));
+    EXPECT_EQ(framesTotal, oboeFramesWritten);
+    EXPECT_EQ(OBOE_OK, OboeStream_getFramesRead(oboeStream, &oboeFramesRead));
+    EXPECT_EQ(oboeFramesRead, oboeFramesWritten);
+
+    // The buffer should be empty after a flush so we should be able to write.
+    framesWritten = OboeStream_write(oboeStream, data, framesPerBurst, timeoutNanos);
+    // There should be some room for priming the buffer.
+    ASSERT_TRUE(framesWritten > 0 && framesWritten <= framesPerBurst);
+
+    EXPECT_EQ(OBOE_OK, OboeStream_close(oboeStream));
+}