Merge "add test for AudioTrack constructor"
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index ddc4b16..44ed034 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -80,6 +80,7 @@
 static bool gDisplayHistogram;
 static bool showProgress = true;
 static String8 gWriteMP4Filename;
+static String8 gComponentNameOverride;
 
 static sp<ANativeWindow> gSurface;
 
@@ -193,7 +194,10 @@
             CHECK(!gPreferSoftwareCodec);
             flags |= MediaCodecList::kHardwareCodecsOnly;
         }
-        rawSource = SimpleDecodingSource::Create(source, flags, gSurface);
+        rawSource = SimpleDecodingSource::Create(
+                source, flags, gSurface,
+                gComponentNameOverride.isEmpty() ? nullptr : gComponentNameOverride.c_str(),
+                !gComponentNameOverride.isEmpty());
         if (rawSource == NULL) {
             return;
         }
@@ -618,6 +622,7 @@
     fprintf(stderr, "       -o playback audio\n");
     fprintf(stderr, "       -w(rite) filename (write to .mp4 file)\n");
     fprintf(stderr, "       -k seek test\n");
+    fprintf(stderr, "       -O(verride) name of the component\n");
     fprintf(stderr, "       -x display a histogram of decoding times/fps "
                     "(video only)\n");
     fprintf(stderr, "       -q don't show progress indicator\n");
@@ -703,7 +708,7 @@
     sp<ALooper> looper;
 
     int res;
-    while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kxSTd:D:")) >= 0) {
+    while ((res = getopt(argc, argv, "haqn:lm:b:ptsrow:kO:xSTd:D:")) >= 0) {
         switch (res) {
             case 'a':
             {
@@ -732,6 +737,12 @@
                 break;
             }
 
+            case 'O':
+            {
+                gComponentNameOverride.setTo(optarg);
+                break;
+            }
+
             case 'l':
             {
                 listComponents = true;
@@ -1073,7 +1084,7 @@
                             i, MediaExtractor::kIncludeExtensiveMetaData);
 
                     if (meta == NULL) {
-                        break;
+                        continue;
                     }
                     const char *mime;
                     meta->findCString(kKeyMIMEType, &mime);
diff --git a/media/libaaudio/tests/test_various.cpp b/media/libaaudio/tests/test_various.cpp
index 9e505d5..de386da 100644
--- a/media/libaaudio/tests/test_various.cpp
+++ b/media/libaaudio/tests/test_various.cpp
@@ -41,10 +41,76 @@
 
 // Test AAudioStream_setBufferSizeInFrames()
 
+constexpr int64_t NANOS_PER_MILLISECOND = 1000 * 1000;
+
+//int foo() { // To fix Android Studio formatting when editing.
+TEST(test_various, aaudio_stop_when_open) {
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    AAudioStream *aaudioStream = nullptr;
+
+// Use an AAudioStreamBuilder to contain requested parameters.
+    ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
+
+// Request stream properties.
+    AAudioStreamBuilder_setDataCallback(aaudioBuilder, MyDataCallbackProc, nullptr);
+    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+// Create an AAudioStream using the Builder.
+    EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+
+
+    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                         AAUDIO_STREAM_STATE_UNKNOWN, &state,
+                                                         1000 * NANOS_PER_MILLISECOND));
+    EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state);
+
+    EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStop(aaudioStream));
+
+    state = AAUDIO_STREAM_STATE_UNKNOWN;
+    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                         AAUDIO_STREAM_STATE_UNKNOWN, &state, 0));
+    EXPECT_EQ(AAUDIO_STREAM_STATE_OPEN, state);
+
+    AAudioStream_close(aaudioStream);
+    AAudioStreamBuilder_delete(aaudioBuilder);
+}
+
+//int boo() { // To fix Android Studio formatting when editing.
+TEST(test_various, aaudio_flush_when_started) {
+    AAudioStreamBuilder *aaudioBuilder = nullptr;
+    AAudioStream *aaudioStream = nullptr;
+
+// Use an AAudioStreamBuilder to contain requested parameters.
+    ASSERT_EQ(AAUDIO_OK, AAudio_createStreamBuilder(&aaudioBuilder));
+
+// Request stream properties.
+    AAudioStreamBuilder_setDataCallback(aaudioBuilder, MyDataCallbackProc, nullptr);
+    AAudioStreamBuilder_setPerformanceMode(aaudioBuilder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
+
+// Create an AAudioStream using the Builder.
+    EXPECT_EQ(AAUDIO_OK, AAudioStreamBuilder_openStream(aaudioBuilder, &aaudioStream));
+    EXPECT_EQ(AAUDIO_OK, AAudioStream_requestStart(aaudioStream));
+
+    aaudio_stream_state_t state = AAUDIO_STREAM_STATE_UNKNOWN;
+    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                         AAUDIO_STREAM_STATE_STARTING, &state,
+                                                         1000 * NANOS_PER_MILLISECOND));
+    EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
+
+    EXPECT_EQ(AAUDIO_ERROR_INVALID_STATE, AAudioStream_requestFlush(aaudioStream));
+
+    state = AAUDIO_STREAM_STATE_UNKNOWN;
+    EXPECT_EQ(AAUDIO_OK, AAudioStream_waitForStateChange(aaudioStream,
+                                                         AAUDIO_STREAM_STATE_UNKNOWN, &state, 0));
+    EXPECT_EQ(AAUDIO_STREAM_STATE_STARTED, state);
+
+    AAudioStream_close(aaudioStream);
+    AAudioStreamBuilder_delete(aaudioBuilder);
+}
+
 //int main() { // To fix Android Studio formatting when editing.
 TEST(test_various, aaudio_set_buffer_size) {
-
-    aaudio_result_t result = AAUDIO_OK;
     int32_t bufferCapacity;
     int32_t framesPerBurst = 0;
     int32_t actualSize = 0;
@@ -103,5 +169,4 @@
 
     AAudioStream_close(aaudioStream);
     AAudioStreamBuilder_delete(aaudioBuilder);
-    printf("          result = %d = %s\n", result, AAudio_convertResultToText(result));
 }
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 0c71487..a618676 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -37,6 +37,8 @@
         "AudioPlayer.cpp",
         "AudioSource.cpp",
         "BufferImpl.cpp",
+        "CCodec.cpp",
+        "CCodecBufferChannel.cpp",
         "CodecBase.cpp",
         "CallbackDataSource.cpp",
         "CallbackMediaSource.cpp",
@@ -89,6 +91,7 @@
         "libdl",
         "libdrmframework",
         "libgui",
+        "libion",
         "liblog",
         "libmedia",
         "libmedia_omx",
@@ -100,6 +103,7 @@
         "libui",
         "libutils",
         "libmedia_helper",
+        "libstagefright_codec2",
         "libstagefright_foundation",
         "libstagefright_omx",
         "libstagefright_omx_utils",
@@ -111,6 +115,11 @@
         "android.hidl.allocator@1.0",
         "android.hardware.cas.native@1.0",
         "android.hardware.media.omx@1.0",
