liboboe: initial checkin of core and legacy files.

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

Test: gunit tests for the Legacy mode and handle tracker in tests folder
Bug: 33347409
Change-Id: I50f9fd99377efbd8de6fef1601e9af4c22c6ab46
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/liboboe/src/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.