+        "android.hardware.graphics.allocator@2.0",
+        "android.hardware.graphics.mapper@2.0",
+
+        // XXX: hack
+        "libstagefright_soft_c2avcdec",
     ],
 
     static_libs: [
@@ -125,6 +134,9 @@
         "libstagefright_esds",
         "libstagefright_id3",
         "libFLAC",
+
+        // XXX: hack
+        "libstagefright_codec2_vndk",
     ],
 
     export_shared_lib_headers: [
diff --git a/media/libstagefright/BufferImpl.cpp b/media/libstagefright/BufferImpl.cpp
index fee3739..9fb6d34 100644
--- a/media/libstagefright/BufferImpl.cpp
+++ b/media/libstagefright/BufferImpl.cpp
@@ -24,11 +24,14 @@
 #include <media/ICrypto.h>
 #include <utils/NativeHandle.h>
 
+#include "include/Codec2Buffer.h"
 #include "include/SecureBuffer.h"
 #include "include/SharedMemoryBuffer.h"
 
 namespace android {
 
+// SharedMemoryBuffer
+
 SharedMemoryBuffer::SharedMemoryBuffer(const sp<AMessage> &format, const sp<IMemory> &mem)
     : MediaCodecBuffer(format, new ABuffer(mem->pointer(), mem->size())),
       mMemory(mem) {
@@ -39,6 +42,8 @@
       mTMemory(mem) {
 }
 
+// SecureBuffer
+
 SecureBuffer::SecureBuffer(const sp<AMessage> &format, const void *ptr, size_t size)
     : MediaCodecBuffer(format, new ABuffer(nullptr, size)),
       mPointer(ptr) {
@@ -59,4 +64,28 @@
     return ICrypto::kDestinationTypeNativeHandle;
 }
 
+// Codec2Buffer
+
+// static
+sp<Codec2Buffer> Codec2Buffer::allocate(
+        const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block) {
+    C2WriteView writeView(block->map().get());
+    if (writeView.error() != C2_OK) {
+        return nullptr;
+    }
+    return new Codec2Buffer(format, new ABuffer(writeView.base(), writeView.capacity()), block);
+}
+
+C2ConstLinearBlock Codec2Buffer::share() {
+    return mBlock->share(offset(), size(), C2Fence());
+}
+
+Codec2Buffer::Codec2Buffer(
+        const sp<AMessage> &format,
+        const sp<ABuffer> &buffer,
+        const std::shared_ptr<C2LinearBlock> &block)
+    : MediaCodecBuffer(format, buffer),
+      mBlock(block) {
+}
+
 }  // namespace android
diff --git a/media/libstagefright/CCodec.cpp b/media/libstagefright/CCodec.cpp
new file mode 100644
index 0000000..080d00f
--- /dev/null
+++ b/media/libstagefright/CCodec.cpp
@@ -0,0 +1,582 @@
+/*
+ * Copyright (C) 2017 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_NDEBUG 0
+#define LOG_TAG "CCodec"
+#include <utils/Log.h>
+
+// XXX: HACK
+#include "codecs/avcdec/C2SoftAvcDec.h"
+
+#include <thread>
+
+#include <gui/Surface.h>
+#include <media/stagefright/CCodec.h>
+
+#include "include/CCodecBufferChannel.h"
+
+using namespace std::chrono_literals;
+
+namespace android {
+
+namespace {
+
+class CCodecWatchdog : public AHandler {
+private:
+    enum {
+        kWhatRegister,
+        kWhatWatch,
+    };
+    constexpr static int64_t kWatchIntervalUs = 3000000;  // 3 secs
+
+public:
+    static sp<CCodecWatchdog> getInstance() {
+        Mutexed<sp<CCodecWatchdog>>::Locked instance(sInstance);
+        if (*instance == nullptr) {
+            *instance = new CCodecWatchdog;
+            (*instance)->init();
+        }
+        return *instance;
+    }
+
+    ~CCodecWatchdog() = default;
+
+    void registerCodec(CCodec *codec) {
+        sp<AMessage> msg = new AMessage(kWhatRegister, this);
+        msg->setPointer("codec", codec);
+        msg->post();
+    }
+
+protected:
+    void onMessageReceived(const sp<AMessage> &msg) {
+        switch (msg->what()) {
+            case kWhatRegister: {
+                void *ptr = nullptr;
+                CHECK(msg->findPointer("codec", &ptr));
+                Mutexed<std::list<wp<CCodec>>>::Locked codecs(mCodecs);
+                codecs->emplace_back((CCodec *)ptr);
+                break;
+            }
+
+            case kWhatWatch: {
+                Mutexed<std::list<wp<CCodec>>>::Locked codecs(mCodecs);
+                for (auto it = codecs->begin(); it != codecs->end(); ) {
+                    sp<CCodec> codec = it->promote();
+                    if (codec == nullptr) {
+                        it = codecs->erase(it);
+                        continue;
+                    }
+                    codec->initiateReleaseIfStuck();
+                    ++it;
+                }
+                msg->post(kWatchIntervalUs);
+                break;
+            }
+
+            default: {
+                TRESPASS("CCodecWatchdog: unrecognized message");
+            }
+        }
+    }
+
+private:
+    CCodecWatchdog() : mLooper(new ALooper) {}
+
+    void init() {
+        mLooper->setName("CCodecWatchdog");
+        mLooper->registerHandler(this);
+        mLooper->start();
+        (new AMessage(kWhatWatch, this))->post(kWatchIntervalUs);
+    }
+
+    static Mutexed<sp<CCodecWatchdog>> sInstance;
+
+    sp<ALooper> mLooper;
+    Mutexed<std::list<wp<CCodec>>> mCodecs;
+};
+
+Mutexed<sp<CCodecWatchdog>> CCodecWatchdog::sInstance;
+
+class CCodecListener : public C2Component::Listener {
+public:
+    CCodecListener(const std::shared_ptr<CCodecBufferChannel> &channel)
+        : mChannel(channel) {
+    }
+
+    virtual void onWorkDone_nb(
+            std::weak_ptr<C2Component> component,
+            std::vector<std::unique_ptr<C2Work>> workItems) override {
+        (void) component;
+        mChannel->onWorkDone(std::move(workItems));
+    }
+
+    virtual void onTripped_nb(
+            std::weak_ptr<C2Component> component,
+            std::vector<std::shared_ptr<C2SettingResult>> settingResult) override {
+        // TODO
+        (void) component;
+        (void) settingResult;
+    }
+
+    virtual void onError_nb(std::weak_ptr<C2Component> component, uint32_t errorCode) override {
+        // TODO
+        (void) component;
+        (void) errorCode;
+    }
+
+private:
+    std::shared_ptr<CCodecBufferChannel> mChannel;
+};
+
+}  // namespace
+
+CCodec::CCodec()
+    : mChannel(new CCodecBufferChannel([this] (status_t err, enum ActionCode actionCode) {
+          mCallback->onError(err, actionCode);
+      })) {
+    CCodecWatchdog::getInstance()->registerCodec(this);
+}
+
+CCodec::~CCodec() {
+}
+
+std::shared_ptr<BufferChannelBase> CCodec::getBufferChannel() {
+    return mChannel;
+}
+
+void CCodec::initiateAllocateComponent(const sp<AMessage> &msg) {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != RELEASED) {
+            mCallback->onError(INVALID_OPERATION, ACTION_CODE_FATAL);
+            return;
+        }
+        state->mState = ALLOCATING;
+    }
+
+    AString componentName;
+    if (!msg->findString("componentName", &componentName)) {
+        // TODO: find componentName appropriate with the media type
+    }
+
+    sp<AMessage> allocMsg(new AMessage(kWhatAllocate, this));
+    allocMsg->setString("componentName", componentName);
+    allocMsg->post();
+}
+
+void CCodec::allocate(const AString &componentName) {
+    // TODO: use C2ComponentStore to create component
+    mListener.reset(new CCodecListener(mChannel));
+
+    std::shared_ptr<C2Component> comp(new C2SoftAvcDec(componentName.c_str(), 0));
+    comp->setListener_sm(mListener);
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != ALLOCATING) {
+            state->mState = RELEASED;
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        state->mState = ALLOCATED;
+        state->mComp = comp;
+    }
+    mChannel->setComponent(comp);
+    mCallback->onComponentAllocated(comp->intf()->getName().c_str());
+}
+
+void CCodec::initiateConfigureComponent(const sp<AMessage> &format) {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != ALLOCATED) {
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            return;
+        }
+    }
+
+    sp<AMessage> msg(new AMessage(kWhatConfigure, this));
+    msg->setMessage("format", format);
+    msg->post();
+}
+
+void CCodec::configure(const sp<AMessage> &msg) {
+    sp<AMessage> inputFormat(new AMessage);
+    sp<AMessage> outputFormat(new AMessage);
+    if (status_t err = [=] {
+        AString mime;
+        if (!msg->findString("mime", &mime)) {
+            return BAD_VALUE;
+        }
+
+        int32_t encoder;
+        if (!msg->findInt32("encoder", &encoder)) {
+            encoder = false;
+        }
+
+        sp<RefBase> obj;
+        if (msg->findObject("native-window", &obj)) {
+            sp<Surface> surface = static_cast<Surface *>(obj.get());
+            setSurface(surface);
+        }
+
+        // TODO
+
+        return OK;
+    }() != OK) {
+        mCallback->onError(err, ACTION_CODE_FATAL);
+        return;
+    }
+
+    {
+        Mutexed<Formats>::Locked formats(mFormats);
+        formats->mInputFormat = inputFormat;
+        formats->mOutputFormat = outputFormat;
+    }
+    mCallback->onComponentConfigured(inputFormat, outputFormat);
+}
+
+
+void CCodec::initiateCreateInputSurface() {
+    // TODO
+}
+
+void CCodec::initiateSetInputSurface(const sp<PersistentSurface> &surface) {
+    // TODO
+    (void) surface;
+}
+
+void CCodec::initiateStart() {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != ALLOCATED) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        state->mState = STARTING;
+    }
+
+    (new AMessage(kWhatStart, this))->post();
+}
+
+void CCodec::start() {
+    std::shared_ptr<C2Component> comp;
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != STARTING) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        comp = state->mComp;
+    }
+    c2_status_t err = comp->start();
+    if (err != C2_OK) {
+        // TODO: convert err into status_t
+        mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+        return;
+    }
+    sp<AMessage> inputFormat;
+    sp<AMessage> outputFormat;
+    {
+        Mutexed<Formats>::Locked formats(mFormats);
+        inputFormat = formats->mInputFormat;
+        outputFormat = formats->mOutputFormat;
+    }
+    mChannel->start(inputFormat, outputFormat);
+
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != STARTING) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        state->mState = RUNNING;
+    }
+    mCallback->onStartCompleted();
+}
+
+void CCodec::initiateShutdown(bool keepComponentAllocated) {
+    if (keepComponentAllocated) {
+        initiateStop();
+    } else {
+        initiateRelease();
+    }
+}
+
+void CCodec::initiateStop() {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState == ALLOCATED
+                || state->mState  == RELEASED
+                || state->mState == STOPPING
+                || state->mState == RELEASING) {
+            // We're already stopped, released, or doing it right now.
+            state.unlock();
+            mCallback->onStopCompleted();
+            state.lock();
+            return;
+        }
+        state->mState = STOPPING;
+    }
+
+    (new AMessage(kWhatStop, this))->post();
+}
+
+void CCodec::stop() {
+    std::shared_ptr<C2Component> comp;
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState == RELEASING) {
+            state.unlock();
+            // We're already stopped or release is in progress.
+            mCallback->onStopCompleted();
+            state.lock();
+            return;
+        } else if (state->mState != STOPPING) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        comp = state->mComp;
+    }
+    mChannel->stop();
+    status_t err = comp->stop();
+    if (err != C2_OK) {
+        // TODO: convert err into status_t
+        mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+    }
+
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState == STOPPING) {
+            state->mState = ALLOCATED;
+        }
+    }
+    mCallback->onStopCompleted();
+}
+
+void CCodec::initiateRelease(bool sendCallback /* = true */) {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState == RELEASED || state->mState == RELEASING) {
+            // We're already released or doing it right now.
+            if (sendCallback) {
+                state.unlock();
+                mCallback->onReleaseCompleted();
+                state.lock();
+            }
+            return;
+        }
+        if (state->mState == ALLOCATING) {
+            state->mState = RELEASING;
+            // With the altered state allocate() would fail and clean up.
+            if (sendCallback) {
+                state.unlock();
+                mCallback->onReleaseCompleted();
+                state.lock();
+            }
+            return;
+        }
+        state->mState = RELEASING;
+    }
+
+    std::thread([this, sendCallback] { release(sendCallback); }).detach();
+}
+
+void CCodec::release(bool sendCallback) {
+    std::shared_ptr<C2Component> comp;
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState == RELEASED) {
+            if (sendCallback) {
+                state.unlock();
+                mCallback->onReleaseCompleted();
+                state.lock();
+            }
+            return;
+        }
+        comp = state->mComp;
+    }
+    mChannel->stop();
+    comp->release();
+
+    {
+        Mutexed<State>::Locked state(mState);
+        state->mState = RELEASED;
+        state->mComp.reset();
+    }
+    if (sendCallback) {
+        mCallback->onReleaseCompleted();
+    }
+}
+
+status_t CCodec::setSurface(const sp<Surface> &surface) {
+    return mChannel->setSurface(surface);
+}
+
+void CCodec::signalFlush() {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != RUNNING) {
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            return;
+        }
+        state->mState = FLUSHING;
+    }
+
+    (new AMessage(kWhatFlush, this))->post();
+}
+
+void CCodec::flush() {
+    std::shared_ptr<C2Component> comp;
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != FLUSHING) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        comp = state->mComp;
+    }
+
+    mChannel->stop();
+
+    std::list<std::unique_ptr<C2Work>> flushedWork;
+    c2_status_t err = comp->flush_sm(C2Component::FLUSH_COMPONENT, &flushedWork);
+    if (err != C2_OK) {
+        // TODO: convert err into status_t
+        mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+    }
+
+    mChannel->flush(flushedWork);
+
+    {
+        Mutexed<State>::Locked state(mState);
+        state->mState = FLUSHED;
+    }
+    mCallback->onFlushCompleted();
+}
+
+void CCodec::signalResume() {
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != FLUSHED) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        state->mState = RESUMING;
+    }
+
+    mChannel->start(nullptr, nullptr);
+
+    {
+        Mutexed<State>::Locked state(mState);
+        if (state->mState != RESUMING) {
+            state.unlock();
+            mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            state.lock();
+            return;
+        }
+        state->mState = RUNNING;
+    }
+}
+
+void CCodec::signalSetParameters(const sp<AMessage> &msg) {
+    // TODO
+    (void) msg;
+}
+
+void CCodec::signalEndOfInputStream() {
+}
+
+void CCodec::signalRequestIDRFrame() {
+    // TODO
+}
+
+void CCodec::onMessageReceived(const sp<AMessage> &msg) {
+    TimePoint now = std::chrono::steady_clock::now();
+    switch (msg->what()) {
+        case kWhatAllocate: {
+            // C2ComponentStore::createComponent() should return within 100ms.
+            setDeadline(now + 150ms);
+            AString componentName;
+            CHECK(msg->findString("componentName", &componentName));
+            allocate(componentName);
+            break;
+        }
+        case kWhatConfigure: {
+            // C2Component::commit_sm() should return within 5ms.
+            setDeadline(now + 50ms);
+            sp<AMessage> format;
+            CHECK(msg->findMessage("format", &format));
+            configure(format);
+            break;
+        }
+        case kWhatStart: {
+            // C2Component::start() should return within 500ms.
+            setDeadline(now + 550ms);
+            start();
+            break;
+        }
+        case kWhatStop: {
+            // C2Component::stop() should return within 500ms.
+            setDeadline(now + 550ms);
+            stop();
+            break;
+        }
+        case kWhatFlush: {
+            // C2Component::flush_sm() should return within 5ms.
+            setDeadline(now + 50ms);
+            flush();
+            break;
+        }
+        default: {
+            ALOGE("unrecognized message");
+            break;
+        }
+    }
+    setDeadline(TimePoint::max());
+}
+
+void CCodec::setDeadline(const TimePoint &newDeadline) {
+    Mutexed<TimePoint>::Locked deadline(mDeadline);
+    *deadline = newDeadline;
+}
+
+void CCodec::initiateReleaseIfStuck() {
+    {
+        Mutexed<TimePoint>::Locked deadline(mDeadline);
+        if (*deadline >= std::chrono::steady_clock::now()) {
+            // We're not stuck.
+            return;
+        }
+    }
+
+    mCallback->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+    initiateRelease();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/CCodecBufferChannel.cpp b/media/libstagefright/CCodecBufferChannel.cpp
new file mode 100644
index 0000000..9868cd4
--- /dev/null
+++ b/media/libstagefright/CCodecBufferChannel.cpp
@@ -0,0 +1,589 @@
+/*
+ * 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_NDEBUG 0
+#define LOG_TAG "CCodecBufferChannel"
+#include <utils/Log.h>
+
+#include <numeric>
+#include <thread>
+
+#include <C2PlatformSupport.h>
+
+#include <android/hardware/cas/native/1.0/IDescrambler.h>
+#include <binder/MemoryDealer.h>
+#include <gui/Surface.h>
+#include <media/openmax/OMX_Core.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AUtils.h>
+#include <media/stagefright/MediaCodec.h>
+#include <media/MediaCodecBuffer.h>
+#include <system/window.h>
+
+#include "include/CCodecBufferChannel.h"
+#include "include/Codec2Buffer.h"
+#include "include/SecureBuffer.h"
+#include "include/SharedMemoryBuffer.h"
+
+namespace android {
+
+using hardware::hidl_handle;
+using hardware::hidl_string;
+using hardware::hidl_vec;
+using namespace hardware::cas::V1_0;
+using namespace hardware::cas::native::V1_0;
+
+// TODO: get this info from component
+const static size_t kMinBufferArraySize = 16;
+
+void CCodecBufferChannel::OutputBuffers::flush(
+        const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    (void) flushedWork;
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+}
+
+namespace {
+
+template <class T>
+ssize_t findBufferSlot(
+        std::vector<T> *buffers,
+        size_t maxSize,
+        std::function<bool(const T&)> pred) {
+    auto it = std::find_if(buffers->begin(), buffers->end(), pred);
+    if (it == buffers->end()) {
+        if (buffers->size() < maxSize) {
+            buffers->emplace_back();
+            return buffers->size() - 1;
+        } else {
+            return -1;
+        }
+    }
+    return std::distance(buffers->begin(), it);
+}
+
+class LinearBuffer : public C2Buffer {
+public:
+    explicit LinearBuffer(C2ConstLinearBlock block) : C2Buffer({ block }) {}
+};
+
+class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
+public:
+    using CCodecBufferChannel::InputBuffers::InputBuffers;
+
+    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
+        *buffer = nullptr;
+        ssize_t ret = findBufferSlot<wp<Codec2Buffer>>(
+                &mBuffers, kMinBufferArraySize,
+                [] (const auto &elem) { return elem.promote() == nullptr; });
+        if (ret < 0) {
+            return false;
+        }
+        std::shared_ptr<C2LinearBlock> block;
+
+        status_t err = mAlloc->fetchLinearBlock(
+                // TODO: proper max input size
+                65536,
+                { 0, C2MemoryUsage::kSoftwareWrite },
+                &block);
+        if (err != OK) {
+            return false;
+        }
+
+        sp<Codec2Buffer> newBuffer = Codec2Buffer::allocate(mFormat, block);
+        mBuffers[ret] = newBuffer;
+        *index = ret;
+        *buffer = newBuffer;
+        return true;
+    }
+
+    virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+        auto it = std::find(mBuffers.begin(), mBuffers.end(), buffer);
+        if (it == mBuffers.end()) {
+            return nullptr;
+        }
+        sp<Codec2Buffer> codecBuffer = it->promote();
+        // We got sp<> reference from the caller so this should never happen..
+        CHECK(codecBuffer != nullptr);
+        return std::make_shared<LinearBuffer>(codecBuffer->share());
+    }
+
+    virtual void flush() override {
+    }
+
+private:
+    // Buffers we passed to the client. The index of a buffer matches what
+    // was passed in BufferCallback::onInputBufferAvailable().
+    std::vector<wp<Codec2Buffer>> mBuffers;
+
+    // Buffer array we passed to the client. This only gets initialized at
+    // getInput/OutputBufferArray() and when this is set we can't add more
+    // buffers.
+    std::vector<sp<Codec2Buffer>> mBufferArray;
+};
+
+class GraphicOutputBuffers : public CCodecBufferChannel::OutputBuffers {
+public:
+    using CCodecBufferChannel::OutputBuffers::OutputBuffers;
+
+    virtual bool registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *codecBuffer) override {
+        *codecBuffer = nullptr;
+        ssize_t ret = findBufferSlot<BufferInfo>(
+                &mBuffers,
+                kMinBufferArraySize,
+                [] (const auto &elem) { return elem.mClientBuffer.promote() == nullptr; });
+        if (ret < 0) {
+            return false;
+        }
+        sp<MediaCodecBuffer> newBuffer = new MediaCodecBuffer(
+                mFormat,
+                buffer == nullptr ? kEmptyBuffer : kDummyBuffer);
+        mBuffers[ret] = { newBuffer, buffer };
+        *index = ret;
+        *codecBuffer = newBuffer;
+        return true;
+    }
+
+    virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) override {
+        auto it = std::find_if(
+                mBuffers.begin(), mBuffers.end(),
+                [buffer] (const auto &elem) {
+                    return elem.mClientBuffer.promote() == buffer;
+                });
+        if (it == mBuffers.end()) {
+            return nullptr;
+        }
+        return it->mBufferRef;
+    }
+
+private:
+    static const sp<ABuffer> kEmptyBuffer;
+    static const sp<ABuffer> kDummyBuffer;
+
+    struct BufferInfo {
+        // wp<> of MediaCodecBuffer for MediaCodec.
+        wp<MediaCodecBuffer> mClientBuffer;
+        // Buffer reference to hold until mClientBuffer is valid.
+        std::shared_ptr<C2Buffer> mBufferRef;
+    };
+    // Buffers we passed to the client. The index of a buffer matches what
+    // was passed in BufferCallback::onInputBufferAvailable().
+    std::vector<BufferInfo> mBuffers;
+};
+
+const sp<ABuffer> GraphicOutputBuffers::kEmptyBuffer = new ABuffer(nullptr, 0);
+const sp<ABuffer> GraphicOutputBuffers::kDummyBuffer = new ABuffer(nullptr, 1);
+
+}  // namespace
+
+CCodecBufferChannel::QueueGuard::QueueGuard(
+        CCodecBufferChannel::QueueSync &sync) : mSync(sync) {
+    std::unique_lock<std::mutex> l(mSync.mMutex);
+    if (mSync.mCount == -1) {
+        mRunning = false;
+    } else {
+        ++mSync.mCount;
+        mRunning = true;
+    }
+}
+
+CCodecBufferChannel::QueueGuard::~QueueGuard() {
+    if (mRunning) {
+        --mSync.mCount;
+    }
+}
+
+void CCodecBufferChannel::QueueSync::start() {
+    std::unique_lock<std::mutex> l(mMutex);
+    // If stopped, it goes to running state; otherwise no-op.
+    int32_t expected = -1;
+    mCount.compare_exchange_strong(expected, 0);
+}
+
+void CCodecBufferChannel::QueueSync::stop() {
+    std::unique_lock<std::mutex> l(mMutex);
+    if (mCount == -1) {
+        // no-op
+        return;
+    }
+    int32_t expected = 0;
+    while (!mCount.compare_exchange_weak(expected, -1)) {
+        std::this_thread::yield();
+    }
+}
+
+CCodecBufferChannel::CCodecBufferChannel(
+        const std::function<void(status_t, enum ActionCode)> &onError)
+    : mOnError(onError),
+      mInputBuffers(new LinearInputBuffers),
+      mOutputBuffers(new GraphicOutputBuffers),
+      mFrameIndex(0u),
+      mFirstValidFrameIndex(0u) {
+}
+
+CCodecBufferChannel::~CCodecBufferChannel() {
+    if (mCrypto != nullptr && mDealer != nullptr && mHeapSeqNum >= 0) {
+        mCrypto->unsetHeap(mHeapSeqNum);
+    }
+}
+
+void CCodecBufferChannel::setComponent(const std::shared_ptr<C2Component> &component) {
+    mComponent = component;
+    // TODO: get pool ID from params
+    std::shared_ptr<C2BlockPool> pool;
+    c2_status_t err = GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, component, &pool);
+    if (err == C2_OK) {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        (*buffers)->setAlloc(pool);
+    }
+}
+
+status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
+    QueueGuard guard(mSync);
+    if (!guard.isRunning()) {
+        ALOGW("No more buffers should be queued at current state.");
+        return -ENOSYS;
+    }
+
+    int64_t timeUs;
+    CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+    int32_t flags = 0;
+    int32_t tmp = 0;
+    if (buffer->meta()->findInt32("eos", &tmp) && tmp) {
+        flags |= C2BufferPack::FLAG_END_OF_STREAM;
+        ALOGV("input EOS");
+    }
+    if (buffer->meta()->findInt32("csd", &tmp) && tmp) {
+        flags |= C2BufferPack::FLAG_CODEC_CONFIG;
+    }
+    std::unique_ptr<C2Work> work(new C2Work);
+    work->input.flags = (C2BufferPack::flags_t)flags;
+    work->input.ordinal.timestamp = timeUs;
+    work->input.ordinal.frame_index = mFrameIndex++;
+    work->input.buffers.clear();
+    {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        work->input.buffers.push_back((*buffers)->releaseBuffer(buffer));
+    }
+    // TODO: fill info's
+
+    work->worklets.clear();
+    work->worklets.emplace_back(new C2Worklet);
+
+    std::list<std::unique_ptr<C2Work>> items;
+    items.push_back(std::move(work));
+    return mComponent->queue_nb(&items);
+}
+
+status_t CCodecBufferChannel::queueSecureInputBuffer(
+        const sp<MediaCodecBuffer> &buffer, bool secure, const uint8_t *key,
+        const uint8_t *iv, CryptoPlugin::Mode mode, CryptoPlugin::Pattern pattern,
+        const CryptoPlugin::SubSample *subSamples, size_t numSubSamples,
+        AString *errorDetailMsg) {
+    // TODO
+    (void) buffer;
+    (void) secure;
+    (void) key;
+    (void) iv;
+    (void) mode;
+    (void) pattern;
+    (void) subSamples;
+    (void) numSubSamples;
+    (void) errorDetailMsg;
+    return -ENOSYS;
+}
+
+status_t CCodecBufferChannel::renderOutputBuffer(
+        const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) {
+    ALOGV("renderOutputBuffer");
+    sp<MediaCodecBuffer> inBuffer;
+    size_t index;
+    {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        if (!(*buffers)->requestNewBuffer(&index, &inBuffer)) {
+            inBuffer = nullptr;
+        }
+    }
+    if (inBuffer != nullptr) {
+        mCallback->onInputBufferAvailable(index, inBuffer);
+    }
+
+    std::shared_ptr<C2Buffer> c2Buffer;
+    {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        c2Buffer = (*buffers)->releaseBuffer(buffer);
+    }
+
+    Mutexed<sp<Surface>>::Locked surface(mSurface);
+    if (*surface == nullptr) {
+        ALOGE("no surface");
+        return OK;
+    }
+
+    std::list<C2ConstGraphicBlock> blocks = c2Buffer->data().graphicBlocks();
+    if (blocks.size() != 1u) {
+        ALOGE("# of graphic blocks expected to be 1, but %zu", blocks.size());
+        return UNKNOWN_ERROR;
+    }
+
+    sp<GraphicBuffer> graphicBuffer(new GraphicBuffer(
+            blocks.front().handle(),
+            GraphicBuffer::CLONE_HANDLE,
+            blocks.front().width(),
+            blocks.front().height(),
+            HAL_PIXEL_FORMAT_YV12,
+            // TODO
+            1,
+            (uint64_t)GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN,
+            // TODO
+            blocks.front().width()));
+
+    status_t result = (*surface)->attachBuffer(graphicBuffer.get());
+    if (result != OK) {
+        ALOGE("attachBuffer failed: %d", result);
+        return result;
+    }
+
+    // TODO: read and set crop
+
+    result = native_window_set_buffers_timestamp((*surface).get(), timestampNs);
+    ALOGW_IF(result != OK, "failed to set buffer timestamp: %d", result);
+
+    // TODO: fix after C2Fence implementation
+#if 0
+    const C2Fence &fence = blocks.front().fence();
+    result = ((ANativeWindow *)(*surface).get())->queueBuffer(
+            (*surface).get(), graphicBuffer.get(), fence.valid() ? fence.fd() : -1);
+#else
+    result = ((ANativeWindow *)(*surface).get())->queueBuffer(
+            (*surface).get(), graphicBuffer.get(), -1);
+#endif
+    if (result != OK) {
+        ALOGE("queueBuffer failed: %d", result);
+        return result;
+    }
+
+    return OK;
+}
+
+status_t CCodecBufferChannel::discardBuffer(const sp<MediaCodecBuffer> &buffer) {
+    ALOGV("discardBuffer");
+    {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        (void) (*buffers)->releaseBuffer(buffer);
+    }
+    {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        (void) (*buffers)->releaseBuffer(buffer);
+    }
+    return OK;
+}
+
+#if 0
+void fillBufferArray_l(Mutexed<Buffers>::Locked &buffers) {
+    for (size_t i = 0; i < buffers->mClientBuffer.size(); ++i) {
+        sp<Codec2Buffer> buffer(buffers->mClientBuffer.get(i).promote());
+        if (buffer == nullptr) {
+            buffer = allocateBuffer_l(buffers->mAlloc);
+        }
+        buffers->mBufferArray.push_back(buffer);
+    }
+    while (buffers->mBufferArray.size() < kMinBufferArraySize) {
+        sp<Codec2Buffer> buffer = allocateBuffer_l(buffers->mAlloc);
+        // allocate buffer
+        buffers->mBufferArray.push_back(buffer);
+    }
+}
+#endif
+
+void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
+    (void) array;
+    // TODO
+#if 0
+    array->clear();
+    Mutexed<Buffers>::Locked buffers(mInputBuffers);
+
+    if (!buffers->isArrayMode()) {
+        // mBufferArray is empty.
+        fillBufferArray_l(buffers);
+    }
+
+    for (const auto &buffer : buffers->mBufferArray) {
+        array->push_back(buffer);
+    }
+#endif
+}
+
+void CCodecBufferChannel::getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
+    (void) array;
+    // TODO
+#if 0
+    array->clear();
+    Mutexed<Buffers>::Locked buffers(mOutputBuffers);
+
+    if (!buffers->isArrayMode()) {
+        if (linear) {
+            // mBufferArray is empty.
+            fillBufferArray_l(buffers);
+
+            // We need to replace the allocator so that the component only returns
+            // buffer from the array.
+            ArrayModeAllocator::Builder builder(buffers->mBufferArray);
+            for (size_t i = 0; i < buffers->mClientBuffer.size(); ++i) {
+                if (buffers->mClientBuffer.get(i).promote() != nullptr) {
+                    builder.markUsing(i);
+                }
+            }
+            buffers->mAlloc.reset(builder.build());
+        } else {
+            for (int i = 0; i < X; ++i) {
+                buffers->mBufferArray.push_back(dummy buffer);
+            }
+        }
+    }
+
+    for (const auto &buffer : buffers->mBufferArray) {
+        array->push_back(buffer);
+    }
+#endif
+}
+
+void CCodecBufferChannel::start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat) {
+    if (inputFormat != nullptr) {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        (*buffers)->setFormat(inputFormat);
+    }
+    if (outputFormat != nullptr) {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        (*buffers)->setFormat(outputFormat);
+    }
+
+    mSync.start();
+    // TODO: use proper buffer depth instead of this random value
+    for (size_t i = 0; i < kMinBufferArraySize; ++i) {
+        size_t index;
+        sp<MediaCodecBuffer> buffer;
+        {
+            Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+            if (!(*buffers)->requestNewBuffer(&index, &buffer)) {
+                buffers.unlock();
+                ALOGE("start: cannot allocate memory");
+                mOnError(NO_MEMORY, ACTION_CODE_FATAL);
+                buffers.lock();
+                return;
+            }
+        }
+        mCallback->onInputBufferAvailable(index, buffer);
+    }
+}
+
+void CCodecBufferChannel::stop() {
+    mSync.stop();
+    mFirstValidFrameIndex = mFrameIndex.load();
+}
+
+void CCodecBufferChannel::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    {
+        Mutexed<std::unique_ptr<InputBuffers>>::Locked buffers(mInputBuffers);
+        (*buffers)->flush();
+    }
+    {
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        (*buffers)->flush(flushedWork);
+    }
+}
+
+void CCodecBufferChannel::onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems) {
+    for (const auto &work : workItems) {
+        if (work->result != OK) {
+            ALOGE("work failed to complete: %d", work->result);
+            mOnError(work->result, ACTION_CODE_FATAL);
+            return;
+        }
+
+        // NOTE: MediaCodec usage supposedly have only one worklet
+        if (work->worklets.size() != 1u) {
+            ALOGE("incorrect number of worklets: %zu", work->worklets.size());
+            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            continue;
+        }
+
+        const std::unique_ptr<C2Worklet> &worklet = work->worklets.front();
+        if (worklet->output.ordinal.frame_index < mFirstValidFrameIndex) {
+            // Discard frames from previous generation.
+            continue;
+        }
+        // NOTE: MediaCodec usage supposedly have only one output stream.
+        if (worklet->output.buffers.size() != 1u) {
+            ALOGE("incorrect number of output buffers: %zu", worklet->output.buffers.size());
+            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            continue;
+        }
+
+        const std::shared_ptr<C2Buffer> &buffer = worklet->output.buffers[0];
+        // TODO: transfer infos() into buffer metadata
+
+        int32_t flags = 0;
+        if (worklet->output.flags & C2BufferPack::FLAG_END_OF_STREAM) {
+            flags |= MediaCodec::BUFFER_FLAG_EOS;
+            ALOGV("output EOS");
+        }
+
+        size_t index;
+        sp<MediaCodecBuffer> outBuffer;
+        Mutexed<std::unique_ptr<OutputBuffers>>::Locked buffers(mOutputBuffers);
+        if (!(*buffers)->registerBuffer(buffer, &index, &outBuffer)) {
+            ALOGE("unable to register output buffer");
+            mOnError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
+            continue;
+        }
+
+        outBuffer->meta()->setInt64("timeUs", worklet->output.ordinal.timestamp);
+        outBuffer->meta()->setInt32("flags", flags);
+        ALOGV("index = %zu", index);
+        mCallback->onOutputBufferAvailable(index, outBuffer);
+    }
+}
+
+status_t CCodecBufferChannel::setSurface(const sp<Surface> &newSurface) {
+    if (newSurface != nullptr) {
+        newSurface->setScalingMode(NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW);
+    }
+
+    Mutexed<sp<Surface>>::Locked surface(mSurface);
+//    if (newSurface == nullptr) {
+//        if (*surface != nullptr) {
+//            ALOGW("cannot unset a surface");
+//            return INVALID_OPERATION;
+//        }
+//        return OK;
+//    }
+//
+//    if (*surface == nullptr) {
+//        ALOGW("component was not configured with a surface");
+//        return INVALID_OPERATION;
+//    }
+
+    *surface = newSurface;
+    return OK;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 4fedab6..677d25a 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -28,6 +28,7 @@
 #include <binder/IPCThreadState.h>
 #include <binder/IServiceManager.h>
 #include <binder/MemoryDealer.h>
+#include <cutils/properties.h>
 #include <gui/BufferQueue.h>
 #include <gui/Surface.h>
 #include <media/ICrypto.h>
@@ -44,6 +45,7 @@
 #include <media/stagefright/foundation/hexdump.h>
 #include <media/stagefright/ACodec.h>
 #include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/CCodec.h>
 #include <media/stagefright/MediaCodec.h>
 #include <media/stagefright/MediaCodecList.h>
 #include <media/stagefright/MediaDefs.h>
@@ -549,8 +551,11 @@
 
 //static
 sp<CodecBase> MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
-    // at this time only ACodec specifies a mime type.
-    if (nameIsType || name.startsWithIgnoreCase("omx.")) {
+    static bool ccodecEnabled = property_get_bool("debug.stagefright.ccodec", false);
+    if (ccodecEnabled && !nameIsType && name.startsWithIgnoreCase("codec2.")) {
+        return new CCodec;
+    } else if (nameIsType || name.startsWithIgnoreCase("omx.")) {
+        // at this time only ACodec specifies a mime type.
         return new ACodec;
     } else if (name.startsWithIgnoreCase("android.filter.")) {
         return new MediaFilter;
@@ -1849,7 +1854,6 @@
                                 }
                             }
                         }
-
                         if (mFlags & kFlagIsAsync) {
                             onOutputFormatChanged();
                         } else {
diff --git a/media/libstagefright/SimpleDecodingSource.cpp b/media/libstagefright/SimpleDecodingSource.cpp
index 67e6748..9b2fb4f 100644
--- a/media/libstagefright/SimpleDecodingSource.cpp
+++ b/media/libstagefright/SimpleDecodingSource.cpp
@@ -14,6 +14,10 @@
  * limitations under the License.
  */
 
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SimpleDecodingSource"
+#include <utils/Log.h>
+
 #include <gui/Surface.h>
 
 #include <media/ICrypto.h>
@@ -43,7 +47,7 @@
 //static
 sp<SimpleDecodingSource> SimpleDecodingSource::Create(
         const sp<MediaSource> &source, uint32_t flags, const sp<ANativeWindow> &nativeWindow,
-        const char *desiredCodec) {
+        const char *desiredCodec, bool skipMediaCodecList) {
     sp<Surface> surface = static_cast<Surface*>(nativeWindow.get());
     const char *mime = NULL;
     sp<MetaData> meta = source->getFormat();
@@ -63,6 +67,33 @@
     looper->start();
 
     sp<MediaCodec> codec;
+    auto configure = [=](const sp<MediaCodec> &codec, const AString &componentName)
+            -> sp<SimpleDecodingSource> {
+        if (codec != NULL) {
+            ALOGI("Successfully allocated codec '%s'", componentName.c_str());
+
+            status_t err = codec->configure(format, surface, NULL /* crypto */, 0 /* flags */);
+            sp<AMessage> outFormat;
+            if (err == OK) {
+                err = codec->getOutputFormat(&outFormat);
+            }
+            if (err == OK) {
+                return new SimpleDecodingSource(codec, source, looper,
+                        surface != NULL,
+                        strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
+                        outFormat);
+            }
+
+            ALOGD("Failed to configure codec '%s'", componentName.c_str());
+            codec->release();
+        }
+        return NULL;
+    };
+
+    if (skipMediaCodecList) {
+        codec = MediaCodec::CreateByComponentName(looper, desiredCodec);
+        return configure(codec, desiredCodec);
+    }
 
     for (size_t i = 0; i < matchingCodecs.size(); ++i) {
         const AString &componentName = matchingCodecs[i];
@@ -73,22 +104,10 @@
         ALOGV("Attempting to allocate codec '%s'", componentName.c_str());
 
         codec = MediaCodec::CreateByComponentName(looper, componentName);
-        if (codec != NULL) {
-            ALOGI("Successfully allocated codec '%s'", componentName.c_str());
-
-            status_t err = codec->configure(format, surface, NULL /* crypto */, 0 /* flags */);
-            if (err == OK) {
-                err = codec->getOutputFormat(&format);
-            }
-            if (err == OK) {
-                return new SimpleDecodingSource(codec, source, looper,
-                        surface != NULL,
-                        strcmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS) == 0,
-                        format);
-            }
-
-            ALOGD("Failed to configure codec '%s'", componentName.c_str());
-            codec->release();
+        sp<SimpleDecodingSource> res = configure(codec, componentName);
+        if (res != NULL) {
+            return res;
+        } else {
             codec = NULL;
         }
     }
diff --git a/media/libstagefright/codec2/Android.bp b/media/libstagefright/codec2/Android.bp
index 311a20b..2f0deb4 100644
--- a/media/libstagefright/codec2/Android.bp
+++ b/media/libstagefright/codec2/Android.bp
@@ -12,6 +12,10 @@
         "frameworks/native/include/media/hardware",
     ],
 
+    export_include_dirs: [
+        "include",
+    ],
+
     sanitize: {
         misc_undefined: [
             "unsigned-integer-overflow",
diff --git a/media/libstagefright/codec2/vndk/Android.bp b/media/libstagefright/codec2/vndk/Android.bp
index 64ce5e6..d2cfebb 100644
--- a/media/libstagefright/codec2/vndk/Android.bp
+++ b/media/libstagefright/codec2/vndk/Android.bp
@@ -9,6 +9,10 @@
         "C2Store.cpp",
     ],
 
+    export_include_dirs: [
+        "include",
+    ],
+
     include_dirs: [
         "frameworks/av/media/libstagefright/codec2/include",
         "frameworks/av/media/libstagefright/codec2/vndk/include",
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
index c74ca6d..b904d26 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.cpp
@@ -790,6 +790,7 @@
 }
 
 void C2SoftAvcDec::processQueue() {
+#if 0
     if (mIsInFlush) {
         setFlushMode();
 
@@ -825,9 +826,10 @@
         }
         mIsInFlush = false;
     }
+#endif
 
     std::unique_ptr<C2Work> work;
-    {
+    if (!mIsInFlush) {
         std::unique_lock<std::mutex> lock(mQueueLock);
         if (mQueue.empty()) {
             mQueueCond.wait(lock);
@@ -844,7 +846,7 @@
     process(work);
 
     std::vector<std::unique_ptr<C2Work>> done;
-    {
+    if (work) {
         std::unique_lock<std::mutex> lock(mPendingLock);
         uint32_t index = work->input.ordinal.frame_index;
         mPendingWork[index].swap(work);
@@ -871,12 +873,12 @@
 
 
 static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
-    UNUSED(ctxt);
+    (void) ctxt;
     return memalign(alignment, size);
 }
 
 static void ivd_aligned_free(void *ctxt, void *buf) {
-    UNUSED(ctxt);
+    (void) ctxt;
     free(buf);
     return;
 }
@@ -1001,6 +1003,7 @@
 }
 
 status_t C2SoftAvcDec::setFlushMode() {
+    ALOGV("setFlushMode");
     IV_API_CALL_STATUS_T status;
     ivd_ctl_flush_ip_t s_video_flush_ip;
     ivd_ctl_flush_op_t s_video_flush_op;
@@ -1019,7 +1022,7 @@
                 s_video_flush_op.u4_error_code);
         return UNKNOWN_ERROR;
     }
-
+    mIsInFlush = true;
     return OK;
 }
 
@@ -1079,7 +1082,6 @@
 }
 
 status_t C2SoftAvcDec::deInitDecoder() {
-    size_t i;
     IV_API_CALL_STATUS_T status;
 
     if (mCodecCtx) {
@@ -1206,7 +1208,6 @@
     if (mSignalledError) {
         return;
     }
-
     if (NULL == mCodecCtx) {
         if (OK != initDecoder()) {
             ALOGE("Failed to initialize decoder");
@@ -1221,66 +1222,78 @@
         setParams(mStride);
     }
 
-    const C2ConstLinearBlock &buffer =
-            work->input.buffers[0]->data().linearBlocks().front();
-    if (buffer.capacity() == 0) {
-        // TODO: result?
+    uint32_t workIndex = 0;
+    std::unique_ptr<C2ReadView> input;
+    if (work) {
+        work->result = C2_OK;
 
-        std::vector<std::unique_ptr<C2Work>> done;
-        done.emplace_back(std::move(work));
-        mListener->onWorkDone_nb(shared_from_this(), std::move(done));
-        if (!(work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
-            return;
-        }
+        const C2ConstLinearBlock &buffer =
+                work->input.buffers[0]->data().linearBlocks().front();
+        if (buffer.capacity() == 0) {
+            // TODO: result?
 
-        mReceivedEOS = true;
-        // TODO: flush
-    } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
-        mReceivedEOS = true;
-    }
-
-    C2ReadView input = work->input.buffers[0]->data().linearBlocks().front().map().get();
-    uint32_t workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
-
-    // TODO: populate --- assume display order?
-    if (!mAllocatedBlock) {
-        // TODO: error handling
-        // TODO: format & usage
-        uint32_t format = HAL_PIXEL_FORMAT_YV12;
-        C2MemoryUsage usage = { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite };
-        // TODO: lock access to interface
-        C2BlockPool::local_id_t poolId =
-            mIntf->mOutputBlockPools->flexCount() ?
-                    mIntf->mOutputBlockPools->m.mValues[0] : C2BlockPool::BASIC_GRAPHIC;
-        if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
-            c2_status_t err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
-            if (err != C2_OK) {
-                // TODO: trip
+            std::vector<std::unique_ptr<C2Work>> done;
+            done.emplace_back(std::move(work));
+            mListener->onWorkDone_nb(shared_from_this(), std::move(done));
+            if (!(work->input.flags & C2BufferPack::FLAG_END_OF_STREAM)) {
+                return;
             }
-        }
-        ALOGE("using allocator %u", mOutputBlockPool->getAllocatorId());
 
-        (void)mOutputBlockPool->fetchGraphicBlock(
-                mWidth, mHeight, format, usage, &mAllocatedBlock);
-        ALOGE("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
+            mReceivedEOS = true;
+            // TODO: flush
+        } else if (work->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
+            ALOGV("input EOS: %llu", work->input.ordinal.frame_index);
+            mReceivedEOS = true;
+        }
+
+        input.reset(new C2ReadView(work->input.buffers[0]->data().linearBlocks().front().map().get()));
+        workIndex = work->input.ordinal.frame_index & 0xFFFFFFFF;
     }
-    C2GraphicView output = mAllocatedBlock->map().get();
-    ALOGE("mapped err = %d", output.error());
 
     size_t inOffset = 0u;
-    while (inOffset < input.capacity()) {
+    while (!input || inOffset < input->capacity()) {
+        if (!input) {
+            ALOGV("flushing");
+        }
+        // TODO: populate --- assume display order?
+        if (!mAllocatedBlock) {
+            // TODO: error handling
+            // TODO: format & usage
+            uint32_t format = HAL_PIXEL_FORMAT_YV12;
+            C2MemoryUsage usage = { C2MemoryUsage::kSoftwareRead, C2MemoryUsage::kSoftwareWrite };
+            // TODO: lock access to interface
+            C2BlockPool::local_id_t poolId =
+                mIntf->mOutputBlockPools->flexCount() ?
+                        mIntf->mOutputBlockPools->m.mValues[0] : C2BlockPool::BASIC_GRAPHIC;
+            if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
+                c2_status_t err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
+                if (err != C2_OK) {
+                    // TODO: trip
+                }
+            }
+            ALOGE("using allocator %u", mOutputBlockPool->getAllocatorId());
+
+            (void)mOutputBlockPool->fetchGraphicBlock(
+                    mWidth, mHeight, format, usage, &mAllocatedBlock);
+            ALOGE("provided (%dx%d) required (%dx%d)", mAllocatedBlock->width(), mAllocatedBlock->height(), mWidth, mHeight);
+        }
+        C2GraphicView output = mAllocatedBlock->map().get();
+        if (output.error() != OK) {
+            ALOGE("mapped err = %d", output.error());
+        }
+
         ivd_video_decode_ip_t s_dec_ip;
         ivd_video_decode_op_t s_dec_op;
         WORD32 timeDelay, timeTaken;
         size_t sizeY, sizeUV;
 
-        if (!setDecodeArgs(&s_dec_ip, &s_dec_op, &input, &output, workIndex, inOffset)) {
+        if (!setDecodeArgs(&s_dec_ip, &s_dec_op, input.get(), &output, workIndex, inOffset)) {
             ALOGE("Decoder arg setup failed");
             // TODO: notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
             mSignalledError = true;
             return;
         }
-        ALOGE("Decoder arg setup succeeded");
+        ALOGV("Decoder arg setup succeeded");
         // If input dump is enabled, then write to file
         DUMP_TO_FILE(mInFile, s_dec_ip.pv_stream_buffer, s_dec_ip.u4_num_Bytes, mInputOffset);
 
@@ -1321,15 +1334,24 @@
 
         PRINT_TIME("timeTaken=%6d delay=%6d numBytes=%6d", timeTaken, timeDelay,
                s_dec_op.u4_num_bytes_consumed);
-        ALOGI("bytes total=%u", input.capacity());
+        if (input) {
+            ALOGI("bytes total=%u", input->capacity());
+        }
         if (s_dec_op.u4_frame_decoded_flag && !mFlushNeeded) {
             mFlushNeeded = true;
         }
 
-        if (1 != s_dec_op.u4_frame_decoded_flag) {
-            /* If the input did not contain picture data, then ignore
-             * the associated timestamp */
-            //mTimeStampsValid[workIndex] = false;
+        if (1 != s_dec_op.u4_frame_decoded_flag && work) {
+            /* If the input did not contain picture data, return work without
+             * buffer */
+            ALOGV("no picture data");
+            std::vector<std::unique_ptr<C2Work>> done;
+            done.push_back(std::move(work));
+            done[0]->worklets.front()->output.flags = (C2BufferPack::flags_t)0;
+            done[0]->worklets.front()->output.buffers.clear();
+            done[0]->worklets.front()->output.buffers.emplace_back(nullptr);
+            done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
+            mListener->onWorkDone_nb(shared_from_this(), std::move(done));
         }
 
         // If the decoder is in the changing resolution mode and there is no output present,
@@ -1373,10 +1395,19 @@
         }
 
         if (s_dec_op.u4_output_present) {
-            ALOGV("output_present");
-            // TODO: outHeader->nFilledLen = (mWidth * mHeight * 3) / 2;
+            ALOGV("output_present: %d", s_dec_op.u4_ts);
             std::vector<std::unique_ptr<C2Work>> done;
-            done.push_back(std::move(mPendingWork[s_dec_op.u4_ts]));
+            {
+                std::unique_lock<std::mutex> lock(mPendingLock);
+                done.push_back(std::move(mPendingWork[s_dec_op.u4_ts]));
+                mPendingWork.erase(s_dec_op.u4_ts);
+            }
+            uint32_t flags = 0;
+            if (done[0]->input.flags & C2BufferPack::FLAG_END_OF_STREAM) {
+                flags |= C2BufferPack::FLAG_END_OF_STREAM;
+                ALOGV("EOS");
+            }
+            done[0]->worklets.front()->output.flags = (C2BufferPack::flags_t)flags;
             done[0]->worklets.front()->output.buffers.clear();
             done[0]->worklets.front()->output.buffers.emplace_back(
                     std::make_shared<GraphicBuffer>(std::move(mAllocatedBlock)));
@@ -1391,16 +1422,25 @@
             /* If EOS was recieved on input port and there is no output
              * from the codec, then signal EOS on output port */
             if (mReceivedEOS) {
-                // TODO
-                // outHeader->nFilledLen = 0;
-                // outHeader->nFlags |= OMX_BUFFERFLAG_EOS;
+                std::vector<std::unique_ptr<C2Work>> done;
+                {
+                    std::unique_lock<std::mutex> lock(mPendingLock);
+                    if (!mPendingWork.empty()) {
+                        done.push_back(std::move(mPendingWork.begin()->second));
+                        mPendingWork.erase(mPendingWork.begin());
+                    }
+                }
+                if (!done.empty()) {
+                    ALOGV("sending empty EOS buffer");
+                    done[0]->worklets.front()->output.flags = C2BufferPack::FLAG_END_OF_STREAM;
+                    done[0]->worklets.front()->output.buffers.clear();
+                    done[0]->worklets.front()->output.buffers.emplace_back(nullptr);
+                    done[0]->worklets.front()->output.ordinal = done[0]->input.ordinal;
+                    mListener->onWorkDone_nb(shared_from_this(), std::move(done));
+                }
 
-                // outInfo->mOwnedByUs = false;
-                // outQueue.erase(outQueue.begin());
-                // outInfo = NULL;
-                // notifyFillBufferDone(outHeader);
-                // outHeader = NULL;
                 resetPlugin();
+                return;
             }
         }
         inOffset += s_dec_op.u4_num_bytes_consumed;
diff --git a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
index 5deaf5d..91ec003 100644
--- a/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
+++ b/media/libstagefright/codecs/avcdec/C2SoftAvcDec.h
@@ -58,9 +58,6 @@
 
 #define MIN(a, b) ((a) < (b)) ? (a) : (b)
 
-/** Used to remove warnings about unused parameters */
-#define UNUSED(x) ((void)(x))
-
 /** Get time */
 #define GETTIME(a, b) gettimeofday(a, b);
 
diff --git a/media/libstagefright/include/CCodecBufferChannel.h b/media/libstagefright/include/CCodecBufferChannel.h
new file mode 100644
index 0000000..354cee2
--- /dev/null
+++ b/media/libstagefright/include/CCodecBufferChannel.h
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2017, 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 A_BUFFER_CHANNEL_H_
+
+#define A_BUFFER_CHANNEL_H_
+
+#include <map>
+#include <memory>
+#include <mutex>
+#include <vector>
+
+#include <C2Buffer.h>
+#include <C2Component.h>
+
+#include <media/stagefright/foundation/Mutexed.h>
+#include <media/stagefright/CodecBase.h>
+#include <media/ICrypto.h>
+
+namespace android {
+
+/**
+ * BufferChannelBase implementation for ACodec.
+ */
+class CCodecBufferChannel : public BufferChannelBase {
+public:
+    class Buffers {
+    public:
+        Buffers() = default;
+        virtual ~Buffers() = default;
+
+        inline void setAlloc(const std::shared_ptr<C2BlockPool> &alloc) { mAlloc = alloc; }
+        inline void setFormat(const sp<AMessage> &format) { mFormat = format; }
+        inline const std::shared_ptr<C2BlockPool> &getAlloc() { return mAlloc; }
+
+    protected:
+        // Input: this object uses it to allocate input buffers with which the
+        // client fills.
+        // Output: this object passes it to the component.
+        std::shared_ptr<C2BlockPool> mAlloc;
+        sp<AMessage> mFormat;
+
+    private:
+        DISALLOW_EVIL_CONSTRUCTORS(Buffers);
+    };
+
+    class InputBuffers : public Buffers {
+    public:
+        using Buffers::Buffers;
+        virtual ~InputBuffers() = default;
+
+        virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
+        virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
+        virtual void flush() = 0;
+
+    private:
+        DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
+    };
+
+    class OutputBuffers : public Buffers {
+    public:
+        using Buffers::Buffers;
+        virtual ~OutputBuffers() = default;
+
+        virtual bool registerBuffer(
+                const std::shared_ptr<C2Buffer> &buffer,
+                size_t *index,
+                sp<MediaCodecBuffer> *codecBuffer) = 0;
+        virtual std::shared_ptr<C2Buffer> releaseBuffer(const sp<MediaCodecBuffer> &buffer) = 0;
+        virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
+
+    private:
+        DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
+    };
+
+    CCodecBufferChannel(const std::function<void(status_t, enum ActionCode)> &onError);
+    virtual ~CCodecBufferChannel();
+
+    // BufferChannelBase interface
+    virtual status_t queueInputBuffer(const sp<MediaCodecBuffer> &buffer) override;
+    virtual status_t queueSecureInputBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            bool secure,
+            const uint8_t *key,
+            const uint8_t *iv,
+            CryptoPlugin::Mode mode,
+            CryptoPlugin::Pattern pattern,
+            const CryptoPlugin::SubSample *subSamples,
+            size_t numSubSamples,
+            AString *errorDetailMsg) override;
+    virtual status_t renderOutputBuffer(
+            const sp<MediaCodecBuffer> &buffer, int64_t timestampNs) override;
+    virtual status_t discardBuffer(const sp<MediaCodecBuffer> &buffer) override;
+    virtual void getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+    virtual void getOutputBufferArray(Vector<sp<MediaCodecBuffer>> *array) override;
+
+    // Methods below are interface for CCodec to use.
+
+    void setComponent(const std::shared_ptr<C2Component> &component);
+    status_t setSurface(const sp<Surface> &surface);
+
+    /**
+     * Set C2BlockPool for input buffers.
+     *
+     * TODO: start timestamp?
+     */
+    void setInputBufferAllocator(const sp<C2BlockPool> &inAlloc);
+
+    /**
+     * Set C2BlockPool for output buffers. This object shall never use the
+     * allocator itself; it's just passed
+     *
+     * TODO: start timestamp?
+     */
+    void setOutputBufferAllocator(const sp<C2BlockPool> &outAlloc);
+
+    /**
+     * Start queueing buffers to the component. This object should never queue
+     * buffers before this call.
+     */
+    void start(const sp<AMessage> &inputFormat, const sp<AMessage> &outputFormat);
+
+    /**
+     * Stop queueing buffers to the component. This object should never queue
+     * buffers after this call, until start() is called.
+     */
+    void stop();
+
+    void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork);
+
+    /**
+     * Notify MediaCodec about work done.
+     *
+     * @param workItems   finished work items.
+     */
+    void onWorkDone(std::vector<std::unique_ptr<C2Work>> workItems);
+
+private:
+    class QueueGuard;
+
+    class QueueSync {
+    public:
+        inline QueueSync() : mCount(-1) {}
+        ~QueueSync() = default;
+
+        void start();
+        void stop();
+
+    private:
+        std::mutex mMutex;
+        std::atomic_int32_t mCount;
+
+        friend class CCodecBufferChannel::QueueGuard;
+    };
+
+    class QueueGuard {
+    public:
+        QueueGuard(QueueSync &sync);
+        ~QueueGuard();
+        inline bool isRunning() { return mRunning; }
+
+    private:
+        QueueSync &mSync;
+        bool mRunning;
+    };
+
+    QueueSync mSync;
+    sp<MemoryDealer> mDealer;
+    sp<IMemory> mDecryptDestination;
+    int32_t mHeapSeqNum;
+
+    std::shared_ptr<C2Component> mComponent;
+    std::function<void(status_t, enum ActionCode)> mOnError;
+    std::shared_ptr<C2BlockPool> mInputAllocator;
+    QueueSync mQueueSync;
+    Mutexed<std::unique_ptr<InputBuffers>> mInputBuffers;
+    Mutexed<std::unique_ptr<OutputBuffers>> mOutputBuffers;
+
+    std::atomic_uint64_t mFrameIndex;
+    std::atomic_uint64_t mFirstValidFrameIndex;
+
+    sp<MemoryDealer> makeMemoryDealer(size_t heapSize);
+    Mutexed<sp<Surface>> mSurface;
+
+    inline bool hasCryptoOrDescrambler() {
+        return mCrypto != NULL || mDescrambler != NULL;
+    }
+};
+
+}  // namespace android
+
+#endif  // A_BUFFER_CHANNEL_H_
diff --git a/media/libstagefright/include/Codec2Buffer.h b/media/libstagefright/include/Codec2Buffer.h
new file mode 100644
index 0000000..0272cea
--- /dev/null
+++ b/media/libstagefright/include/Codec2Buffer.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2017, 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 CODEC2_BUFFER_H_
+
+#define CODEC2_BUFFER_H_
+
+#include <C2Buffer.h>
+
+#include <media/MediaCodecBuffer.h>
+
+namespace android {
+
+class C2Buffer;
+
+/**
+ * MediaCodecBuffer implementation wraps around C2LinearBlock.
+ */
+class Codec2Buffer : public MediaCodecBuffer {
+public:
+    static sp<Codec2Buffer> allocate(
+            const sp<AMessage> &format, const std::shared_ptr<C2LinearBlock> &block);
+
+    virtual ~Codec2Buffer() = default;
+
+    C2ConstLinearBlock share();
+
+private:
+    Codec2Buffer(
+            const sp<AMessage> &format,
+            const sp<ABuffer> &buffer,
+            const std::shared_ptr<C2LinearBlock> &block);
+    Codec2Buffer() = delete;
+
+    std::shared_ptr<C2LinearBlock> mBlock;
+};
+
+}  // namespace android
+
+#endif  // CODEC2_BUFFER_H_
diff --git a/media/libstagefright/include/media/stagefright/CCodec.h b/media/libstagefright/include/media/stagefright/CCodec.h
new file mode 100644
index 0000000..3e24bbe
--- /dev/null
+++ b/media/libstagefright/include/media/stagefright/CCodec.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2017 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 C_CODEC_H_
+#define C_CODEC_H_
+
+#include <chrono>
+
+#include <C2Component.h>
+
+#include <android/native_window.h>
+#include <media/hardware/MetadataBufferType.h>
+#include <media/stagefright/foundation/Mutexed.h>
+#include <media/stagefright/CodecBase.h>
+#include <media/stagefright/FrameRenderTracker.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/SkipCutBuffer.h>
+#include <utils/NativeHandle.h>
+#include <hardware/gralloc.h>
+#include <nativebase/nativebase.h>
+
+namespace android {
+
+class CCodecBufferChannel;
+
+class CCodec : public CodecBase {
+public:
+    CCodec();
+
+    virtual std::shared_ptr<BufferChannelBase> getBufferChannel() override;
+    virtual void initiateAllocateComponent(const sp<AMessage> &msg) override;
+    virtual void initiateConfigureComponent(const sp<AMessage> &msg) override;
+    virtual void initiateCreateInputSurface() override;
+    virtual void initiateSetInputSurface(const sp<PersistentSurface> &surface) override;
+    virtual void initiateStart() override;
+    virtual void initiateShutdown(bool keepComponentAllocated = false) override;
+
+    virtual status_t setSurface(const sp<Surface> &surface) override;
+
+    virtual void signalFlush() override;
+    virtual void signalResume() override;
+
+    virtual void signalSetParameters(const sp<AMessage> &msg) override;
+    virtual void signalEndOfInputStream() override;
+    virtual void signalRequestIDRFrame() override;
+
+    void initiateReleaseIfStuck();
+
+protected:
+    virtual ~CCodec();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg) override;
+
+private:
+    typedef std::chrono::time_point<std::chrono::steady_clock> TimePoint;
+
+    void initiateStop();
+    void initiateRelease(bool sendCallback = true);
+
+    void allocate(const AString &componentName);
+    void configure(const sp<AMessage> &msg);
+    void start();
+    void stop();
+    void flush();
+    void release(bool sendCallback);
+
+    void setDeadline(const TimePoint &deadline);
+
+    enum {
+        kWhatAllocate,
+        kWhatConfigure,
+        kWhatStart,
+        kWhatFlush,
+        kWhatStop,
+        kWhatRelease,
+    };
+
+    enum {
+        RELEASED,
+        ALLOCATED,
+        FLUSHED,
+        RUNNING,
+
+        ALLOCATING,  // RELEASED -> ALLOCATED
+        STARTING,    // ALLOCATED -> RUNNING
+        STOPPING,    // RUNNING -> ALLOCATED
+        FLUSHING,    // RUNNING -> FLUSHED
+        RESUMING,    // FLUSHED -> RUNNING
+        RELEASING,   // {ANY EXCEPT RELEASED} -> RELEASED
+    };
+
+    struct State {
+        inline State() : mState(RELEASED) {}
+
+        int mState;
+        std::shared_ptr<C2Component> mComp;
+    };
+
+    struct Formats {
+        sp<AMessage> mInputFormat;
+        sp<AMessage> mOutputFormat;
+    };
+
+    Mutexed<State> mState;
+    std::shared_ptr<CCodecBufferChannel> mChannel;
+    std::shared_ptr<C2Component::Listener> mListener;
+    Mutexed<TimePoint> mDeadline;
+    Mutexed<Formats> mFormats;
+
+    DISALLOW_EVIL_CONSTRUCTORS(CCodec);
+};
+
+}  // namespace android
+
+#endif  // C_CODEC_H_
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 9197f7b..268662f 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -18,6 +18,7 @@
 
 #define CODEC_BASE_H_
 
+#include <list>
 #include <memory>
 
 #include <stdint.h>
@@ -26,7 +27,6 @@
 
 #include <media/hardware/CryptoAPI.h>
 #include <media/hardware/HardwareAPI.h>
-#include <media/IOMX.h>
 #include <media/MediaCodecInfo.h>
 #include <media/stagefright/foundation/AHandler.h>
 #include <media/stagefright/foundation/ColorUtils.h>
diff --git a/media/libstagefright/include/media/stagefright/MediaFilter.h b/media/libstagefright/include/media/stagefright/MediaFilter.h
index 0c10d11..a28c49d 100644
--- a/media/libstagefright/include/media/stagefright/MediaFilter.h
+++ b/media/libstagefright/include/media/stagefright/MediaFilter.h
@@ -57,7 +57,7 @@
             OWNED_BY_UPSTREAM,
         };
 
-        IOMX::buffer_id mBufferID;
+        uint32_t mBufferID;
         int32_t mGeneration;
         int32_t mOutputFlags;
         Status mStatus;
@@ -121,7 +121,7 @@
 
     status_t allocateBuffersOnPort(OMX_U32 portIndex);
     BufferInfo *findBufferByID(
-            uint32_t portIndex, IOMX::buffer_id bufferID,
+            uint32_t portIndex, uint32_t bufferID,
             ssize_t *index = NULL);
     void postFillThisBuffer(BufferInfo *info);
     void postDrainThisBuffer(BufferInfo *info);
diff --git a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
index 5060dc1..6aede08 100644
--- a/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
+++ b/media/libstagefright/include/media/stagefright/SimpleDecodingSource.h
@@ -47,7 +47,8 @@
     static sp<SimpleDecodingSource> Create(
             const sp<MediaSource> &source, uint32_t flags,
             const sp<ANativeWindow> &nativeWindow,
-            const char *desiredCodec = NULL);
+            const char *desiredCodec = NULL,
+            bool skipMediaCodecList = false);
 
     static sp<SimpleDecodingSource> Create(
             const sp<MediaSource> &source, uint32_t flags = 0);