Merge "Camera: Camera3Device: Fix off-by-one error" into qt-dev
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
new file mode 100644
index 0000000..a2e98cc
--- /dev/null
+++ b/apex/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "imports": [
+    {
+      "path": "system/apex/tests"
+    }
+  ]
+}
diff --git a/apex/ld.config.txt b/apex/ld.config.txt
index b6896f5..715113d 100644
--- a/apex/ld.config.txt
+++ b/apex/ld.config.txt
@@ -27,7 +27,7 @@
 # TODO: replace the following when apex has a way to auto-generate this list
 # namespace.default.link.platform.shared_libs  = %LLNDK_LIBRARIES%
 # namespace.default.link.platform.shared_libs += %SANITIZER_RUNTIME_LIBRARIES%
-namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libvulkan.so
+namespace.default.link.platform.shared_libs = libEGL.so:libGLESv1_CM.so:libGLESv2.so:libGLESv3.so:libRS.so:libandroid_net.so:libc.so:libcgrouprc.so:libclang_rt.asan-aarch64-android.so:libclang_rt.asan-arm-android.so:libclang_rt.hwasan-aarch64-android.so:libclang_rt.asan-i686-android.so:libclang_rt.asan-x86_64-android.so:libdl.so:libft2.so:liblog.so:libm.so:libmediandk.so:libnativewindow.so:libneuralnetworks.so:libsync.so:libvndksupport.so:libdl_android.so:libvulkan.so
 
 ###############################################################################
 # "platform" namespace
diff --git a/apex/manifest.json b/apex/manifest.json
index c6c63f6..cee94e2 100644
--- a/apex/manifest.json
+++ b/apex/manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.media",
-  "version": 200000000
+  "version": 210000000
 }
diff --git a/apex/manifest_codec.json b/apex/manifest_codec.json
index 4f31b15..b83e65a 100644
--- a/apex/manifest_codec.json
+++ b/apex/manifest_codec.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.media.swcodec",
-  "version": 200000000
+  "version": 210000000
 }
diff --git a/apex/testing/test_manifest.json b/apex/testing/test_manifest.json
index 9f81f9f..ddd642e 100644
--- a/apex/testing/test_manifest.json
+++ b/apex/testing/test_manifest.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.media",
-  "version": 2
+  "version": 300000000
 }
diff --git a/apex/testing/test_manifest_codec.json b/apex/testing/test_manifest_codec.json
index c956454..2320fd7 100644
--- a/apex/testing/test_manifest_codec.json
+++ b/apex/testing/test_manifest_codec.json
@@ -1,4 +1,4 @@
 {
   "name": "com.android.media.swcodec",
-  "version": 2
+  "version": 300000000
 }
diff --git a/camera/ndk/impl/ACameraMetadata.cpp b/camera/ndk/impl/ACameraMetadata.cpp
index d832deb..77dcd48 100644
--- a/camera/ndk/impl/ACameraMetadata.cpp
+++ b/camera/ndk/impl/ACameraMetadata.cpp
@@ -47,24 +47,14 @@
 bool
 ACameraMetadata::isNdkSupportedCapability(int32_t capability) {
     switch (capability) {
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BACKWARD_COMPATIBLE:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_MANUAL_POST_PROCESSING:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_RAW:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_READ_SENSOR_SETTINGS:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_BURST_CAPTURE:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_DEPTH_OUTPUT:
-        case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_LOGICAL_MULTI_CAMERA:
-            return true;
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_YUV_REPROCESSING:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_PRIVATE_REPROCESSING:
         case ANDROID_REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO:
             return false;
         default:
-            // Newly defined capabilities will be unsupported by default (blacklist)
-            // TODO: Should we do whitelist or blacklist here?
-            ALOGE("%s: Unknonwn capability %d", __FUNCTION__, capability);
-            return false;
+            // Assuming every capability passed to this function is actually a
+            // valid capability.
+            return true;
     }
 }
 
diff --git a/camera/ndk/impl/ACameraMetadata.h b/camera/ndk/impl/ACameraMetadata.h
index 3d895cb..97f7f48 100644
--- a/camera/ndk/impl/ACameraMetadata.h
+++ b/camera/ndk/impl/ACameraMetadata.h
@@ -70,6 +70,8 @@
 
   private:
 
+    // This function does not check whether the capability passed to it is valid.
+    // The caller must make sure that it is.
     bool isNdkSupportedCapability(const int32_t capability);
     static inline bool isVendorTag(const uint32_t tag);
     static bool isCaptureRequestTag(const uint32_t tag);
diff --git a/camera/ndk/include/camera/NdkCameraManager.h b/camera/ndk/include/camera/NdkCameraManager.h
index 136a497..5c810bb 100644
--- a/camera/ndk/include/camera/NdkCameraManager.h
+++ b/camera/ndk/include/camera/NdkCameraManager.h
@@ -85,6 +85,11 @@
  * <p>ACameraManager_getCameraIdList will allocate and return an {@link ACameraIdList}.
  * The caller must call {@link ACameraManager_deleteCameraIdList} to free the memory</p>
  *
+ * <p>Note: the returned camera list might be a subset to the output of <a href=
+ * "https://developer.android.com/reference/android/hardware/camera2/CameraManager.html#getCameraIdList()">
+ * SDK CameraManager#getCameraIdList API</a> as the NDK API does not support some legacy camera
+ * hardware.</p>
+ *
  * @param manager the {@link ACameraManager} of interest
  * @param cameraIdList the output {@link ACameraIdList} will be filled in here if the method call
  *        succeeds.
diff --git a/camera/ndk/include/camera/NdkCameraMetadataTags.h b/camera/ndk/include/camera/NdkCameraMetadataTags.h
index 320e7b3..ef5f26c 100644
--- a/camera/ndk/include/camera/NdkCameraMetadataTags.h
+++ b/camera/ndk/include/camera/NdkCameraMetadataTags.h
@@ -8311,6 +8311,7 @@
      * fire the flash for flash power metering during precapture, and then fire the flash
      * for the final capture, if a flash is available on the device and the AE mode is set to
      * enable the flash.</p>
+     * <p>Devices that initially shipped with Android version <a href="https://developer.android.com/reference/android/os/Build.VERSION_CODES.html#Q">Q</a> or newer will not include any LEGACY-level devices.</p>
      *
      * @see ACAMERA_CONTROL_AE_PRECAPTURE_TRIGGER
      * @see ACAMERA_REQUEST_AVAILABLE_CAPABILITIES
diff --git a/media/OWNERS b/media/OWNERS
index 1e2d123..1afc253 100644
--- a/media/OWNERS
+++ b/media/OWNERS
@@ -1,3 +1,4 @@
+andrewlewis@google.com
 chz@google.com
 dwkang@google.com
 elaurent@google.com
diff --git a/media/codec2/components/base/SimpleC2Component.cpp b/media/codec2/components/base/SimpleC2Component.cpp
index 44f1fe0..fb3fbd0 100644
--- a/media/codec2/components/base/SimpleC2Component.cpp
+++ b/media/codec2/components/base/SimpleC2Component.cpp
@@ -272,12 +272,9 @@
                 flushedWork->push_back(std::move(work));
             }
         }
-    }
-    {
-        Mutexed<PendingWork>::Locked pending(mPendingWork);
-        while (!pending->empty()) {
-            flushedWork->push_back(std::move(pending->begin()->second));
-            pending->erase(pending->begin());
+        while (!queue->pending().empty()) {
+            flushedWork->push_back(std::move(queue->pending().begin()->second));
+            queue->pending().erase(queue->pending().begin());
         }
     }
 
@@ -342,10 +339,7 @@
     {
         Mutexed<WorkQueue>::Locked queue(mWorkQueue);
         queue->clear();
-    }
-    {
-        Mutexed<PendingWork>::Locked pending(mPendingWork);
-        pending->clear();
+        queue->pending().clear();
     }
     sp<AMessage> reply;
     (new AMessage(WorkHandler::kWhatStop, mHandler))->postAndAwaitResponse(&reply);
@@ -366,10 +360,7 @@
     {
         Mutexed<WorkQueue>::Locked queue(mWorkQueue);
         queue->clear();
-    }
-    {
-        Mutexed<PendingWork>::Locked pending(mPendingWork);
-        pending->clear();
+        queue->pending().clear();
     }
     sp<AMessage> reply;
     (new AMessage(WorkHandler::kWhatReset, mHandler))->postAndAwaitResponse(&reply);
@@ -401,13 +392,13 @@
         uint64_t frameIndex, std::function<void(const std::unique_ptr<C2Work> &)> fillWork) {
     std::unique_ptr<C2Work> work;
     {
-        Mutexed<PendingWork>::Locked pending(mPendingWork);
-        if (pending->count(frameIndex) == 0) {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->pending().count(frameIndex) == 0) {
             ALOGW("unknown frame index: %" PRIu64, frameIndex);
             return;
         }
-        work = std::move(pending->at(frameIndex));
-        pending->erase(frameIndex);
+        work = std::move(queue->pending().at(frameIndex));
+        queue->pending().erase(frameIndex);
     }
     if (work) {
         fillWork(work);
@@ -426,13 +417,13 @@
         work->input.flags = currentWork->input.flags;
         work->input.ordinal = currentWork->input.ordinal;
     } else {
-        Mutexed<PendingWork>::Locked pending(mPendingWork);
-        if (pending->count(frameIndex) == 0) {
+        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+        if (queue->pending().count(frameIndex) == 0) {
             ALOGW("unknown frame index: %" PRIu64, frameIndex);
             return;
         }
-        work->input.flags = pending->at(frameIndex)->input.flags;
-        work->input.ordinal = pending->at(frameIndex)->input.ordinal;
+        work->input.flags = queue->pending().at(frameIndex)->input.flags;
+        work->input.ordinal = queue->pending().at(frameIndex)->input.ordinal;
     }
     work->worklets.emplace_back(new C2Worklet);
     if (work) {
@@ -552,24 +543,21 @@
     }
     process(work, mOutputBlockPool);
     ALOGV("processed frame #%" PRIu64, work->input.ordinal.frameIndex.peeku());
-    {
-        Mutexed<WorkQueue>::Locked queue(mWorkQueue);
-        if (queue->generation() != generation) {
-            ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
-                    queue->generation(), generation);
-            work->result = C2_NOT_FOUND;
-            queue.unlock();
-            {
-                Mutexed<ExecState>::Locked state(mExecState);
-                std::shared_ptr<C2Component::Listener> listener = state->mListener;
-                state.unlock();
-                listener->onWorkDone_nb(shared_from_this(), vec(work));
-            }
-            queue.lock();
-            return hasQueuedWork;
-        }
+    Mutexed<WorkQueue>::Locked queue(mWorkQueue);
+    if (queue->generation() != generation) {
+        ALOGD("work form old generation: was %" PRIu64 " now %" PRIu64,
+                queue->generation(), generation);
+        work->result = C2_NOT_FOUND;
+        queue.unlock();
+
+        Mutexed<ExecState>::Locked state(mExecState);
+        std::shared_ptr<C2Component::Listener> listener = state->mListener;
+        state.unlock();
+        listener->onWorkDone_nb(shared_from_this(), vec(work));
+        return hasQueuedWork;
     }
     if (work->workletsProcessed != 0u) {
+        queue.unlock();
         Mutexed<ExecState>::Locked state(mExecState);
         ALOGV("returning this work");
         std::shared_ptr<C2Component::Listener> listener = state->mListener;
@@ -579,15 +567,15 @@
         ALOGV("queue pending work");
         work->input.buffers.clear();
         std::unique_ptr<C2Work> unexpected;
-        {
-            Mutexed<PendingWork>::Locked pending(mPendingWork);
-            uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
-            if (pending->count(frameIndex) != 0) {
-                unexpected = std::move(pending->at(frameIndex));
-                pending->erase(frameIndex);
-            }
-            (void)pending->insert({ frameIndex, std::move(work) });
+
+        uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
+        if (queue->pending().count(frameIndex) != 0) {
+            unexpected = std::move(queue->pending().at(frameIndex));
+            queue->pending().erase(frameIndex);
         }
+        (void)queue->pending().insert({ frameIndex, std::move(work) });
+
+        queue.unlock();
         if (unexpected) {
             ALOGD("unexpected pending work");
             unexpected->result = C2_CORRUPTED;
diff --git a/media/codec2/components/base/include/SimpleC2Component.h b/media/codec2/components/base/include/SimpleC2Component.h
index 43029a9..22d5714 100644
--- a/media/codec2/components/base/include/SimpleC2Component.h
+++ b/media/codec2/components/base/include/SimpleC2Component.h
@@ -202,6 +202,8 @@
 
     class WorkQueue {
     public:
+        typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
+
         inline WorkQueue() : mFlush(false), mGeneration(0ul) {}
 
         inline uint64_t generation() const { return mGeneration; }
@@ -218,6 +220,7 @@
             return flush;
         }
         void clear();
+        PendingWork &pending() { return mPendingWork; }
 
     private:
         struct Entry {
@@ -228,12 +231,10 @@
         bool mFlush;
         uint64_t mGeneration;
         std::list<Entry> mQueue;
+        PendingWork mPendingWork;
     };
     Mutexed<WorkQueue> mWorkQueue;
 
-    typedef std::unordered_map<uint64_t, std::unique_ptr<C2Work>> PendingWork;
-    Mutexed<PendingWork> mPendingWork;
-
     class BlockingBlockPool;
     std::shared_ptr<BlockingBlockPool> mOutputBlockPool;
 
diff --git a/media/codec2/core/include/C2Config.h b/media/codec2/core/include/C2Config.h
index 9545c45..c395d62 100644
--- a/media/codec2/core/include/C2Config.h
+++ b/media/codec2/core/include/C2Config.h
@@ -1194,7 +1194,8 @@
     PREPEND_HEADER_TO_ALL_SYNC,
 )
 
-typedef C2GlobalParam<C2Setting, C2BoolValue, kParamIndexPrependHeaderMode>
+typedef C2GlobalParam<C2Setting, C2SimpleValueStruct<C2Config::prepend_header_mode_t>,
+                kParamIndexPrependHeaderMode>
         C2PrependHeaderModeSetting;
 constexpr char C2_PARAMKEY_PREPEND_HEADER_MODE[] = "output.buffers.prepend-header";
 
diff --git a/media/codec2/hidl/client/Android.bp b/media/codec2/hidl/client/Android.bp
index a2a498d..965e438 100644
--- a/media/codec2/hidl/client/Android.bp
+++ b/media/codec2/hidl/client/Android.bp
@@ -20,7 +20,6 @@
         "libhidltransport",
         "liblog",
         "libstagefright_bufferpool@2.0",
-        "libstagefright_bufferqueue_helper",
         "libui",
         "libutils",
     ],
@@ -30,8 +29,8 @@
     ],
 
     export_shared_lib_headers: [
-        "libcodec2_hidl@1.0",
         "libcodec2",
+        "libcodec2_hidl@1.0",
     ],
 
 }
diff --git a/media/codec2/hidl/client/client.cpp b/media/codec2/hidl/client/client.cpp
index 0fe8376..53adbbc 100644
--- a/media/codec2/hidl/client/client.cpp
+++ b/media/codec2/hidl/client/client.cpp
@@ -21,8 +21,12 @@
 #include <codec2/hidl/client.h>
 
 #include <deque>
+#include <iterator>
 #include <limits>
 #include <map>
+#include <mutex>
+#include <sstream>
+#include <thread>
 #include <type_traits>
 #include <vector>
 
@@ -39,6 +43,7 @@
 #include <android/hardware/media/c2/1.0/IComponentListener.h>
 #include <android/hardware/media/c2/1.0/IComponentStore.h>
 #include <android/hardware/media/c2/1.0/IConfigurable.h>
+#include <android/hidl/manager/1.2/IServiceManager.h>
 
 #include <C2Debug.h>
 #include <C2BufferPriv.h>
@@ -70,35 +75,181 @@
 // c2_status_t value that corresponds to hwbinder transaction failure.
 constexpr c2_status_t C2_TRANSACTION_FAILED = C2_CORRUPTED;
 
-// List of known IComponentStore services in the decreasing order of preference.
-constexpr const char* kClientNames[] = {
-        "default",
-        "software",
-    };
+// Returns the list of IComponentStore service names that are available on the
+// device. This list is specified at the build time in manifest files.
+// Note: A software service will have "_software" as a suffix.
+std::vector<std::string> const& getServiceNames() {
+    static std::vector<std::string> sServiceNames{[]() {
+        using ::android::hardware::media::c2::V1_0::IComponentStore;
+        using ::android::hidl::manager::V1_2::IServiceManager;
 
-// Number of known IComponentStore services.
-constexpr size_t kNumClients = std::extent<decltype(kClientNames)>::value;
+        while (true) {
+            sp<IServiceManager> serviceManager = IServiceManager::getService();
+            CHECK(serviceManager) << "Hardware service manager is not running.";
 
-typedef std::array<std::shared_ptr<Codec2Client>, kNumClients> ClientList;
+            // There are three categories of services based on names.
+            std::vector<std::string> defaultNames; // Prefixed with "default"
+            std::vector<std::string> vendorNames;  // Prefixed with "vendor"
+            std::vector<std::string> otherNames;   // Others
+            Return<void> transResult;
+            transResult = serviceManager->listManifestByInterface(
+                    IComponentStore::descriptor,
+                    [&defaultNames, &vendorNames, &otherNames](
+                            hidl_vec<hidl_string> const& instanceNames) {
+                        for (hidl_string const& instanceName : instanceNames) {
+                            char const* name = instanceName.c_str();
+                            if (strncmp(name, "default", 7) == 0) {
+                                defaultNames.emplace_back(name);
+                            } else if (strncmp(name, "vendor", 6) == 0) {
+                                vendorNames.emplace_back(name);
+                            } else {
+                                otherNames.emplace_back(name);
+                            }
+                        }
+                    });
+            if (transResult.isOk()) {
+                // Sort service names in each category.
+                std::sort(defaultNames.begin(), defaultNames.end());
+                std::sort(vendorNames.begin(), vendorNames.end());
+                std::sort(otherNames.begin(), otherNames.end());
 
-// Convenience methods to obtain known clients.
-std::shared_ptr<Codec2Client> getClient(size_t index) {
-    uint32_t serviceMask = ::android::base::GetUintProperty(
-            "debug.media.codec2", uint32_t(0));
-    return Codec2Client::CreateFromService(
-            kClientNames[index],
-            (serviceMask & (1 << index)) != 0);
+                // Concatenate the three lists in this order: default, vendor,
+                // other.
+                std::vector<std::string>& names = defaultNames;
+                names.reserve(names.size() + vendorNames.size() + otherNames.size());
+                names.insert(names.end(),
+                             std::make_move_iterator(vendorNames.begin()),
+                             std::make_move_iterator(vendorNames.end()));
+                names.insert(names.end(),
+                             std::make_move_iterator(otherNames.begin()),
+                             std::make_move_iterator(otherNames.end()));
+
+                // Summarize to logcat.
+                if (names.empty()) {
+                    LOG(INFO) << "No Codec2 services declared in the manifest.";
+                } else {
+                    std::stringstream stringOutput;
+                    stringOutput << "Available Codec2 services:";
+                    for (std::string const& name : names) {
+                        stringOutput << " \"" << name << "\"";
+                    }
+                    LOG(INFO) << stringOutput.str();
+                }
+
+                return names;
+            }
+            LOG(ERROR) << "Could not retrieve the list of service instances of "
+                       << IComponentStore::descriptor
+                       << ". Retrying...";
+        }
+    }()};
+    return sServiceNames;
 }
 
-ClientList getClientList() {
-    ClientList list;
-    for (size_t i = 0; i < list.size(); ++i) {
-        list[i] = getClient(i);
+// Searches for a name in getServiceNames() and returns the index found. If the
+// name is not found, the returned index will be equal to
+// getServiceNames().size().
+size_t getServiceIndex(char const* name) {
+    std::vector<std::string> const& names = getServiceNames();
+    size_t i = 0;
+    for (; i < names.size(); ++i) {
+        if (name == names[i]) {
+            break;
+        }
     }
-    return list;
+    return i;
 }
 
-} // unnamed
+}  // unnamed namespace
+
+// This class caches a Codec2Client object and its component traits. The client
+// will be created the first time it is needed, and it can be refreshed if the
+// service dies (by calling invalidate()). The first time listComponents() is
+// called from the client, the result will be cached.
+class Codec2Client::Cache {
+    // Cached client
+    std::shared_ptr<Codec2Client> mClient;
+    mutable std::mutex mClientMutex;
+
+    // Cached component traits
+    std::vector<C2Component::Traits> mTraits;
+    std::once_flag mTraitsInitializationFlag;
+
+    // The index of the service. This is based on getServiceNames().
+    size_t mIndex;
+    // A "valid" cache object must have its mIndex set with init().
+    bool mValid{false};
+    // Called by s() exactly once to initialize the cache. The index must be a
+    // valid index into the vector returned by getServiceNames(). Calling
+    // init(index) will associate the cache to the service with name
+    // getServiceNames()[index].
+    void init(size_t index) {
+        mIndex = index;
+        mValid = true;
+    }
+
+public:
+    Cache() = default;
+
+    // Initializes mClient if needed, then returns mClient.
+    // If the service is unavailable but listed in the manifest, this function
+    // will block indefinitely.
+    std::shared_ptr<Codec2Client> getClient() {
+        CHECK(mValid) << "Uninitialized cache";
+        std::scoped_lock lock{mClientMutex};
+        if (!mClient) {
+            mClient = Codec2Client::_CreateFromIndex(mIndex);
+        }
+        return mClient;
+    }
+
+    // Causes a subsequent call to getClient() to create a new client. This
+    // function should be called after the service dies.
+    //
+    // Note: This function is called only by ForAllServices().
+    void invalidate() {
+        CHECK(mValid) << "Uninitialized cache";
+        std::scoped_lock lock{mClientMutex};
+        mClient = nullptr;
+    }
+
+    // Returns a list of traits for components supported by the service. This
+    // list is cached.
+    std::vector<C2Component::Traits> const& getTraits() {
+        CHECK(mValid) << "Uninitialized cache";
+        std::call_once(mTraitsInitializationFlag, [this]() {
+            bool success{false};
+            // Spin until _listComponents() is successful.
+            while (true) {
+                std::shared_ptr<Codec2Client> client = getClient();
+                mTraits = client->_listComponents(&success);
+                if (success) {
+                    break;
+                }
+                using namespace std::chrono_literals;
+                static constexpr auto kServiceRetryPeriod = 5s;
+                LOG(INFO) << "Failed to retrieve component traits from service "
+                             "\"" << getServiceNames()[mIndex] << "\". "
+                             "Retrying...";
+                std::this_thread::sleep_for(kServiceRetryPeriod);
+            }
+        });
+        return mTraits;
+    }
+
+    // List() returns the list of all caches.
+    static std::vector<Cache>& List() {
+        static std::vector<Cache> sCaches{[]() {
+            size_t numServices = getServiceNames().size();
+            std::vector<Cache> caches(numServices);
+            for (size_t i = 0; i < numServices; ++i) {
+                caches[i].init(i);
+            }
+            return caches;
+        }()};
+        return sCaches;
+    }
+};
 
 // Codec2ConfigurableClient
 
@@ -439,7 +590,7 @@
 
 // Codec2Client
 Codec2Client::Codec2Client(const sp<IComponentStore>& base,
-                           std::string serviceName)
+                           size_t serviceIndex)
       : Configurable{
             [base]() -> sp<IConfigurable> {
                 Return<sp<IConfigurable>> transResult =
@@ -450,8 +601,7 @@
             }()
         },
         mBase{base},
-        mListed{false},
-        mServiceName{serviceName} {
+        mServiceIndex{serviceIndex} {
     Return<sp<IClientManager>> transResult = base->getPoolClientManager();
     if (!transResult.isOk()) {
         LOG(ERROR) << "getPoolClientManager -- transaction failed.";
@@ -460,6 +610,10 @@
     }
 }
 
+std::string const& Codec2Client::getServiceName() const {
+    return getServiceNames()[mServiceIndex];
+}
+
 c2_status_t Codec2Client::createComponent(
         const C2String& name,
         const std::shared_ptr<Codec2Client::Listener>& listener,
@@ -558,33 +712,38 @@
     return status;
 }
 
-const std::vector<C2Component::Traits>& Codec2Client::listComponents() const {
-    std::lock_guard<std::mutex> lock(mMutex);
-    if (mListed) {
-        return mTraitsList;
-    }
+std::vector<C2Component::Traits> const& Codec2Client::listComponents() const {
+    return Cache::List()[mServiceIndex].getTraits();
+}
+
+std::vector<C2Component::Traits> Codec2Client::_listComponents(
+        bool* success) const {
+    std::vector<C2Component::Traits> traits;
+    std::string const& serviceName = getServiceName();
     Return<void> transStatus = mBase->listComponents(
-            [this](Status s,
+            [&traits, &serviceName](Status s,
                    const hidl_vec<IComponentStore::ComponentTraits>& t) {
                 if (s != Status::OK) {
-                    LOG(DEBUG) << "listComponents -- call failed: "
+                    LOG(DEBUG) << "_listComponents -- call failed: "
                                << static_cast<c2_status_t>(s) << ".";
                     return;
                 }
-                mTraitsList.resize(t.size());
+                traits.resize(t.size());
                 for (size_t i = 0; i < t.size(); ++i) {
-                    if (!objcpy(&mTraitsList[i], t[i])) {
-                        LOG(ERROR) << "listComponents -- corrupted output.";
+                    if (!objcpy(&traits[i], t[i])) {
+                        LOG(ERROR) << "_listComponents -- corrupted output.";
                         return;
                     }
-                    mTraitsList[i].owner = mServiceName;
+                    traits[i].owner = serviceName;
                 }
             });
     if (!transStatus.isOk()) {
-        LOG(ERROR) << "listComponents -- transaction failed.";
+        LOG(ERROR) << "_listComponents -- transaction failed.";
+        *success = false;
+    } else {
+        *success = true;
     }
-    mListed = true;
-    return mTraitsList;
+    return traits;
 }
 
 c2_status_t Codec2Client::copyBuffer(
@@ -649,34 +808,29 @@
 };
 
 std::shared_ptr<Codec2Client> Codec2Client::CreateFromService(
-        const char* serviceName, bool waitForService) {
-    if (!serviceName) {
-        return nullptr;
-    }
-    sp<Base> baseStore = waitForService ?
-            Base::getService(serviceName) :
-            Base::tryGetService(serviceName);
-    if (!baseStore) {
-        if (waitForService) {
-            LOG(WARNING) << "Codec2.0 service \"" << serviceName << "\""
-                            " inaccessible. Check the device manifest.";
-        } else {
-            LOG(DEBUG) << "Codec2.0 service \"" << serviceName << "\""
-                          " unavailable at the moment. "
-                          " Wait or check the device manifest.";
-        }
-        return nullptr;
-    }
-    return std::make_shared<Codec2Client>(baseStore, serviceName);
+        const char* name) {
+    size_t index = getServiceIndex(name);
+    return index == getServiceNames().size() ?
+            nullptr : _CreateFromIndex(index);
 }
 
-c2_status_t Codec2Client::ForAllStores(
+std::shared_ptr<Codec2Client> Codec2Client::_CreateFromIndex(size_t index) {
+    std::string const& name = getServiceNames()[index];
+    LOG(INFO) << "Creating a Codec2 client to service \"" << name << "\"";
+    sp<Base> baseStore = Base::getService(name);
+    CHECK(baseStore) << "Codec2 service \"" << name << "\""
+                        " inaccessible for unknown reasons.";
+    LOG(INFO) << "Client to Codec2 service \"" << name << "\" created";
+    return std::make_shared<Codec2Client>(baseStore, index);
+}
+
+c2_status_t Codec2Client::ForAllServices(
         const std::string &key,
         std::function<c2_status_t(const std::shared_ptr<Codec2Client>&)>
             predicate) {
     c2_status_t status = C2_NO_INIT;  // no IComponentStores present
 
-    // Cache the mapping key -> index of Codec2Client in getClient().
+    // Cache the mapping key -> index of Codec2Client in Cache::List().
     static std::mutex key2IndexMutex;
     static std::map<std::string, size_t> key2Index;
 
@@ -684,33 +838,36 @@
     // the last known client fails, retry once. We do this by pushing the last
     // known client in front of the list of all clients.
     std::deque<size_t> indices;
-    for (size_t index = kNumClients; index > 0; ) {
+    for (size_t index = Cache::List().size(); index > 0; ) {
         indices.push_front(--index);
     }
 
     bool wasMapped = false;
-    std::unique_lock<std::mutex> lock(key2IndexMutex);
-    auto it = key2Index.find(key);
-    if (it != key2Index.end()) {
-        indices.push_front(it->second);
-        wasMapped = true;
+    {
+        std::scoped_lock lock{key2IndexMutex};
+        auto it = key2Index.find(key);
+        if (it != key2Index.end()) {
+            indices.push_front(it->second);
+            wasMapped = true;
+        }
     }
-    lock.unlock();
 
     for (size_t index : indices) {
-        std::shared_ptr<Codec2Client> client = getClient(index);
+        Cache& cache = Cache::List()[index];
+        std::shared_ptr<Codec2Client> client{cache.getClient()};
         if (client) {
             status = predicate(client);
             if (status == C2_OK) {
-                lock.lock();
+                std::scoped_lock lock{key2IndexMutex};
                 key2Index[key] = index; // update last known client index
-                return status;
+                return C2_OK;
             }
         }
         if (wasMapped) {
             LOG(INFO) << "Could not find \"" << key << "\""
                          " in the last instance. Retrying...";
             wasMapped = false;
+            cache.invalidate();
         }
     }
     return status;  // return the last status from a valid client
@@ -722,7 +879,7 @@
         const std::shared_ptr<Listener>& listener,
         std::shared_ptr<Codec2Client>* owner) {
     std::shared_ptr<Component> component;
-    c2_status_t status = ForAllStores(
+    c2_status_t status = ForAllServices(
             componentName,
             [owner, &component, componentName, &listener](
                     const std::shared_ptr<Codec2Client> &client)
@@ -755,7 +912,7 @@
         const char* interfaceName,
         std::shared_ptr<Codec2Client>* owner) {
     std::shared_ptr<Interface> interface;
-    c2_status_t status = ForAllStores(
+    c2_status_t status = ForAllServices(
             interfaceName,
             [owner, &interface, interfaceName](
                     const std::shared_ptr<Codec2Client> &client)
@@ -782,50 +939,54 @@
     return interface;
 }
 
-std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface() {
-    uint32_t serviceMask = ::android::base::GetUintProperty(
-            "debug.stagefright.c2inputsurface", uint32_t(0));
-    for (size_t i = 0; i < kNumClients; ++i) {
-        if ((1 << i) & serviceMask) {
-            std::shared_ptr<Codec2Client> client = getClient(i);
-            std::shared_ptr<Codec2Client::InputSurface> inputSurface;
-            if (client &&
-                    client->createInputSurface(&inputSurface) == C2_OK &&
-                    inputSurface) {
-                return inputSurface;
-            }
-        }
-    }
-    LOG(INFO) << "Could not create an input surface "
-                 "from any Codec2.0 services.";
-    return nullptr;
-}
-
-const std::vector<C2Component::Traits>& Codec2Client::ListComponents() {
-    static std::vector<C2Component::Traits> traitsList = [](){
+std::vector<C2Component::Traits> const& Codec2Client::ListComponents() {
+    static std::vector<C2Component::Traits> sList{[]() {
         std::vector<C2Component::Traits> list;
-        size_t listSize = 0;
-        ClientList clientList = getClientList();
-        for (const std::shared_ptr<Codec2Client>& client : clientList) {
-            if (!client) {
-                continue;
-            }
-            listSize += client->listComponents().size();
-        }
-        list.reserve(listSize);
-        for (const std::shared_ptr<Codec2Client>& client : clientList) {
-            if (!client) {
-                continue;
-            }
-            list.insert(
-                    list.end(),
-                    client->listComponents().begin(),
-                    client->listComponents().end());
+        for (Cache& cache : Cache::List()) {
+            std::vector<C2Component::Traits> const& traits = cache.getTraits();
+            list.insert(list.end(), traits.begin(), traits.end());
         }
         return list;
-    }();
+    }()};
+    return sList;
+}
 
-    return traitsList;
+std::shared_ptr<Codec2Client::InputSurface> Codec2Client::CreateInputSurface(
+        char const* serviceName) {
+    uint32_t inputSurfaceSetting = ::android::base::GetUintProperty(
+            "debug.stagefright.c2inputsurface", uint32_t(0));
+    if (inputSurfaceSetting == 0) {
+        return nullptr;
+    }
+    size_t index = getServiceNames().size();
+    if (serviceName) {
+        index = getServiceIndex(serviceName);
+        if (index == getServiceNames().size()) {
+            LOG(DEBUG) << "CreateInputSurface -- invalid service name: \""
+                       << serviceName << "\"";
+        }
+    }
+
+    std::shared_ptr<Codec2Client::InputSurface> inputSurface;
+    if (index != getServiceNames().size()) {
+        std::shared_ptr<Codec2Client> client = Cache::List()[index].getClient();
+        if (client->createInputSurface(&inputSurface) == C2_OK) {
+            return inputSurface;
+        }
+    }
+    LOG(INFO) << "CreateInputSurface -- attempting to create an input surface "
+                 "from all services...";
+    for (Cache& cache : Cache::List()) {
+        std::shared_ptr<Codec2Client> client = cache.getClient();
+        if (client->createInputSurface(&inputSurface) == C2_OK) {
+            LOG(INFO) << "CreateInputSurface -- input surface obtained from "
+                         "service \"" << client->getServiceName() << "\"";
+            return inputSurface;
+        }
+    }
+    LOG(WARNING) << "CreateInputSurface -- failed to create an input surface "
+                    "from all services";
+    return nullptr;
 }
 
 // Codec2Client::Listener
diff --git a/media/codec2/hidl/client/include/codec2/hidl/client.h b/media/codec2/hidl/client/include/codec2/hidl/client.h
index cd42205..8265380 100644
--- a/media/codec2/hidl/client/include/codec2/hidl/client.h
+++ b/media/codec2/hidl/client/include/codec2/hidl/client.h
@@ -144,53 +144,52 @@
 
     typedef Codec2Client Store;
 
-    std::string getServiceName() const { return mServiceName; }
+    std::string const& getServiceName() const;
 
     c2_status_t createComponent(
-            const C2String& name,
-            const std::shared_ptr<Listener>& listener,
+            C2String const& name,
+            std::shared_ptr<Listener> const& listener,
             std::shared_ptr<Component>* const component);
 
     c2_status_t createInterface(
-            const C2String& name,
+            C2String const& name,
             std::shared_ptr<Interface>* const interface);
 
     c2_status_t createInputSurface(
             std::shared_ptr<InputSurface>* const inputSurface);
 
-    const std::vector<C2Component::Traits>& listComponents() const;
+    std::vector<C2Component::Traits> const& listComponents() const;
 
     c2_status_t copyBuffer(
-            const std::shared_ptr<C2Buffer>& src,
-            const std::shared_ptr<C2Buffer>& dst);
+            std::shared_ptr<C2Buffer> const& src,
+            std::shared_ptr<C2Buffer> const& dst);
 
     std::shared_ptr<C2ParamReflector> getParamReflector();
 
-    static std::shared_ptr<Codec2Client> CreateFromService(
-            const char* serviceName,
-            bool waitForService = true);
+    static std::shared_ptr<Codec2Client> CreateFromService(char const* name);
 
     // Try to create a component with a given name from all known
     // IComponentStore services.
     static std::shared_ptr<Component> CreateComponentByName(
-            const char* componentName,
-            const std::shared_ptr<Listener>& listener,
+            char const* componentName,
+            std::shared_ptr<Listener> const& listener,
             std::shared_ptr<Codec2Client>* owner = nullptr);
 
     // Try to create a component interface with a given name from all known
     // IComponentStore services.
     static std::shared_ptr<Interface> CreateInterfaceByName(
-            const char* interfaceName,
+            char const* interfaceName,
             std::shared_ptr<Codec2Client>* owner = nullptr);
 
     // List traits from all known IComponentStore services.
-    static const std::vector<C2Component::Traits>& ListComponents();
+    static std::vector<C2Component::Traits> const& ListComponents();
 
     // Create an input surface.
-    static std::shared_ptr<InputSurface> CreateInputSurface();
+    static std::shared_ptr<InputSurface> CreateInputSurface(
+            char const* serviceName = nullptr);
 
     // base cannot be null.
-    Codec2Client(const sp<Base>& base, std::string serviceName);
+    Codec2Client(sp<Base> const& base, size_t serviceIndex);
 
 protected:
     sp<Base> mBase;
@@ -198,17 +197,22 @@
     // Finds the first store where the predicate returns OK, and returns the last
     // predicate result. Uses key to remember the last store found, and if cached,
     // it tries that store before trying all stores (one retry).
-    static c2_status_t ForAllStores(
+    static c2_status_t ForAllServices(
             const std::string& key,
-            std::function<c2_status_t(const std::shared_ptr<Codec2Client>&)> predicate);
+            std::function<c2_status_t(std::shared_ptr<Codec2Client> const&)>
+                predicate);
 
-    mutable std::mutex mMutex;
-    mutable bool mListed;
-    std::string mServiceName;
+    size_t mServiceIndex;
     mutable std::vector<C2Component::Traits> mTraitsList;
 
     sp<::android::hardware::media::bufferpool::V2_0::IClientManager>
             mHostPoolManager;
+
+    static std::shared_ptr<Codec2Client> _CreateFromIndex(size_t index);
+
+    std::vector<C2Component::Traits> _listComponents(bool* success) const;
+
+    class Cache;
 };
 
 struct Codec2Client::Interface : public Codec2Client::Configurable {
diff --git a/media/codec2/sfplugin/Android.bp b/media/codec2/sfplugin/Android.bp
index a212651..8ae80ee 100644
--- a/media/codec2/sfplugin/Android.bp
+++ b/media/codec2/sfplugin/Android.bp
@@ -5,6 +5,7 @@
         "C2OMXNode.cpp",
         "CCodec.cpp",
         "CCodecBufferChannel.cpp",
+        "CCodecBuffers.cpp",
         "CCodecConfig.cpp",
         "Codec2Buffer.cpp",
         "Codec2InfoBuilder.cpp",
@@ -40,7 +41,6 @@
         "libmedia",
         "libmedia_omx",
         "libsfplugin_ccodec_utils",
-        "libstagefright_bufferqueue_helper",
         "libstagefright_codecbase",
         "libstagefright_foundation",
         "libstagefright_omx_utils",
diff --git a/media/codec2/sfplugin/C2OMXNode.cpp b/media/codec2/sfplugin/C2OMXNode.cpp
index 962df0f..6ae1c13 100644
--- a/media/codec2/sfplugin/C2OMXNode.cpp
+++ b/media/codec2/sfplugin/C2OMXNode.cpp
@@ -287,7 +287,7 @@
         return UNKNOWN_ERROR;
     }
 
-    (void)mBufferIdsInUse.emplace(index, buffer);
+    mBufferIdsInUse.lock()->emplace(index, buffer);
     return OK;
 }
 
@@ -327,13 +327,19 @@
         ALOGD("Buffer source not set (index=%llu)", index.peekull());
         return;
     }
-    auto it = mBufferIdsInUse.find(index.peeku());
-    if (it == mBufferIdsInUse.end()) {
-        ALOGV("Untracked input index %llu (maybe already removed)", index.peekull());
-        return;
+
+    int32_t bufferId = 0;
+    {
+        decltype(mBufferIdsInUse)::Locked bufferIds(mBufferIdsInUse);
+        auto it = bufferIds->find(index.peeku());
+        if (it == bufferIds->end()) {
+            ALOGV("Untracked input index %llu (maybe already removed)", index.peekull());
+            return;
+        }
+        bufferId = it->second;
+        (void)bufferIds->erase(it);
     }
-    (void)mBufferSource->onInputBufferEmptied(it->second, -1);
-    (void)mBufferIdsInUse.erase(it);
+    (void)mBufferSource->onInputBufferEmptied(bufferId, -1);
 }
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/C2OMXNode.h b/media/codec2/sfplugin/C2OMXNode.h
index b7bd696..3ca6c0a 100644
--- a/media/codec2/sfplugin/C2OMXNode.h
+++ b/media/codec2/sfplugin/C2OMXNode.h
@@ -20,9 +20,10 @@
 #include <atomic>
 
 #include <android/IOMXBufferSource.h>
+#include <codec2/hidl/client.h>
+#include <media/stagefright/foundation/Mutexed.h>
 #include <media/IOMX.h>
 #include <media/OMXBuffer.h>
-#include <codec2/hidl/client.h>
 
 namespace android {
 
@@ -111,7 +112,7 @@
     c2_cntr64_t mPrevInputTimestamp; // input timestamp for previous frame
     c2_cntr64_t mPrevCodecTimestamp; // adjusted (codec) timestamp for previous frame
 
-    std::map<uint64_t, buffer_id> mBufferIdsInUse;
+    Mutexed<std::map<uint64_t, buffer_id>> mBufferIdsInUse;
 };
 
 }  // namespace android
diff --git a/media/codec2/sfplugin/CCodec.cpp b/media/codec2/sfplugin/CCodec.cpp
index 8474ce8..751c8c5 100644
--- a/media/codec2/sfplugin/CCodec.cpp
+++ b/media/codec2/sfplugin/CCodec.cpp
@@ -600,7 +600,7 @@
     std::shared_ptr<Codec2Client> client;
 
     // set up preferred component store to access vendor store parameters
-    client = Codec2Client::CreateFromService("default", false);
+    client = Codec2Client::CreateFromService("default");
     if (client) {
         ALOGI("setting up '%s' as default (vendor) store", client->getServiceName().c_str());
         SetPreferredCodec2ComponentStore(
@@ -859,11 +859,12 @@
         std::vector<std::unique_ptr<C2Param>> params;
         C2StreamUsageTuning::input usage(0u, 0u);
         C2StreamMaxBufferSizeInfo::input maxInputSize(0u, 0u);
+        C2PrependHeaderModeSetting prepend(PREPEND_HEADER_TO_NONE);
 
         std::initializer_list<C2Param::Index> indices {
         };
         c2_status_t c2err = comp->query(
-                { &usage, &maxInputSize },
+                { &usage, &maxInputSize, &prepend },
                 indices,
                 C2_DONT_BLOCK,
                 &params);
@@ -931,6 +932,16 @@
             }
         }
 
+        int32_t clientPrepend;
+        if ((config->mDomain & Config::IS_VIDEO)
+                && (config->mDomain & Config::IS_ENCODER)
+                && msg->findInt32(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES, &clientPrepend)
+                && clientPrepend
+                && (!prepend || prepend.value != PREPEND_HEADER_TO_ALL_SYNC)) {
+            ALOGE("Failed to set KEY_PREPEND_HEADERS_TO_SYNC_FRAMES");
+            return BAD_VALUE;
+        }
+
         if ((config->mDomain & (Config::IS_VIDEO | Config::IS_IMAGE))) {
             // propagate HDR static info to output format for both encoders and decoders
             // if component supports this info, we will update from component, but only the raw port,
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index d1fa920..00e0c16 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -56,1398 +56,10 @@
 
 using CasStatus = hardware::cas::V1_0::Status;
 
-/**
- * Base class for representation of buffers at one port.
- */
-class CCodecBufferChannel::Buffers {
-public:
-    Buffers(const char *componentName, const char *name = "Buffers")
-        : mComponentName(componentName),
-          mChannelName(std::string(componentName) + ":" + name),
-          mName(mChannelName.c_str()) {
-    }
-    virtual ~Buffers() = default;
-
-    /**
-     * Set format for MediaCodec-facing buffers.
-     */
-    void setFormat(const sp<AMessage> &format) {
-        CHECK(format != nullptr);
-        mFormat = format;
-    }
-
-    /**
-     * Return a copy of current format.
-     */
-    sp<AMessage> dupFormat() {
-        return mFormat != nullptr ? mFormat->dup() : nullptr;
-    }
-
-    /**
-     * Returns true if the buffers are operating under array mode.
-     */
-    virtual bool isArrayMode() const { return false; }
-
-    /**
-     * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
-     * no-op.
-     */
-    virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
-
-    /**
-     * Return number of buffers the client owns.
-     */
-    virtual size_t numClientBuffers() const = 0;
-
-    void handleImageData(const sp<Codec2Buffer> &buffer) {
-        sp<ABuffer> imageDataCandidate = buffer->getImageData();
-        if (imageDataCandidate == nullptr) {
-            return;
-        }
-        sp<ABuffer> imageData;
-        if (!mFormat->findBuffer("image-data", &imageData)
-                || imageDataCandidate->size() != imageData->size()
-                || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) {
-            ALOGD("[%s] updating image-data", mName);
-            sp<AMessage> newFormat = dupFormat();
-            newFormat->setBuffer("image-data", imageDataCandidate);
-            MediaImage2 *img = (MediaImage2*)imageDataCandidate->data();
-            if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) {
-                int32_t stride = img->mPlane[0].mRowInc;
-                newFormat->setInt32(KEY_STRIDE, stride);
-                ALOGD("[%s] updating stride = %d", mName, stride);
-                if (img->mNumPlanes > 1 && stride > 0) {
-                    int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride;
-                    newFormat->setInt32(KEY_SLICE_HEIGHT, vstride);
-                    ALOGD("[%s] updating vstride = %d", mName, vstride);
-                }
-            }
-            setFormat(newFormat);
-            buffer->setFormat(newFormat);
-        }
-    }
-
-protected:
-    std::string mComponentName; ///< name of component for debugging
-    std::string mChannelName; ///< name of channel for debugging
-    const char *mName; ///< C-string version of channel name
-    // Format to be used for creating MediaCodec-facing buffers.
-    sp<AMessage> mFormat;
-
-private:
-    DISALLOW_EVIL_CONSTRUCTORS(Buffers);
-};
-
-class CCodecBufferChannel::InputBuffers : public CCodecBufferChannel::Buffers {
-public:
-    InputBuffers(const char *componentName, const char *name = "Input[]")
-        : Buffers(componentName, name) { }
-    virtual ~InputBuffers() = default;
-
-    /**
-     * Set a block pool to obtain input memory blocks.
-     */
-    void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
-
-    /**
-     * Get a new MediaCodecBuffer for input and its corresponding index.
-     * Returns false if no new buffer can be obtained at the moment.
-     */
-    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
-
-    /**
-     * Release the buffer obtained from requestNewBuffer() and get the
-     * associated C2Buffer object back. Returns true if the buffer was on file
-     * and released successfully.
-     */
-    virtual bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) = 0;
-
-    /**
-     * Release the buffer that is no longer used by the codec process. Return
-     * true if and only if the buffer was on file and released successfully.
-     */
-    virtual bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) = 0;
-
-    /**
-     * Flush internal state. After this call, no index or buffer previously
-     * returned from requestNewBuffer() is valid.
-     */
-    virtual void flush() = 0;
-
-    /**
-     * Return array-backed version of input buffers. The returned object
-     * shall retain the internal state so that it will honor index and
-     * buffer from previous calls of requestNewBuffer().
-     */
-    virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
-
-protected:
-    // Pool to obtain blocks for input buffers.
-    std::shared_ptr<C2BlockPool> mPool;
-
-private:
-    DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
-};
-
-class CCodecBufferChannel::OutputBuffers : public CCodecBufferChannel::Buffers {
-public:
-    OutputBuffers(const char *componentName, const char *name = "Output")
-        : Buffers(componentName, name) { }
-    virtual ~OutputBuffers() = default;
-
-    /**
-     * Register output C2Buffer from the component and obtain corresponding
-     * index and MediaCodecBuffer object. Returns false if registration
-     * fails.
-     */
-    virtual status_t registerBuffer(
-            const std::shared_ptr<C2Buffer> &buffer,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) = 0;
-
-    /**
-     * Register codec specific data as a buffer to be consistent with
-     * MediaCodec behavior.
-     */
-    virtual status_t registerCsd(
-            const C2StreamInitDataInfo::output * /* csd */,
-            size_t * /* index */,
-            sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
-
-    /**
-     * Release the buffer obtained from registerBuffer() and get the
-     * associated C2Buffer object back. Returns true if the buffer was on file
-     * and released successfully.
-     */
-    virtual bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) = 0;
-
-    /**
-     * Flush internal state. After this call, no index or buffer previously
-     * returned from registerBuffer() is valid.
-     */
-    virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
-
-    /**
-     * Return array-backed version of output buffers. The returned object
-     * shall retain the internal state so that it will honor index and
-     * buffer from previous calls of registerBuffer().
-     */
-    virtual std::unique_ptr<OutputBuffers> toArrayMode(size_t size) = 0;
-
-    /**
-     * Initialize SkipCutBuffer object.
-     */
-    void initSkipCutBuffer(
-            int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) {
-        CHECK(mSkipCutBuffer == nullptr);
-        mDelay = delay;
-        mPadding = padding;
-        mSampleRate = sampleRate;
-        setSkipCutBuffer(delay, padding, channelCount);
-    }
-
-    /**
-     * Update the SkipCutBuffer object. No-op if it's never initialized.
-     */
-    void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) {
-        if (mSkipCutBuffer == nullptr) {
-            return;
-        }
-        int32_t delay = mDelay;
-        int32_t padding = mPadding;
-        if (sampleRate != mSampleRate) {
-            delay = ((int64_t)delay * sampleRate) / mSampleRate;
-            padding = ((int64_t)padding * sampleRate) / mSampleRate;
-        }
-        setSkipCutBuffer(delay, padding, channelCount);
-    }
-
-    /**
-     * Submit buffer to SkipCutBuffer object, if initialized.
-     */
-    void submit(const sp<MediaCodecBuffer> &buffer) {
-        if (mSkipCutBuffer != nullptr) {
-            mSkipCutBuffer->submit(buffer);
-        }
-    }
-
-    /**
-     * Transfer SkipCutBuffer object to the other Buffers object.
-     */
-    void transferSkipCutBuffer(const sp<SkipCutBuffer> &scb) {
-        mSkipCutBuffer = scb;
-    }
-
-protected:
-    sp<SkipCutBuffer> mSkipCutBuffer;
-
-private:
-    int32_t mDelay;
-    int32_t mPadding;
-    int32_t mSampleRate;
-
-    void setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount) {
-        if (mSkipCutBuffer != nullptr) {
-            size_t prevSize = mSkipCutBuffer->size();
-            if (prevSize != 0u) {
-                ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
-            }
-        }
-        mSkipCutBuffer = new SkipCutBuffer(skip, cut, channelCount);
-    }
-
-    DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
-};
-
 namespace {
 
-const static size_t kSmoothnessFactor = 4;
-const static size_t kRenderingDepth = 3;
-const static size_t kLinearBufferSize = 1048576;
-// This can fit 4K RGBA frame, and most likely client won't need more than this.
-const static size_t kMaxLinearBufferSize = 3840 * 2160 * 4;
-
-/**
- * Simple local buffer pool backed by std::vector.
- */
-class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
-public:
-    /**
-     * Create a new LocalBufferPool object.
-     *
-     * \param poolCapacity  max total size of buffers managed by this pool.
-     *
-     * \return  a newly created pool object.
-     */
-    static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity) {
-        return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
-    }
-
-    /**
-     * Return an ABuffer object whose size is at least |capacity|.
-     *
-     * \param   capacity  requested capacity
-     * \return  nullptr if the pool capacity is reached
-     *          an ABuffer object otherwise.
-     */
-    sp<ABuffer> newBuffer(size_t capacity) {
-        Mutex::Autolock lock(mMutex);
-        auto it = std::find_if(
-                mPool.begin(), mPool.end(),
-                [capacity](const std::vector<uint8_t> &vec) {
-                    return vec.capacity() >= capacity;
-                });
-        if (it != mPool.end()) {
-            sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
-            mPool.erase(it);
-            return buffer;
-        }
-        if (mUsedSize + capacity > mPoolCapacity) {
-            while (!mPool.empty()) {
-                mUsedSize -= mPool.back().capacity();
-                mPool.pop_back();
-            }
-            if (mUsedSize + capacity > mPoolCapacity) {
-                ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
-                        mUsedSize, capacity, mPoolCapacity);
-                return nullptr;
-            }
-        }
-        std::vector<uint8_t> vec(capacity);
-        mUsedSize += vec.capacity();
-        return new VectorBuffer(std::move(vec), shared_from_this());
-    }
-
-private:
-    /**
-     * ABuffer backed by std::vector.
-     */
-    class VectorBuffer : public ::android::ABuffer {
-    public:
-        /**
-         * Construct a VectorBuffer by taking the ownership of supplied vector.
-         *
-         * \param vec   backing vector of the buffer. this object takes
-         *              ownership at construction.
-         * \param pool  a LocalBufferPool object to return the vector at
-         *              destruction.
-         */
-        VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
-            : ABuffer(vec.data(), vec.capacity()),
-              mVec(std::move(vec)),
-              mPool(pool) {
-        }
-
-        ~VectorBuffer() override {
-            std::shared_ptr<LocalBufferPool> pool = mPool.lock();
-            if (pool) {
-                // If pool is alive, return the vector back to the pool so that
-                // it can be recycled.
-                pool->returnVector(std::move(mVec));
-            }
-        }
-
-    private:
-        std::vector<uint8_t> mVec;
-        std::weak_ptr<LocalBufferPool> mPool;
-    };
-
-    Mutex mMutex;
-    size_t mPoolCapacity;
-    size_t mUsedSize;
-    std::list<std::vector<uint8_t>> mPool;
-
-    /**
-     * Private constructor to prevent constructing non-managed LocalBufferPool.
-     */
-    explicit LocalBufferPool(size_t poolCapacity)
-        : mPoolCapacity(poolCapacity), mUsedSize(0) {
-    }
-
-    /**
-     * Take back the ownership of vec from the destructed VectorBuffer and put
-     * it in front of the pool.
-     */
-    void returnVector(std::vector<uint8_t> &&vec) {
-        Mutex::Autolock lock(mMutex);
-        mPool.push_front(std::move(vec));
-    }
-
-    DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
-};
-
-sp<GraphicBlockBuffer> AllocateGraphicBuffer(
-        const std::shared_ptr<C2BlockPool> &pool,
-        const sp<AMessage> &format,
-        uint32_t pixelFormat,
-        const C2MemoryUsage &usage,
-        const std::shared_ptr<LocalBufferPool> &localBufferPool) {
-    int32_t width, height;
-    if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
-        ALOGD("format lacks width or height");
-        return nullptr;
-    }
-
-    std::shared_ptr<C2GraphicBlock> block;
-    c2_status_t err = pool->fetchGraphicBlock(
-            width, height, pixelFormat, usage, &block);
-    if (err != C2_OK) {
-        ALOGD("fetch graphic block failed: %d", err);
-        return nullptr;
-    }
-
-    return GraphicBlockBuffer::Allocate(
-            format,
-            block,
-            [localBufferPool](size_t capacity) {
-                return localBufferPool->newBuffer(capacity);
-            });
-}
-
-class BuffersArrayImpl;
-
-/**
- * Flexible buffer slots implementation.
- */
-class FlexBuffersImpl {
-public:
-    FlexBuffersImpl(const char *name)
-        : mImplName(std::string(name) + ".Impl"),
-          mName(mImplName.c_str()) { }
-
-    /**
-     * Assign an empty slot for a buffer and return the index. If there's no
-     * empty slot, just add one at the end and return it.
-     *
-     * \param buffer[in]  a new buffer to assign a slot.
-     * \return            index of the assigned slot.
-     */
-    size_t assignSlot(const sp<Codec2Buffer> &buffer) {
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (mBuffers[i].clientBuffer == nullptr
-                    && mBuffers[i].compBuffer.expired()) {
-                mBuffers[i].clientBuffer = buffer;
-                return i;
-            }
-        }
-        mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
-        return mBuffers.size() - 1;
-    }
-
-    /**
-     * Release the slot from the client, and get the C2Buffer object back from
-     * the previously assigned buffer. Note that the slot is not completely free
-     * until the returned C2Buffer object is freed.
-     *
-     * \param   buffer[in]        the buffer previously assigned a slot.
-     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
-     *                            if null.
-     * \return  true  if the buffer is successfully released from a slot
-     *          false otherwise
-     */
-    bool releaseSlot(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) {
-        sp<Codec2Buffer> clientBuffer;
-        size_t index = mBuffers.size();
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (mBuffers[i].clientBuffer == buffer) {
-                clientBuffer = mBuffers[i].clientBuffer;
-                if (release) {
-                    mBuffers[i].clientBuffer.clear();
-                }
-                index = i;
-                break;
-            }
-        }
-        if (clientBuffer == nullptr) {
-            ALOGV("[%s] %s: No matching buffer found", mName, __func__);
-            return false;
-        }
-        std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
-        if (!result) {
-            result = clientBuffer->asC2Buffer();
-            mBuffers[index].compBuffer = result;
-        }
-        if (c2buffer) {
-            *c2buffer = result;
-        }
-        return true;
-    }
-
-    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            std::shared_ptr<C2Buffer> compBuffer =
-                    mBuffers[i].compBuffer.lock();
-            if (!compBuffer || compBuffer != c2buffer) {
-                continue;
-            }
-            mBuffers[i].compBuffer.reset();
-            ALOGV("[%s] codec released buffer #%zu", mName, i);
-            return true;
-        }
-        ALOGV("[%s] codec released an unknown buffer", mName);
-        return false;
-    }
-
-    void flush() {
-        ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size());
-        mBuffers.clear();
-    }
-
-    size_t numClientBuffers() const {
-        return std::count_if(
-                mBuffers.begin(), mBuffers.end(),
-                [](const Entry &entry) {
-                    return (entry.clientBuffer != nullptr);
-                });
-    }
-
-private:
-    friend class BuffersArrayImpl;
-
-    std::string mImplName; ///< name for debugging
-    const char *mName; ///< C-string version of name
-
-    struct Entry {
-        sp<Codec2Buffer> clientBuffer;
-        std::weak_ptr<C2Buffer> compBuffer;
-    };
-    std::vector<Entry> mBuffers;
-};
-
-/**
- * Static buffer slots implementation based on a fixed-size array.
- */
-class BuffersArrayImpl {
-public:
-    BuffersArrayImpl()
-        : mImplName("BuffersArrayImpl"),
-          mName(mImplName.c_str()) { }
-
-    /**
-     * Initialize buffer array from the original |impl|. The buffers known by
-     * the client is preserved, and the empty slots are populated so that the
-     * array size is at least |minSize|.
-     *
-     * \param impl[in]      FlexBuffersImpl object used so far.
-     * \param minSize[in]   minimum size of the buffer array.
-     * \param allocate[in]  function to allocate a client buffer for an empty slot.
-     */
-    void initialize(
-            const FlexBuffersImpl &impl,
-            size_t minSize,
-            std::function<sp<Codec2Buffer>()> allocate) {
-        mImplName = impl.mImplName + "[N]";
-        mName = mImplName.c_str();
-        for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
-            sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer;
-            bool ownedByClient = (clientBuffer != nullptr);
-            if (!ownedByClient) {
-                clientBuffer = allocate();
-            }
-            mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
-        }
-        ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize);
-        for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
-            mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
-        }
-    }
-
-    /**
-     * Grab a buffer from the underlying array which matches the criteria.
-     *
-     * \param index[out]    index of the slot.
-     * \param buffer[out]   the matching buffer.
-     * \param match[in]     a function to test whether the buffer matches the
-     *                      criteria or not.
-     * \return OK           if successful,
-     *         WOULD_BLOCK  if slots are being used,
-     *         NO_MEMORY    if no slot matches the criteria, even though it's
-     *                      available
-     */
-    status_t grabBuffer(
-            size_t *index,
-            sp<Codec2Buffer> *buffer,
-            std::function<bool(const sp<Codec2Buffer> &)> match =
-                [](const sp<Codec2Buffer> &) { return true; }) {
-        // allBuffersDontMatch remains true if all buffers are available but
-        // match() returns false for every buffer.
-        bool allBuffersDontMatch = true;
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()) {
-                if (match(mBuffers[i].clientBuffer)) {
-                    mBuffers[i].ownedByClient = true;
-                    *buffer = mBuffers[i].clientBuffer;
-                    (*buffer)->meta()->clear();
-                    (*buffer)->setRange(0, (*buffer)->capacity());
-                    *index = i;
-                    return OK;
-                }
-            } else {
-                allBuffersDontMatch = false;
-            }
-        }
-        return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK;
-    }
-
-    /**
-     * Return the buffer from the client, and get the C2Buffer object back from
-     * the buffer. Note that the slot is not completely free until the returned
-     * C2Buffer object is freed.
-     *
-     * \param   buffer[in]        the buffer previously grabbed.
-     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
-     *                            if null.
-     * \return  true  if the buffer is successfully returned
-     *          false otherwise
-     */
-    bool returnBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) {
-        sp<Codec2Buffer> clientBuffer;
-        size_t index = mBuffers.size();
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            if (mBuffers[i].clientBuffer == buffer) {
-                if (!mBuffers[i].ownedByClient) {
-                    ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu", mName, i);
-                }
-                clientBuffer = mBuffers[i].clientBuffer;
-                if (release) {
-                    mBuffers[i].ownedByClient = false;
-                }
-                index = i;
-                break;
-            }
-        }
-        if (clientBuffer == nullptr) {
-            ALOGV("[%s] %s: No matching buffer found", mName, __func__);
-            return false;
-        }
-        ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index);
-        std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
-        if (!result) {
-            result = clientBuffer->asC2Buffer();
-            mBuffers[index].compBuffer = result;
-        }
-        if (c2buffer) {
-            *c2buffer = result;
-        }
-        return true;
-    }
-
-    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
-        for (size_t i = 0; i < mBuffers.size(); ++i) {
-            std::shared_ptr<C2Buffer> compBuffer =
-                    mBuffers[i].compBuffer.lock();
-            if (!compBuffer) {
-                continue;
-            }
-            if (c2buffer == compBuffer) {
-                if (mBuffers[i].ownedByClient) {
-                    // This should not happen.
-                    ALOGD("[%s] codec released a buffer owned by client "
-                          "(index %zu)", mName, i);
-                }
-                mBuffers[i].compBuffer.reset();
-                ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i);
-                return true;
-            }
-        }
-        ALOGV("[%s] codec released an unknown buffer (array mode)", mName);
-        return false;
-    }
-
-    /**
-     * Populate |array| with the underlying buffer array.
-     *
-     * \param array[out]  an array to be filled with the underlying buffer array.
-     */
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const {
-        array->clear();
-        for (const Entry &entry : mBuffers) {
-            array->push(entry.clientBuffer);
-        }
-    }
-
-    /**
-     * The client abandoned all known buffers, so reclaim the ownership.
-     */
-    void flush() {
-        for (Entry &entry : mBuffers) {
-            entry.ownedByClient = false;
-        }
-    }
-
-    void realloc(std::function<sp<Codec2Buffer>()> alloc) {
-        size_t size = mBuffers.size();
-        mBuffers.clear();
-        for (size_t i = 0; i < size; ++i) {
-            mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
-        }
-    }
-
-    size_t numClientBuffers() const {
-        return std::count_if(
-                mBuffers.begin(), mBuffers.end(),
-                [](const Entry &entry) {
-                    return entry.ownedByClient;
-                });
-    }
-
-private:
-    std::string mImplName; ///< name for debugging
-    const char *mName; ///< C-string version of name
-
-    struct Entry {
-        const sp<Codec2Buffer> clientBuffer;
-        std::weak_ptr<C2Buffer> compBuffer;
-        bool ownedByClient;
-    };
-    std::vector<Entry> mBuffers;
-};
-
-class InputBuffersArray : public CCodecBufferChannel::InputBuffers {
-public:
-    InputBuffersArray(const char *componentName, const char *name = "Input[N]")
-        : InputBuffers(componentName, name) { }
-    ~InputBuffersArray() override = default;
-
-    void initialize(
-            const FlexBuffersImpl &impl,
-            size_t minSize,
-            std::function<sp<Codec2Buffer>()> allocate) {
-        mImpl.initialize(impl, minSize, allocate);
-    }
-
-    bool isArrayMode() const final { return true; }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t) final {
-        return nullptr;
-    }
-
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
-        mImpl.getArray(array);
-    }
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        sp<Codec2Buffer> c2Buffer;
-        status_t err = mImpl.grabBuffer(index, &c2Buffer);
-        if (err == OK) {
-            c2Buffer->setFormat(mFormat);
-            handleImageData(c2Buffer);
-            *buffer = c2Buffer;
-            return true;
-        }
-        return false;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.returnBuffer(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        mImpl.flush();
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    BuffersArrayImpl mImpl;
-};
-
-class LinearInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    LinearInputBuffers(const char *componentName, const char *name = "1D-Input")
-        : InputBuffers(componentName, name),
-          mImpl(mName) { }
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        int32_t capacity = kLinearBufferSize;
-        (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
-        if ((size_t)capacity > kMaxLinearBufferSize) {
-            ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
-            capacity = kMaxLinearBufferSize;
-        }
-        // TODO: proper max input size
-        // TODO: read usage from intf
-        sp<Codec2Buffer> newBuffer = alloc((size_t)capacity);
-        if (newBuffer == nullptr) {
-            return false;
-        }
-        *index = mImpl.assignSlot(newBuffer);
-        *buffer = newBuffer;
-        return true;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.releaseSlot(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-        mImpl.flush();
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t size) final {
-        int32_t capacity = kLinearBufferSize;
-        (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
-        if ((size_t)capacity > kMaxLinearBufferSize) {
-            ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
-            capacity = kMaxLinearBufferSize;
-        }
-        // TODO: proper max input size
-        // TODO: read usage from intf
-        std::unique_ptr<InputBuffersArray> array(
-                new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
-        array->setPool(mPool);
-        array->setFormat(mFormat);
-        array->initialize(
-                mImpl,
-                size,
-                [this, capacity] () -> sp<Codec2Buffer> { return alloc(capacity); });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-    virtual sp<Codec2Buffer> alloc(size_t size) {
-        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        std::shared_ptr<C2LinearBlock> block;
-
-        c2_status_t err = mPool->fetchLinearBlock(size, usage, &block);
-        if (err != C2_OK) {
-            return nullptr;
-        }
-
-        return LinearBlockBuffer::Allocate(mFormat, block);
-    }
-
-private:
-    FlexBuffersImpl mImpl;
-};
-
-class EncryptedLinearInputBuffers : public LinearInputBuffers {
-public:
-    EncryptedLinearInputBuffers(
-            bool secure,
-            const sp<MemoryDealer> &dealer,
-            const sp<ICrypto> &crypto,
-            int32_t heapSeqNum,
-            size_t capacity,
-            size_t numInputSlots,
-            const char *componentName, const char *name = "EncryptedInput")
-        : LinearInputBuffers(componentName, name),
-          mUsage({0, 0}),
-          mDealer(dealer),
-          mCrypto(crypto),
-          mHeapSeqNum(heapSeqNum) {
-        if (secure) {
-            mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
-        } else {
-            mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        }
-        for (size_t i = 0; i < numInputSlots; ++i) {
-            sp<IMemory> memory = mDealer->allocate(capacity);
-            if (memory == nullptr) {
-                ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated", mName, i);
-                break;
-            }
-            mMemoryVector.push_back({std::weak_ptr<C2LinearBlock>(), memory});
-        }
-    }
-
-    ~EncryptedLinearInputBuffers() override {
-    }
-
-    sp<Codec2Buffer> alloc(size_t size) override {
-        sp<IMemory> memory;
-        size_t slot = 0;
-        for (; slot < mMemoryVector.size(); ++slot) {
-            if (mMemoryVector[slot].block.expired()) {
-                memory = mMemoryVector[slot].memory;
-                break;
-            }
-        }
-        if (memory == nullptr) {
-            return nullptr;
-        }
-
-        std::shared_ptr<C2LinearBlock> block;
-        c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
-        if (err != C2_OK || block == nullptr) {
-            return nullptr;
-        }
-
-        mMemoryVector[slot].block = block;
-        return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
-    }
-
-private:
-    C2MemoryUsage mUsage;
-    sp<MemoryDealer> mDealer;
-    sp<ICrypto> mCrypto;
-    int32_t mHeapSeqNum;
-    struct Entry {
-        std::weak_ptr<C2LinearBlock> block;
-        sp<IMemory> memory;
-    };
-    std::vector<Entry> mMemoryVector;
-};
-
-class GraphicMetadataInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    GraphicMetadataInputBuffers(const char *componentName, const char *name = "2D-MetaInput")
-        : InputBuffers(componentName, name),
-          mImpl(mName),
-          mStore(GetCodec2PlatformAllocatorStore()) { }
-    ~GraphicMetadataInputBuffers() override = default;
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        std::shared_ptr<C2Allocator> alloc;
-        c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
-        if (err != C2_OK) {
-            return false;
-        }
-        sp<GraphicMetadataBuffer> newBuffer = new GraphicMetadataBuffer(mFormat, alloc);
-        if (newBuffer == nullptr) {
-            return false;
-        }
-        *index = mImpl.assignSlot(newBuffer);
-        *buffer = newBuffer;
-        return true;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.releaseSlot(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t size) final {
-        std::shared_ptr<C2Allocator> alloc;
-        c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
-        if (err != C2_OK) {
-            return nullptr;
-        }
-        std::unique_ptr<InputBuffersArray> array(
-                new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]"));
-        array->setPool(mPool);
-        array->setFormat(mFormat);
-        array->initialize(
-                mImpl,
-                size,
-                [format = mFormat, alloc]() -> sp<Codec2Buffer> {
-                    return new GraphicMetadataBuffer(format, alloc);
-                });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    FlexBuffersImpl mImpl;
-    std::shared_ptr<C2AllocatorStore> mStore;
-};
-
-class GraphicInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    GraphicInputBuffers(
-            size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input")
-        : InputBuffers(componentName, name),
-          mImpl(mName),
-          mLocalBufferPool(LocalBufferPool::Create(
-                  kMaxLinearBufferSize * numInputSlots)) { }
-    ~GraphicInputBuffers() override = default;
-
-    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override {
-        // TODO: proper max input size
-        // TODO: read usage from intf
-        C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-        sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
-                mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
-        if (newBuffer == nullptr) {
-            return false;
-        }
-        *index = mImpl.assignSlot(newBuffer);
-        handleImageData(newBuffer);
-        *buffer = newBuffer;
-        return true;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer,
-            bool release) override {
-        return mImpl.releaseSlot(buffer, c2buffer, release);
-    }
-
-    bool expireComponentBuffer(
-            const std::shared_ptr<C2Buffer> &c2buffer) override {
-        return mImpl.expireComponentBuffer(c2buffer);
-    }
-
-    void flush() override {
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t size) final {
-        std::unique_ptr<InputBuffersArray> array(
-                new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]"));
-        array->setPool(mPool);
-        array->setFormat(mFormat);
-        array->initialize(
-                mImpl,
-                size,
-                [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
-                    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
-                    return AllocateGraphicBuffer(
-                            pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
-                });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    FlexBuffersImpl mImpl;
-    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
-};
-
-class DummyInputBuffers : public CCodecBufferChannel::InputBuffers {
-public:
-    DummyInputBuffers(const char *componentName, const char *name = "2D-Input")
-        : InputBuffers(componentName, name) { }
-
-    bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
-        return false;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &, std::shared_ptr<C2Buffer> *, bool) override {
-        return false;
-    }
-
-    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &) override {
-        return false;
-    }
-    void flush() override {
-    }
-
-    std::unique_ptr<CCodecBufferChannel::InputBuffers> toArrayMode(
-            size_t) final {
-        return nullptr;
-    }
-
-    bool isArrayMode() const final { return true; }
-
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
-        array->clear();
-    }
-
-    size_t numClientBuffers() const final {
-        return 0u;
-    }
-};
-
-class OutputBuffersArray : public CCodecBufferChannel::OutputBuffers {
-public:
-    OutputBuffersArray(const char *componentName, const char *name = "Output[N]")
-        : OutputBuffers(componentName, name) { }
-    ~OutputBuffersArray() override = default;
-
-    void initialize(
-            const FlexBuffersImpl &impl,
-            size_t minSize,
-            std::function<sp<Codec2Buffer>()> allocate) {
-        mImpl.initialize(impl, minSize, allocate);
-    }
-
-    bool isArrayMode() const final { return true; }
-
-    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode(
-            size_t) final {
-        return nullptr;
-    }
-
-    status_t registerBuffer(
-            const std::shared_ptr<C2Buffer> &buffer,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) final {
-        sp<Codec2Buffer> c2Buffer;
-        status_t err = mImpl.grabBuffer(
-                index,
-                &c2Buffer,
-                [buffer](const sp<Codec2Buffer> &clientBuffer) {
-                    return clientBuffer->canCopy(buffer);
-                });
-        if (err == WOULD_BLOCK) {
-            ALOGV("[%s] buffers temporarily not available", mName);
-            return err;
-        } else if (err != OK) {
-            ALOGD("[%s] grabBuffer failed: %d", mName, err);
-            return err;
-        }
-        c2Buffer->setFormat(mFormat);
-        if (!c2Buffer->copy(buffer)) {
-            ALOGD("[%s] copy buffer failed", mName);
-            return WOULD_BLOCK;
-        }
-        submit(c2Buffer);
-        handleImageData(c2Buffer);
-        *clientBuffer = c2Buffer;
-        ALOGV("[%s] grabbed buffer %zu", mName, *index);
-        return OK;
-    }
-
-    status_t registerCsd(
-            const C2StreamInitDataInfo::output *csd,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) final {
-        sp<Codec2Buffer> c2Buffer;
-        status_t err = mImpl.grabBuffer(
-                index,
-                &c2Buffer,
-                [csd](const sp<Codec2Buffer> &clientBuffer) {
-                    return clientBuffer->base() != nullptr
-                            && clientBuffer->capacity() >= csd->flexCount();
-                });
-        if (err != OK) {
-            return err;
-        }
-        memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
-        c2Buffer->setRange(0, csd->flexCount());
-        c2Buffer->setFormat(mFormat);
-        *clientBuffer = c2Buffer;
-        return OK;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.returnBuffer(buffer, c2buffer, true);
-    }
-
-    void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
-        (void)flushedWork;
-        mImpl.flush();
-        if (mSkipCutBuffer != nullptr) {
-            mSkipCutBuffer->clear();
-        }
-    }
-
-    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
-        mImpl.getArray(array);
-    }
-
-    void realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
-        std::function<sp<Codec2Buffer>()> alloc;
-        switch (c2buffer->data().type()) {
-            case C2BufferData::LINEAR: {
-                uint32_t size = kLinearBufferSize;
-                const C2ConstLinearBlock &block = c2buffer->data().linearBlocks().front();
-                if (block.size() < kMaxLinearBufferSize / 2) {
-                    size = block.size() * 2;
-                } else {
-                    size = kMaxLinearBufferSize;
-                }
-                alloc = [format = mFormat, size] {
-                    return new LocalLinearBuffer(format, new ABuffer(size));
-                };
-                break;
-            }
-
-            // TODO: add support
-            case C2BufferData::GRAPHIC:         FALLTHROUGH_INTENDED;
-
-            case C2BufferData::INVALID:         FALLTHROUGH_INTENDED;
-            case C2BufferData::LINEAR_CHUNKS:   FALLTHROUGH_INTENDED;
-            case C2BufferData::GRAPHIC_CHUNKS:  FALLTHROUGH_INTENDED;
-            default:
-                ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
-                return;
-        }
-        mImpl.realloc(alloc);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-private:
-    BuffersArrayImpl mImpl;
-};
-
-class FlexOutputBuffers : public CCodecBufferChannel::OutputBuffers {
-public:
-    FlexOutputBuffers(const char *componentName, const char *name = "Output[]")
-        : OutputBuffers(componentName, name),
-          mImpl(mName) { }
-
-    status_t registerBuffer(
-            const std::shared_ptr<C2Buffer> &buffer,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) override {
-        sp<Codec2Buffer> newBuffer = wrap(buffer);
-        if (newBuffer == nullptr) {
-            return NO_MEMORY;
-        }
-        newBuffer->setFormat(mFormat);
-        *index = mImpl.assignSlot(newBuffer);
-        handleImageData(newBuffer);
-        *clientBuffer = newBuffer;
-        ALOGV("[%s] registered buffer %zu", mName, *index);
-        return OK;
-    }
-
-    status_t registerCsd(
-            const C2StreamInitDataInfo::output *csd,
-            size_t *index,
-            sp<MediaCodecBuffer> *clientBuffer) final {
-        sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(
-                mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
-        *index = mImpl.assignSlot(newBuffer);
-        *clientBuffer = newBuffer;
-        return OK;
-    }
-
-    bool releaseBuffer(
-            const sp<MediaCodecBuffer> &buffer,
-            std::shared_ptr<C2Buffer> *c2buffer) override {
-        return mImpl.releaseSlot(buffer, c2buffer, true);
-    }
-
-    void flush(
-            const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
-        (void) flushedWork;
-        // This is no-op by default unless we're in array mode where we need to keep
-        // track of the flushed work.
-    }
-
-    std::unique_ptr<CCodecBufferChannel::OutputBuffers> toArrayMode(
-            size_t size) override {
-        std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
-        array->setFormat(mFormat);
-        array->transferSkipCutBuffer(mSkipCutBuffer);
-        array->initialize(
-                mImpl,
-                size,
-                [this]() { return allocateArrayBuffer(); });
-        return std::move(array);
-    }
-
-    size_t numClientBuffers() const final {
-        return mImpl.numClientBuffers();
-    }
-
-    /**
-     * Return an appropriate Codec2Buffer object for the type of buffers.
-     *
-     * \param buffer  C2Buffer object to wrap.
-     *
-     * \return  appropriate Codec2Buffer object to wrap |buffer|.
-     */
-    virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
-
-    /**
-     * Return an appropriate Codec2Buffer object for the type of buffers, to be
-     * used as an empty array buffer.
-     *
-     * \return  appropriate Codec2Buffer object which can copy() from C2Buffers.
-     */
-    virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
-
-private:
-    FlexBuffersImpl mImpl;
-};
-
-class LinearOutputBuffers : public FlexOutputBuffers {
-public:
-    LinearOutputBuffers(const char *componentName, const char *name = "1D-Output")
-        : FlexOutputBuffers(componentName, name) { }
-
-    void flush(
-            const std::list<std::unique_ptr<C2Work>> &flushedWork) override {
-        if (mSkipCutBuffer != nullptr) {
-            mSkipCutBuffer->clear();
-        }
-        FlexOutputBuffers::flush(flushedWork);
-    }
-
-    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
-        if (buffer == nullptr) {
-            ALOGV("[%s] using a dummy buffer", mName);
-            return new LocalLinearBuffer(mFormat, new ABuffer(0));
-        }
-        if (buffer->data().type() != C2BufferData::LINEAR) {
-            ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type());
-            // We expect linear output buffers from the component.
-            return nullptr;
-        }
-        if (buffer->data().linearBlocks().size() != 1u) {
-            ALOGV("[%s] no linear buffers", mName);
-            // We expect one and only one linear block from the component.
-            return nullptr;
-        }
-        sp<Codec2Buffer> clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer);
-        if (clientBuffer == nullptr) {
-            ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName);
-            return nullptr;
-        }
-        submit(clientBuffer);
-        return clientBuffer;
-    }
-
-    sp<Codec2Buffer> allocateArrayBuffer() override {
-        // TODO: proper max output size
-        return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
-    }
-};
-
-class GraphicOutputBuffers : public FlexOutputBuffers {
-public:
-    GraphicOutputBuffers(const char *componentName, const char *name = "2D-Output")
-        : FlexOutputBuffers(componentName, name) { }
-
-    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
-        return new DummyContainerBuffer(mFormat, buffer);
-    }
-
-    sp<Codec2Buffer> allocateArrayBuffer() override {
-        return new DummyContainerBuffer(mFormat);
-    }
-};
-
-class RawGraphicOutputBuffers : public FlexOutputBuffers {
-public:
-    RawGraphicOutputBuffers(
-            size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output")
-        : FlexOutputBuffers(componentName, name),
-          mLocalBufferPool(LocalBufferPool::Create(
-                  kMaxLinearBufferSize * numOutputSlots)) { }
-    ~RawGraphicOutputBuffers() override = default;
-
-    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override {
-        if (buffer == nullptr) {
-            sp<Codec2Buffer> c2buffer = ConstGraphicBlockBuffer::AllocateEmpty(
-                    mFormat,
-                    [lbp = mLocalBufferPool](size_t capacity) {
-                        return lbp->newBuffer(capacity);
-                    });
-            if (c2buffer == nullptr) {
-                ALOGD("[%s] ConstGraphicBlockBuffer::AllocateEmpty failed", mName);
-                return nullptr;
-            }
-            c2buffer->setRange(0, 0);
-            return c2buffer;
-        } else {
-            return ConstGraphicBlockBuffer::Allocate(
-                    mFormat,
-                    buffer,
-                    [lbp = mLocalBufferPool](size_t capacity) {
-                        return lbp->newBuffer(capacity);
-                    });
-        }
-    }
-
-    sp<Codec2Buffer> allocateArrayBuffer() override {
-        return ConstGraphicBlockBuffer::AllocateEmpty(
-                mFormat,
-                [lbp = mLocalBufferPool](size_t capacity) {
-                    return lbp->newBuffer(capacity);
-                });
-    }
-
-private:
-    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
-};
+constexpr size_t kSmoothnessFactor = 4;
+constexpr size_t kRenderingDepth = 3;
 
 }  // namespace
 
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.h b/media/codec2/sfplugin/CCodecBufferChannel.h
index 1ea29b4..b7a8445 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.h
+++ b/media/codec2/sfplugin/CCodecBufferChannel.h
@@ -33,6 +33,7 @@
 #include <media/stagefright/CodecBase.h>
 #include <media/ICrypto.h>
 
+#include "CCodecBuffers.h"
 #include "InputSurfaceWrapper.h"
 #include "PipelineWatcher.h"
 
@@ -151,11 +152,6 @@
 
     void setMetaMode(MetaMode mode);
 
-    // Internal classes
-    class Buffers;
-    class InputBuffers;
-    class OutputBuffers;
-
 private:
     class QueueGuard;
 
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
new file mode 100644
index 0000000..fb0efce
--- /dev/null
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -0,0 +1,963 @@
+/*
+ * Copyright 2019, 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 "CCodecBuffers"
+#include <utils/Log.h>
+
+#include <C2PlatformSupport.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaCodecConstants.h>
+
+#include "CCodecBuffers.h"
+
+namespace android {
+
+namespace {
+
+sp<GraphicBlockBuffer> AllocateGraphicBuffer(
+        const std::shared_ptr<C2BlockPool> &pool,
+        const sp<AMessage> &format,
+        uint32_t pixelFormat,
+        const C2MemoryUsage &usage,
+        const std::shared_ptr<LocalBufferPool> &localBufferPool) {
+    int32_t width, height;
+    if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
+        ALOGD("format lacks width or height");
+        return nullptr;
+    }
+
+    std::shared_ptr<C2GraphicBlock> block;
+    c2_status_t err = pool->fetchGraphicBlock(
+            width, height, pixelFormat, usage, &block);
+    if (err != C2_OK) {
+        ALOGD("fetch graphic block failed: %d", err);
+        return nullptr;
+    }
+
+    return GraphicBlockBuffer::Allocate(
+            format,
+            block,
+            [localBufferPool](size_t capacity) {
+                return localBufferPool->newBuffer(capacity);
+            });
+}
+
+}  // namespace
+
+// CCodecBuffers
+
+void CCodecBuffers::setFormat(const sp<AMessage> &format) {
+    CHECK(format != nullptr);
+    mFormat = format;
+}
+
+sp<AMessage> CCodecBuffers::dupFormat() {
+    return mFormat != nullptr ? mFormat->dup() : nullptr;
+}
+
+void CCodecBuffers::handleImageData(const sp<Codec2Buffer> &buffer) {
+    sp<ABuffer> imageDataCandidate = buffer->getImageData();
+    if (imageDataCandidate == nullptr) {
+        return;
+    }
+    sp<ABuffer> imageData;
+    if (!mFormat->findBuffer("image-data", &imageData)
+            || imageDataCandidate->size() != imageData->size()
+            || memcmp(imageDataCandidate->data(), imageData->data(), imageData->size()) != 0) {
+        ALOGD("[%s] updating image-data", mName);
+        sp<AMessage> newFormat = dupFormat();
+        newFormat->setBuffer("image-data", imageDataCandidate);
+        MediaImage2 *img = (MediaImage2*)imageDataCandidate->data();
+        if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) {
+            int32_t stride = img->mPlane[0].mRowInc;
+            newFormat->setInt32(KEY_STRIDE, stride);
+            ALOGD("[%s] updating stride = %d", mName, stride);
+            if (img->mNumPlanes > 1 && stride > 0) {
+                int32_t vstride = (img->mPlane[1].mOffset - img->mPlane[0].mOffset) / stride;
+                newFormat->setInt32(KEY_SLICE_HEIGHT, vstride);
+                ALOGD("[%s] updating vstride = %d", mName, vstride);
+            }
+        }
+        setFormat(newFormat);
+        buffer->setFormat(newFormat);
+    }
+}
+
+// OutputBuffers
+
+void OutputBuffers::initSkipCutBuffer(
+        int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) {
+    CHECK(mSkipCutBuffer == nullptr);
+    mDelay = delay;
+    mPadding = padding;
+    mSampleRate = sampleRate;
+    setSkipCutBuffer(delay, padding, channelCount);
+}
+
+void OutputBuffers::updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) {
+    if (mSkipCutBuffer == nullptr) {
+        return;
+    }
+    int32_t delay = mDelay;
+    int32_t padding = mPadding;
+    if (sampleRate != mSampleRate) {
+        delay = ((int64_t)delay * sampleRate) / mSampleRate;
+        padding = ((int64_t)padding * sampleRate) / mSampleRate;
+    }
+    setSkipCutBuffer(delay, padding, channelCount);
+}
+
+void OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer) {
+    if (mSkipCutBuffer != nullptr) {
+        mSkipCutBuffer->submit(buffer);
+    }
+}
+
+void OutputBuffers::transferSkipCutBuffer(const sp<SkipCutBuffer> &scb) {
+    mSkipCutBuffer = scb;
+}
+
+void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount) {
+    if (mSkipCutBuffer != nullptr) {
+        size_t prevSize = mSkipCutBuffer->size();
+        if (prevSize != 0u) {
+            ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
+        }
+    }
+    mSkipCutBuffer = new SkipCutBuffer(skip, cut, channelCount);
+}
+
+// LocalBufferPool
+
+std::shared_ptr<LocalBufferPool> LocalBufferPool::Create(size_t poolCapacity) {
+    return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(poolCapacity));
+}
+
+sp<ABuffer> LocalBufferPool::newBuffer(size_t capacity) {
+    Mutex::Autolock lock(mMutex);
+    auto it = std::find_if(
+            mPool.begin(), mPool.end(),
+            [capacity](const std::vector<uint8_t> &vec) {
+                return vec.capacity() >= capacity;
+            });
+    if (it != mPool.end()) {
+        sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
+        mPool.erase(it);
+        return buffer;
+    }
+    if (mUsedSize + capacity > mPoolCapacity) {
+        while (!mPool.empty()) {
+            mUsedSize -= mPool.back().capacity();
+            mPool.pop_back();
+        }
+        if (mUsedSize + capacity > mPoolCapacity) {
+            ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
+                    mUsedSize, capacity, mPoolCapacity);
+            return nullptr;
+        }
+    }
+    std::vector<uint8_t> vec(capacity);
+    mUsedSize += vec.capacity();
+    return new VectorBuffer(std::move(vec), shared_from_this());
+}
+
+LocalBufferPool::VectorBuffer::VectorBuffer(
+        std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
+    : ABuffer(vec.data(), vec.capacity()),
+      mVec(std::move(vec)),
+      mPool(pool) {
+}
+
+LocalBufferPool::VectorBuffer::~VectorBuffer() {
+    std::shared_ptr<LocalBufferPool> pool = mPool.lock();
+    if (pool) {
+        // If pool is alive, return the vector back to the pool so that
+        // it can be recycled.
+        pool->returnVector(std::move(mVec));
+    }
+}
+
+void LocalBufferPool::returnVector(std::vector<uint8_t> &&vec) {
+    Mutex::Autolock lock(mMutex);
+    mPool.push_front(std::move(vec));
+}
+
+size_t FlexBuffersImpl::assignSlot(const sp<Codec2Buffer> &buffer) {
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (mBuffers[i].clientBuffer == nullptr
+                && mBuffers[i].compBuffer.expired()) {
+            mBuffers[i].clientBuffer = buffer;
+            return i;
+        }
+    }
+    mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
+    return mBuffers.size() - 1;
+}
+
+// FlexBuffersImpl
+
+bool FlexBuffersImpl::releaseSlot(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    sp<Codec2Buffer> clientBuffer;
+    size_t index = mBuffers.size();
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (mBuffers[i].clientBuffer == buffer) {
+            clientBuffer = mBuffers[i].clientBuffer;
+            if (release) {
+                mBuffers[i].clientBuffer.clear();
+            }
+            index = i;
+            break;
+        }
+    }
+    if (clientBuffer == nullptr) {
+        ALOGV("[%s] %s: No matching buffer found", mName, __func__);
+        return false;
+    }
+    std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
+    if (!result) {
+        result = clientBuffer->asC2Buffer();
+        mBuffers[index].compBuffer = result;
+    }
+    if (c2buffer) {
+        *c2buffer = result;
+    }
+    return true;
+}
+
+bool FlexBuffersImpl::expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        std::shared_ptr<C2Buffer> compBuffer =
+                mBuffers[i].compBuffer.lock();
+        if (!compBuffer || compBuffer != c2buffer) {
+            continue;
+        }
+        mBuffers[i].compBuffer.reset();
+        ALOGV("[%s] codec released buffer #%zu", mName, i);
+        return true;
+    }
+    ALOGV("[%s] codec released an unknown buffer", mName);
+    return false;
+}
+
+void FlexBuffersImpl::flush() {
+    ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size());
+    mBuffers.clear();
+}
+
+size_t FlexBuffersImpl::numClientBuffers() const {
+    return std::count_if(
+            mBuffers.begin(), mBuffers.end(),
+            [](const Entry &entry) {
+                return (entry.clientBuffer != nullptr);
+            });
+}
+
+// BuffersArrayImpl
+
+void BuffersArrayImpl::initialize(
+        const FlexBuffersImpl &impl,
+        size_t minSize,
+        std::function<sp<Codec2Buffer>()> allocate) {
+    mImplName = impl.mImplName + "[N]";
+    mName = mImplName.c_str();
+    for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
+        sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer;
+        bool ownedByClient = (clientBuffer != nullptr);
+        if (!ownedByClient) {
+            clientBuffer = allocate();
+        }
+        mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
+    }
+    ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize);
+    for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
+        mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
+    }
+}
+
+status_t BuffersArrayImpl::grabBuffer(
+        size_t *index,
+        sp<Codec2Buffer> *buffer,
+        std::function<bool(const sp<Codec2Buffer> &)> match) {
+    // allBuffersDontMatch remains true if all buffers are available but
+    // match() returns false for every buffer.
+    bool allBuffersDontMatch = true;
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()) {
+            if (match(mBuffers[i].clientBuffer)) {
+                mBuffers[i].ownedByClient = true;
+                *buffer = mBuffers[i].clientBuffer;
+                (*buffer)->meta()->clear();
+                (*buffer)->setRange(0, (*buffer)->capacity());
+                *index = i;
+                return OK;
+            }
+        } else {
+            allBuffersDontMatch = false;
+        }
+    }
+    return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK;
+}
+
+bool BuffersArrayImpl::returnBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    sp<Codec2Buffer> clientBuffer;
+    size_t index = mBuffers.size();
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        if (mBuffers[i].clientBuffer == buffer) {
+            if (!mBuffers[i].ownedByClient) {
+                ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu",
+                      mName, i);
+            }
+            clientBuffer = mBuffers[i].clientBuffer;
+            if (release) {
+                mBuffers[i].ownedByClient = false;
+            }
+            index = i;
+            break;
+        }
+    }
+    if (clientBuffer == nullptr) {
+        ALOGV("[%s] %s: No matching buffer found", mName, __func__);
+        return false;
+    }
+    ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index);
+    std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
+    if (!result) {
+        result = clientBuffer->asC2Buffer();
+        mBuffers[index].compBuffer = result;
+    }
+    if (c2buffer) {
+        *c2buffer = result;
+    }
+    return true;
+}
+
+bool BuffersArrayImpl::expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
+    for (size_t i = 0; i < mBuffers.size(); ++i) {
+        std::shared_ptr<C2Buffer> compBuffer =
+                mBuffers[i].compBuffer.lock();
+        if (!compBuffer) {
+            continue;
+        }
+        if (c2buffer == compBuffer) {
+            if (mBuffers[i].ownedByClient) {
+                // This should not happen.
+                ALOGD("[%s] codec released a buffer owned by client "
+                      "(index %zu)", mName, i);
+            }
+            mBuffers[i].compBuffer.reset();
+            ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i);
+            return true;
+        }
+    }
+    ALOGV("[%s] codec released an unknown buffer (array mode)", mName);
+    return false;
+}
+
+void BuffersArrayImpl::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+    array->clear();
+    for (const Entry &entry : mBuffers) {
+        array->push(entry.clientBuffer);
+    }
+}
+
+void BuffersArrayImpl::flush() {
+    for (Entry &entry : mBuffers) {
+        entry.ownedByClient = false;
+    }
+}
+
+void BuffersArrayImpl::realloc(std::function<sp<Codec2Buffer>()> alloc) {
+    size_t size = mBuffers.size();
+    mBuffers.clear();
+    for (size_t i = 0; i < size; ++i) {
+        mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
+    }
+}
+
+size_t BuffersArrayImpl::numClientBuffers() const {
+    return std::count_if(
+            mBuffers.begin(), mBuffers.end(),
+            [](const Entry &entry) {
+                return entry.ownedByClient;
+            });
+}
+
+// InputBuffersArray
+
+void InputBuffersArray::initialize(
+        const FlexBuffersImpl &impl,
+        size_t minSize,
+        std::function<sp<Codec2Buffer>()> allocate) {
+    mImpl.initialize(impl, minSize, allocate);
+}
+
+void InputBuffersArray::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+    mImpl.getArray(array);
+}
+
+bool InputBuffersArray::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
+    sp<Codec2Buffer> c2Buffer;
+    status_t err = mImpl.grabBuffer(index, &c2Buffer);
+    if (err == OK) {
+        c2Buffer->setFormat(mFormat);
+        handleImageData(c2Buffer);
+        *buffer = c2Buffer;
+        return true;
+    }
+    return false;
+}
+
+bool InputBuffersArray::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.returnBuffer(buffer, c2buffer, release);
+}
+
+bool InputBuffersArray::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void InputBuffersArray::flush() {
+    mImpl.flush();
+}
+
+size_t InputBuffersArray::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// LinearInputBuffers
+
+bool LinearInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
+    int32_t capacity = kLinearBufferSize;
+    (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+    if ((size_t)capacity > kMaxLinearBufferSize) {
+        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+        capacity = kMaxLinearBufferSize;
+    }
+    // TODO: proper max input size
+    // TODO: read usage from intf
+    sp<Codec2Buffer> newBuffer = alloc((size_t)capacity);
+    if (newBuffer == nullptr) {
+        return false;
+    }
+    *index = mImpl.assignSlot(newBuffer);
+    *buffer = newBuffer;
+    return true;
+}
+
+bool LinearInputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool LinearInputBuffers::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void LinearInputBuffers::flush() {
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+    mImpl.flush();
+}
+
+std::unique_ptr<InputBuffers> LinearInputBuffers::toArrayMode(
+        size_t size) {
+    int32_t capacity = kLinearBufferSize;
+    (void)mFormat->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
+    if ((size_t)capacity > kMaxLinearBufferSize) {
+        ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
+        capacity = kMaxLinearBufferSize;
+    }
+    // TODO: proper max input size
+    // TODO: read usage from intf
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [this, capacity] () -> sp<Codec2Buffer> { return alloc(capacity); });
+    return std::move(array);
+}
+
+size_t LinearInputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+sp<Codec2Buffer> LinearInputBuffers::alloc(size_t size) {
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    std::shared_ptr<C2LinearBlock> block;
+
+    c2_status_t err = mPool->fetchLinearBlock(size, usage, &block);
+    if (err != C2_OK) {
+        return nullptr;
+    }
+
+    return LinearBlockBuffer::Allocate(mFormat, block);
+}
+
+// EncryptedLinearInputBuffers
+
+EncryptedLinearInputBuffers::EncryptedLinearInputBuffers(
+        bool secure,
+        const sp<MemoryDealer> &dealer,
+        const sp<ICrypto> &crypto,
+        int32_t heapSeqNum,
+        size_t capacity,
+        size_t numInputSlots,
+        const char *componentName, const char *name)
+    : LinearInputBuffers(componentName, name),
+      mUsage({0, 0}),
+      mDealer(dealer),
+      mCrypto(crypto),
+      mHeapSeqNum(heapSeqNum) {
+    if (secure) {
+        mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
+    } else {
+        mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    }
+    for (size_t i = 0; i < numInputSlots; ++i) {
+        sp<IMemory> memory = mDealer->allocate(capacity);
+        if (memory == nullptr) {
+            ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated",
+                  mName, i);
+            break;
+        }
+        mMemoryVector.push_back({std::weak_ptr<C2LinearBlock>(), memory});
+    }
+}
+
+sp<Codec2Buffer> EncryptedLinearInputBuffers::alloc(size_t size) {
+    sp<IMemory> memory;
+    size_t slot = 0;
+    for (; slot < mMemoryVector.size(); ++slot) {
+        if (mMemoryVector[slot].block.expired()) {
+            memory = mMemoryVector[slot].memory;
+            break;
+        }
+    }
+    if (memory == nullptr) {
+        return nullptr;
+    }
+
+    std::shared_ptr<C2LinearBlock> block;
+    c2_status_t err = mPool->fetchLinearBlock(size, mUsage, &block);
+    if (err != C2_OK || block == nullptr) {
+        return nullptr;
+    }
+
+    mMemoryVector[slot].block = block;
+    return new EncryptedLinearBlockBuffer(mFormat, block, memory, mHeapSeqNum);
+}
+
+// GraphicMetadataInputBuffers
+
+GraphicMetadataInputBuffers::GraphicMetadataInputBuffers(
+        const char *componentName, const char *name)
+    : InputBuffers(componentName, name),
+      mImpl(mName),
+      mStore(GetCodec2PlatformAllocatorStore()) { }
+
+bool GraphicMetadataInputBuffers::requestNewBuffer(
+        size_t *index, sp<MediaCodecBuffer> *buffer) {
+    std::shared_ptr<C2Allocator> alloc;
+    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
+    if (err != C2_OK) {
+        return false;
+    }
+    sp<GraphicMetadataBuffer> newBuffer = new GraphicMetadataBuffer(mFormat, alloc);
+    if (newBuffer == nullptr) {
+        return false;
+    }
+    *index = mImpl.assignSlot(newBuffer);
+    *buffer = newBuffer;
+    return true;
+}
+
+bool GraphicMetadataInputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool GraphicMetadataInputBuffers::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void GraphicMetadataInputBuffers::flush() {
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+}
+
+std::unique_ptr<InputBuffers> GraphicMetadataInputBuffers::toArrayMode(
+        size_t size) {
+    std::shared_ptr<C2Allocator> alloc;
+    c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
+    if (err != C2_OK) {
+        return nullptr;
+    }
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [format = mFormat, alloc]() -> sp<Codec2Buffer> {
+                return new GraphicMetadataBuffer(format, alloc);
+            });
+    return std::move(array);
+}
+
+size_t GraphicMetadataInputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// GraphicInputBuffers
+
+GraphicInputBuffers::GraphicInputBuffers(
+        size_t numInputSlots, const char *componentName, const char *name)
+    : InputBuffers(componentName, name),
+      mImpl(mName),
+      mLocalBufferPool(LocalBufferPool::Create(
+              kMaxLinearBufferSize * numInputSlots)) { }
+
+bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
+    // TODO: proper max input size
+    // TODO: read usage from intf
+    C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+    sp<GraphicBlockBuffer> newBuffer = AllocateGraphicBuffer(
+            mPool, mFormat, HAL_PIXEL_FORMAT_YV12, usage, mLocalBufferPool);
+    if (newBuffer == nullptr) {
+        return false;
+    }
+    *index = mImpl.assignSlot(newBuffer);
+    handleImageData(newBuffer);
+    *buffer = newBuffer;
+    return true;
+}
+
+bool GraphicInputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer,
+        bool release) {
+    return mImpl.releaseSlot(buffer, c2buffer, release);
+}
+
+bool GraphicInputBuffers::expireComponentBuffer(
+        const std::shared_ptr<C2Buffer> &c2buffer) {
+    return mImpl.expireComponentBuffer(c2buffer);
+}
+
+void GraphicInputBuffers::flush() {
+    // This is no-op by default unless we're in array mode where we need to keep
+    // track of the flushed work.
+}
+
+std::unique_ptr<InputBuffers> GraphicInputBuffers::toArrayMode(size_t size) {
+    std::unique_ptr<InputBuffersArray> array(
+            new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]"));
+    array->setPool(mPool);
+    array->setFormat(mFormat);
+    array->initialize(
+            mImpl,
+            size,
+            [pool = mPool, format = mFormat, lbp = mLocalBufferPool]() -> sp<Codec2Buffer> {
+                C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+                return AllocateGraphicBuffer(
+                        pool, format, HAL_PIXEL_FORMAT_YV12, usage, lbp);
+            });
+    return std::move(array);
+}
+
+size_t GraphicInputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// OutputBuffersArray
+
+void OutputBuffersArray::initialize(
+        const FlexBuffersImpl &impl,
+        size_t minSize,
+        std::function<sp<Codec2Buffer>()> allocate) {
+    mImpl.initialize(impl, minSize, allocate);
+}
+
+status_t OutputBuffersArray::registerBuffer(
+        const std::shared_ptr<C2Buffer> &buffer,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> c2Buffer;
+    status_t err = mImpl.grabBuffer(
+            index,
+            &c2Buffer,
+            [buffer](const sp<Codec2Buffer> &clientBuffer) {
+                return clientBuffer->canCopy(buffer);
+            });
+    if (err == WOULD_BLOCK) {
+        ALOGV("[%s] buffers temporarily not available", mName);
+        return err;
+    } else if (err != OK) {
+        ALOGD("[%s] grabBuffer failed: %d", mName, err);
+        return err;
+    }
+    c2Buffer->setFormat(mFormat);
+    if (!c2Buffer->copy(buffer)) {
+        ALOGD("[%s] copy buffer failed", mName);
+        return WOULD_BLOCK;
+    }
+    submit(c2Buffer);
+    handleImageData(c2Buffer);
+    *clientBuffer = c2Buffer;
+    ALOGV("[%s] grabbed buffer %zu", mName, *index);
+    return OK;
+}
+
+status_t OutputBuffersArray::registerCsd(
+        const C2StreamInitDataInfo::output *csd,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> c2Buffer;
+    status_t err = mImpl.grabBuffer(
+            index,
+            &c2Buffer,
+            [csd](const sp<Codec2Buffer> &clientBuffer) {
+                return clientBuffer->base() != nullptr
+                        && clientBuffer->capacity() >= csd->flexCount();
+            });
+    if (err != OK) {
+        return err;
+    }
+    memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
+    c2Buffer->setRange(0, csd->flexCount());
+    c2Buffer->setFormat(mFormat);
+    *clientBuffer = c2Buffer;
+    return OK;
+}
+
+bool OutputBuffersArray::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) {
+    return mImpl.returnBuffer(buffer, c2buffer, true);
+}
+
+void OutputBuffersArray::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    (void)flushedWork;
+    mImpl.flush();
+    if (mSkipCutBuffer != nullptr) {
+        mSkipCutBuffer->clear();
+    }
+}
+
+void OutputBuffersArray::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
+    mImpl.getArray(array);
+}
+
+void OutputBuffersArray::realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
+    std::function<sp<Codec2Buffer>()> alloc;
+    switch (c2buffer->data().type()) {
+        case C2BufferData::LINEAR: {
+            uint32_t size = kLinearBufferSize;
+            const C2ConstLinearBlock &block = c2buffer->data().linearBlocks().front();
+            if (block.size() < kMaxLinearBufferSize / 2) {
+                size = block.size() * 2;
+            } else {
+                size = kMaxLinearBufferSize;
+            }
+            alloc = [format = mFormat, size] {
+                return new LocalLinearBuffer(format, new ABuffer(size));
+            };
+            break;
+        }
+
+        // TODO: add support
+        case C2BufferData::GRAPHIC:         [[fallthrough]];
+
+        case C2BufferData::INVALID:         [[fallthrough]];
+        case C2BufferData::LINEAR_CHUNKS:   [[fallthrough]];
+        case C2BufferData::GRAPHIC_CHUNKS:  [[fallthrough]];
+        default:
+            ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
+            return;
+    }
+    mImpl.realloc(alloc);
+}
+
+size_t OutputBuffersArray::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// FlexOutputBuffers
+
+status_t FlexOutputBuffers::registerBuffer(
+        const std::shared_ptr<C2Buffer> &buffer,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> newBuffer = wrap(buffer);
+    if (newBuffer == nullptr) {
+        return NO_MEMORY;
+    }
+    newBuffer->setFormat(mFormat);
+    *index = mImpl.assignSlot(newBuffer);
+    handleImageData(newBuffer);
+    *clientBuffer = newBuffer;
+    ALOGV("[%s] registered buffer %zu", mName, *index);
+    return OK;
+}
+
+status_t FlexOutputBuffers::registerCsd(
+        const C2StreamInitDataInfo::output *csd,
+        size_t *index,
+        sp<MediaCodecBuffer> *clientBuffer) {
+    sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(
+            mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
+    *index = mImpl.assignSlot(newBuffer);
+    *clientBuffer = newBuffer;
+    return OK;
+}
+
+bool FlexOutputBuffers::releaseBuffer(
+        const sp<MediaCodecBuffer> &buffer,
+        std::shared_ptr<C2Buffer> *c2buffer) {
+    return mImpl.releaseSlot(buffer, c2buffer, true);
+}
+
+void FlexOutputBuffers::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.
+}
+
+std::unique_ptr<OutputBuffers> FlexOutputBuffers::toArrayMode(size_t size) {
+    std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
+    array->setFormat(mFormat);
+    array->transferSkipCutBuffer(mSkipCutBuffer);
+    array->initialize(
+            mImpl,
+            size,
+            [this]() { return allocateArrayBuffer(); });
+    return std::move(array);
+}
+
+size_t FlexOutputBuffers::numClientBuffers() const {
+    return mImpl.numClientBuffers();
+}
+
+// LinearOutputBuffers
+
+void LinearOutputBuffers::flush(
+        const std::list<std::unique_ptr<C2Work>> &flushedWork) {
+    if (mSkipCutBuffer != nullptr) {
+        mSkipCutBuffer->clear();
+    }
+    FlexOutputBuffers::flush(flushedWork);
+}
+
+sp<Codec2Buffer> LinearOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
+    if (buffer == nullptr) {
+        ALOGV("[%s] using a dummy buffer", mName);
+        return new LocalLinearBuffer(mFormat, new ABuffer(0));
+    }
+    if (buffer->data().type() != C2BufferData::LINEAR) {
+        ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type());
+        // We expect linear output buffers from the component.
+        return nullptr;
+    }
+    if (buffer->data().linearBlocks().size() != 1u) {
+        ALOGV("[%s] no linear buffers", mName);
+        // We expect one and only one linear block from the component.
+        return nullptr;
+    }
+    sp<Codec2Buffer> clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer);
+    if (clientBuffer == nullptr) {
+        ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName);
+        return nullptr;
+    }
+    submit(clientBuffer);
+    return clientBuffer;
+}
+
+sp<Codec2Buffer> LinearOutputBuffers::allocateArrayBuffer() {
+    // TODO: proper max output size
+    return new LocalLinearBuffer(mFormat, new ABuffer(kLinearBufferSize));
+}
+
+// GraphicOutputBuffers
+
+sp<Codec2Buffer> GraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
+    return new DummyContainerBuffer(mFormat, buffer);
+}
+
+sp<Codec2Buffer> GraphicOutputBuffers::allocateArrayBuffer() {
+    return new DummyContainerBuffer(mFormat);
+}
+
+// RawGraphicOutputBuffers
+
+RawGraphicOutputBuffers::RawGraphicOutputBuffers(
+        size_t numOutputSlots, const char *componentName, const char *name)
+    : FlexOutputBuffers(componentName, name),
+      mLocalBufferPool(LocalBufferPool::Create(
+              kMaxLinearBufferSize * numOutputSlots)) { }
+
+sp<Codec2Buffer> RawGraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
+    if (buffer == nullptr) {
+        sp<Codec2Buffer> c2buffer = ConstGraphicBlockBuffer::AllocateEmpty(
+                mFormat,
+                [lbp = mLocalBufferPool](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+        if (c2buffer == nullptr) {
+            ALOGD("[%s] ConstGraphicBlockBuffer::AllocateEmpty failed", mName);
+            return nullptr;
+        }
+        c2buffer->setRange(0, 0);
+        return c2buffer;
+    } else {
+        return ConstGraphicBlockBuffer::Allocate(
+                mFormat,
+                buffer,
+                [lbp = mLocalBufferPool](size_t capacity) {
+                    return lbp->newBuffer(capacity);
+                });
+    }
+}
+
+sp<Codec2Buffer> RawGraphicOutputBuffers::allocateArrayBuffer() {
+    return ConstGraphicBlockBuffer::AllocateEmpty(
+            mFormat,
+            [lbp = mLocalBufferPool](size_t capacity) {
+                return lbp->newBuffer(capacity);
+            });
+}
+
+}  // namespace android
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
new file mode 100644
index 0000000..f5d9fee
--- /dev/null
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -0,0 +1,808 @@
+/*
+ * Copyright 2019, 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 CCODEC_BUFFERS_H_
+
+#define CCODEC_BUFFERS_H_
+
+#include <string>
+
+#include <C2Config.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/MediaCodecBuffer.h>
+
+#include "Codec2Buffer.h"
+#include "SkipCutBuffer.h"
+
+namespace android {
+
+constexpr size_t kLinearBufferSize = 1048576;
+// This can fit 4K RGBA frame, and most likely client won't need more than this.
+constexpr size_t kMaxLinearBufferSize = 3840 * 2160 * 4;
+
+/**
+ * Base class for representation of buffers at one port.
+ */
+class CCodecBuffers {
+public:
+    CCodecBuffers(const char *componentName, const char *name = "Buffers")
+        : mComponentName(componentName),
+          mChannelName(std::string(componentName) + ":" + name),
+          mName(mChannelName.c_str()) {
+    }
+    virtual ~CCodecBuffers() = default;
+
+    /**
+     * Set format for MediaCodec-facing buffers.
+     */
+    void setFormat(const sp<AMessage> &format);
+
+    /**
+     * Return a copy of current format.
+     */
+    sp<AMessage> dupFormat();
+
+    /**
+     * Returns true if the buffers are operating under array mode.
+     */
+    virtual bool isArrayMode() const { return false; }
+
+    /**
+     * Fills the vector with MediaCodecBuffer's if in array mode; otherwise,
+     * no-op.
+     */
+    virtual void getArray(Vector<sp<MediaCodecBuffer>> *) const {}
+
+    /**
+     * Return number of buffers the client owns.
+     */
+    virtual size_t numClientBuffers() const = 0;
+
+    /**
+     * Examine image data from the buffer and update the format if necessary.
+     */
+    void handleImageData(const sp<Codec2Buffer> &buffer);
+
+protected:
+    std::string mComponentName; ///< name of component for debugging
+    std::string mChannelName; ///< name of channel for debugging
+    const char *mName; ///< C-string version of channel name
+    // Format to be used for creating MediaCodec-facing buffers.
+    sp<AMessage> mFormat;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(CCodecBuffers);
+};
+
+class InputBuffers : public CCodecBuffers {
+public:
+    InputBuffers(const char *componentName, const char *name = "Input[]")
+        : CCodecBuffers(componentName, name) { }
+    virtual ~InputBuffers() = default;
+
+    /**
+     * Set a block pool to obtain input memory blocks.
+     */
+    void setPool(const std::shared_ptr<C2BlockPool> &pool) { mPool = pool; }
+
+    /**
+     * Get a new MediaCodecBuffer for input and its corresponding index.
+     * Returns false if no new buffer can be obtained at the moment.
+     */
+    virtual bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) = 0;
+
+    /**
+     * Release the buffer obtained from requestNewBuffer() and get the
+     * associated C2Buffer object back. Returns true if the buffer was on file
+     * and released successfully.
+     */
+    virtual bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) = 0;
+
+    /**
+     * Release the buffer that is no longer used by the codec process. Return
+     * true if and only if the buffer was on file and released successfully.
+     */
+    virtual bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) = 0;
+
+    /**
+     * Flush internal state. After this call, no index or buffer previously
+     * returned from requestNewBuffer() is valid.
+     */
+    virtual void flush() = 0;
+
+    /**
+     * Return array-backed version of input buffers. The returned object
+     * shall retain the internal state so that it will honor index and
+     * buffer from previous calls of requestNewBuffer().
+     */
+    virtual std::unique_ptr<InputBuffers> toArrayMode(size_t size) = 0;
+
+protected:
+    // Pool to obtain blocks for input buffers.
+    std::shared_ptr<C2BlockPool> mPool;
+
+private:
+    DISALLOW_EVIL_CONSTRUCTORS(InputBuffers);
+};
+
+class OutputBuffers : public CCodecBuffers {
+public:
+    OutputBuffers(const char *componentName, const char *name = "Output")
+        : CCodecBuffers(componentName, name) { }
+    virtual ~OutputBuffers() = default;
+
+    /**
+     * Register output C2Buffer from the component and obtain corresponding
+     * index and MediaCodecBuffer object. Returns false if registration
+     * fails.
+     */
+    virtual status_t registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) = 0;
+
+    /**
+     * Register codec specific data as a buffer to be consistent with
+     * MediaCodec behavior.
+     */
+    virtual status_t registerCsd(
+            const C2StreamInitDataInfo::output * /* csd */,
+            size_t * /* index */,
+            sp<MediaCodecBuffer> * /* clientBuffer */) = 0;
+
+    /**
+     * Release the buffer obtained from registerBuffer() and get the
+     * associated C2Buffer object back. Returns true if the buffer was on file
+     * and released successfully.
+     */
+    virtual bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) = 0;
+
+    /**
+     * Flush internal state. After this call, no index or buffer previously
+     * returned from registerBuffer() is valid.
+     */
+    virtual void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) = 0;
+
+    /**
+     * Return array-backed version of output buffers. The returned object
+     * shall retain the internal state so that it will honor index and
+     * buffer from previous calls of registerBuffer().
+     */
+    virtual std::unique_ptr<OutputBuffers> toArrayMode(size_t size) = 0;
+
+    /**
+     * Initialize SkipCutBuffer object.
+     */
+    void initSkipCutBuffer(
+            int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount);
+
+    /**
+     * Update the SkipCutBuffer object. No-op if it's never initialized.
+     */
+    void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount);
+
+    /**
+     * Submit buffer to SkipCutBuffer object, if initialized.
+     */
+    void submit(const sp<MediaCodecBuffer> &buffer);
+
+    /**
+     * Transfer SkipCutBuffer object to the other Buffers object.
+     */
+    void transferSkipCutBuffer(const sp<SkipCutBuffer> &scb);
+
+protected:
+    sp<SkipCutBuffer> mSkipCutBuffer;
+
+private:
+    int32_t mDelay;
+    int32_t mPadding;
+    int32_t mSampleRate;
+
+    void setSkipCutBuffer(int32_t skip, int32_t cut, int32_t channelCount);
+
+    DISALLOW_EVIL_CONSTRUCTORS(OutputBuffers);
+};
+
+/**
+ * Simple local buffer pool backed by std::vector.
+ */
+class LocalBufferPool : public std::enable_shared_from_this<LocalBufferPool> {
+public:
+    /**
+     * Create a new LocalBufferPool object.
+     *
+     * \param poolCapacity  max total size of buffers managed by this pool.
+     *
+     * \return  a newly created pool object.
+     */
+    static std::shared_ptr<LocalBufferPool> Create(size_t poolCapacity);
+
+    /**
+     * Return an ABuffer object whose size is at least |capacity|.
+     *
+     * \param   capacity  requested capacity
+     * \return  nullptr if the pool capacity is reached
+     *          an ABuffer object otherwise.
+     */
+    sp<ABuffer> newBuffer(size_t capacity);
+
+private:
+    /**
+     * ABuffer backed by std::vector.
+     */
+    class VectorBuffer : public ::android::ABuffer {
+    public:
+        /**
+         * Construct a VectorBuffer by taking the ownership of supplied vector.
+         *
+         * \param vec   backing vector of the buffer. this object takes
+         *              ownership at construction.
+         * \param pool  a LocalBufferPool object to return the vector at
+         *              destruction.
+         */
+        VectorBuffer(std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool);
+
+        ~VectorBuffer() override;
+
+    private:
+        std::vector<uint8_t> mVec;
+        std::weak_ptr<LocalBufferPool> mPool;
+    };
+
+    Mutex mMutex;
+    size_t mPoolCapacity;
+    size_t mUsedSize;
+    std::list<std::vector<uint8_t>> mPool;
+
+    /**
+     * Private constructor to prevent constructing non-managed LocalBufferPool.
+     */
+    explicit LocalBufferPool(size_t poolCapacity)
+        : mPoolCapacity(poolCapacity), mUsedSize(0) {
+    }
+
+    /**
+     * Take back the ownership of vec from the destructed VectorBuffer and put
+     * it in front of the pool.
+     */
+    void returnVector(std::vector<uint8_t> &&vec);
+
+    DISALLOW_EVIL_CONSTRUCTORS(LocalBufferPool);
+};
+
+class BuffersArrayImpl;
+
+/**
+ * Flexible buffer slots implementation.
+ */
+class FlexBuffersImpl {
+public:
+    FlexBuffersImpl(const char *name)
+        : mImplName(std::string(name) + ".Impl"),
+          mName(mImplName.c_str()) { }
+
+    /**
+     * Assign an empty slot for a buffer and return the index. If there's no
+     * empty slot, just add one at the end and return it.
+     *
+     * \param buffer[in]  a new buffer to assign a slot.
+     * \return            index of the assigned slot.
+     */
+    size_t assignSlot(const sp<Codec2Buffer> &buffer);
+
+    /**
+     * Release the slot from the client, and get the C2Buffer object back from
+     * the previously assigned buffer. Note that the slot is not completely free
+     * until the returned C2Buffer object is freed.
+     *
+     * \param   buffer[in]        the buffer previously assigned a slot.
+     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
+     *                            if null.
+     * \return  true  if the buffer is successfully released from a slot
+     *          false otherwise
+     */
+    bool releaseSlot(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release);
+
+    /**
+     * Expire the C2Buffer object in the slot.
+     *
+     * \param   c2buffer[in]  C2Buffer object which the component released.
+     * \return  true  if the buffer is found in one of the slots and
+     *                successfully released
+     *          false otherwise
+     */
+    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer);
+
+    /**
+     * The client abandoned all known buffers, so reclaim the ownership.
+     */
+    void flush();
+
+    /**
+     * Return the number of buffers that are sent to the client but not released
+     * yet.
+     */
+    size_t numClientBuffers() const;
+
+private:
+    friend class BuffersArrayImpl;
+
+    std::string mImplName; ///< name for debugging
+    const char *mName; ///< C-string version of name
+
+    struct Entry {
+        sp<Codec2Buffer> clientBuffer;
+        std::weak_ptr<C2Buffer> compBuffer;
+    };
+    std::vector<Entry> mBuffers;
+};
+
+/**
+ * Static buffer slots implementation based on a fixed-size array.
+ */
+class BuffersArrayImpl {
+public:
+    BuffersArrayImpl()
+        : mImplName("BuffersArrayImpl"),
+          mName(mImplName.c_str()) { }
+
+    /**
+     * Initialize buffer array from the original |impl|. The buffers known by
+     * the client is preserved, and the empty slots are populated so that the
+     * array size is at least |minSize|.
+     *
+     * \param impl[in]      FlexBuffersImpl object used so far.
+     * \param minSize[in]   minimum size of the buffer array.
+     * \param allocate[in]  function to allocate a client buffer for an empty slot.
+     */
+    void initialize(
+            const FlexBuffersImpl &impl,
+            size_t minSize,
+            std::function<sp<Codec2Buffer>()> allocate);
+
+    /**
+     * Grab a buffer from the underlying array which matches the criteria.
+     *
+     * \param index[out]    index of the slot.
+     * \param buffer[out]   the matching buffer.
+     * \param match[in]     a function to test whether the buffer matches the
+     *                      criteria or not.
+     * \return OK           if successful,
+     *         WOULD_BLOCK  if slots are being used,
+     *         NO_MEMORY    if no slot matches the criteria, even though it's
+     *                      available
+     */
+    status_t grabBuffer(
+            size_t *index,
+            sp<Codec2Buffer> *buffer,
+            std::function<bool(const sp<Codec2Buffer> &)> match =
+                [](const sp<Codec2Buffer> &) { return true; });
+
+    /**
+     * Return the buffer from the client, and get the C2Buffer object back from
+     * the buffer. Note that the slot is not completely free until the returned
+     * C2Buffer object is freed.
+     *
+     * \param   buffer[in]        the buffer previously grabbed.
+     * \param   c2buffer[in,out]  pointer to C2Buffer to be populated. Ignored
+     *                            if null.
+     * \return  true  if the buffer is successfully returned
+     *          false otherwise
+     */
+    bool returnBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release);
+
+    /**
+     * Expire the C2Buffer object in the slot.
+     *
+     * \param   c2buffer[in]  C2Buffer object which the component released.
+     * \return  true  if the buffer is found in one of the slots and
+     *                successfully released
+     *          false otherwise
+     */
+    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer);
+
+    /**
+     * Populate |array| with the underlying buffer array.
+     *
+     * \param array[out]  an array to be filled with the underlying buffer array.
+     */
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const;
+
+    /**
+     * The client abandoned all known buffers, so reclaim the ownership.
+     */
+    void flush();
+
+    /**
+     * Reallocate the array with the given allocation function.
+     *
+     * \param alloc[in] the allocation function for client buffers.
+     */
+    void realloc(std::function<sp<Codec2Buffer>()> alloc);
+
+    /**
+     * Return the number of buffers that are sent to the client but not released
+     * yet.
+     */
+    size_t numClientBuffers() const;
+
+private:
+    std::string mImplName; ///< name for debugging
+    const char *mName; ///< C-string version of name
+
+    struct Entry {
+        const sp<Codec2Buffer> clientBuffer;
+        std::weak_ptr<C2Buffer> compBuffer;
+        bool ownedByClient;
+    };
+    std::vector<Entry> mBuffers;
+};
+
+class InputBuffersArray : public InputBuffers {
+public:
+    InputBuffersArray(const char *componentName, const char *name = "Input[N]")
+        : InputBuffers(componentName, name) { }
+    ~InputBuffersArray() override = default;
+
+    /**
+     * Initialize this object from the non-array state. We keep existing slots
+     * at the same index, and for empty slots we allocate client buffers with
+     * the given allocate function. If the number of slots is less than minSize,
+     * we fill the array to the minimum size.
+     *
+     * \param impl[in]      existing non-array state
+     * \param minSize[in]   minimum size of the array
+     * \param allocate[in]  allocate function to fill empty slots
+     */
+    void initialize(
+            const FlexBuffersImpl &impl,
+            size_t minSize,
+            std::function<sp<Codec2Buffer>()> allocate);
+
+    bool isArrayMode() const final { return true; }
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t) final {
+        return nullptr;
+    }
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    size_t numClientBuffers() const final;
+
+private:
+    BuffersArrayImpl mImpl;
+};
+
+class LinearInputBuffers : public InputBuffers {
+public:
+    LinearInputBuffers(const char *componentName, const char *name = "1D-Input")
+        : InputBuffers(componentName, name),
+          mImpl(mName) { }
+    ~LinearInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+
+    size_t numClientBuffers() const final;
+
+    /**
+     * Allocate a client buffer with the given size. This method may be
+     * overridden to support different kind of linear buffers (e.g. encrypted).
+     */
+    virtual sp<Codec2Buffer> alloc(size_t size);
+
+private:
+    FlexBuffersImpl mImpl;
+};
+
+class EncryptedLinearInputBuffers : public LinearInputBuffers {
+public:
+    EncryptedLinearInputBuffers(
+            bool secure,
+            const sp<MemoryDealer> &dealer,
+            const sp<ICrypto> &crypto,
+            int32_t heapSeqNum,
+            size_t capacity,
+            size_t numInputSlots,
+            const char *componentName, const char *name = "EncryptedInput");
+
+    ~EncryptedLinearInputBuffers() override = default;
+
+    sp<Codec2Buffer> alloc(size_t size) override;
+
+private:
+    C2MemoryUsage mUsage;
+    sp<MemoryDealer> mDealer;
+    sp<ICrypto> mCrypto;
+    int32_t mHeapSeqNum;
+    struct Entry {
+        std::weak_ptr<C2LinearBlock> block;
+        sp<IMemory> memory;
+    };
+    std::vector<Entry> mMemoryVector;
+};
+
+class GraphicMetadataInputBuffers : public InputBuffers {
+public:
+    GraphicMetadataInputBuffers(const char *componentName, const char *name = "2D-MetaInput");
+    ~GraphicMetadataInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t size) final;
+
+    size_t numClientBuffers() const final;
+
+private:
+    FlexBuffersImpl mImpl;
+    std::shared_ptr<C2AllocatorStore> mStore;
+};
+
+class GraphicInputBuffers : public InputBuffers {
+public:
+    GraphicInputBuffers(
+            size_t numInputSlots, const char *componentName, const char *name = "2D-BB-Input");
+    ~GraphicInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) override;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer,
+            bool release) override;
+
+    bool expireComponentBuffer(
+            const std::shared_ptr<C2Buffer> &c2buffer) override;
+
+    void flush() override;
+
+    std::unique_ptr<InputBuffers> toArrayMode(
+            size_t size) final;
+
+    size_t numClientBuffers() const final;
+
+private:
+    FlexBuffersImpl mImpl;
+    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+class DummyInputBuffers : public InputBuffers {
+public:
+    DummyInputBuffers(const char *componentName, const char *name = "2D-Input")
+        : InputBuffers(componentName, name) { }
+    ~DummyInputBuffers() override = default;
+
+    bool requestNewBuffer(size_t *, sp<MediaCodecBuffer> *) override {
+        return false;
+    }
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &, std::shared_ptr<C2Buffer> *, bool) override {
+        return false;
+    }
+
+    bool expireComponentBuffer(const std::shared_ptr<C2Buffer> &) override {
+        return false;
+    }
+    void flush() override {
+    }
+
+    std::unique_ptr<InputBuffers> toArrayMode(size_t) final {
+        return nullptr;
+    }
+
+    bool isArrayMode() const final { return true; }
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final {
+        array->clear();
+    }
+
+    size_t numClientBuffers() const final {
+        return 0u;
+    }
+};
+
+class OutputBuffersArray : public OutputBuffers {
+public:
+    OutputBuffersArray(const char *componentName, const char *name = "Output[N]")
+        : OutputBuffers(componentName, name) { }
+    ~OutputBuffersArray() override = default;
+
+    /**
+     * Initialize this object from the non-array state. We keep existing slots
+     * at the same index, and for empty slots we allocate client buffers with
+     * the given allocate function. If the number of slots is less than minSize,
+     * we fill the array to the minimum size.
+     *
+     * \param impl[in]      existing non-array state
+     * \param minSize[in]   minimum size of the array
+     * \param allocate[in]  allocate function to fill empty slots
+     */
+    void initialize(
+            const FlexBuffersImpl &impl,
+            size_t minSize,
+            std::function<sp<Codec2Buffer>()> allocate);
+
+    bool isArrayMode() const final { return true; }
+
+    std::unique_ptr<OutputBuffers> toArrayMode(size_t) final {
+        return nullptr;
+    }
+
+    status_t registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) final;
+
+    status_t registerCsd(
+            const C2StreamInitDataInfo::output *csd,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) final;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) override;
+
+    void flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
+
+    void getArray(Vector<sp<MediaCodecBuffer>> *array) const final;
+
+    /**
+     * Reallocate the array, filled with buffers with the same size as given
+     * buffer.
+     *
+     * \param c2buffer[in] the reference buffer
+     */
+    void realloc(const std::shared_ptr<C2Buffer> &c2buffer);
+
+    size_t numClientBuffers() const final;
+
+private:
+    BuffersArrayImpl mImpl;
+};
+
+class FlexOutputBuffers : public OutputBuffers {
+public:
+    FlexOutputBuffers(const char *componentName, const char *name = "Output[]")
+        : OutputBuffers(componentName, name),
+          mImpl(mName) { }
+
+    status_t registerBuffer(
+            const std::shared_ptr<C2Buffer> &buffer,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) override;
+
+    status_t registerCsd(
+            const C2StreamInitDataInfo::output *csd,
+            size_t *index,
+            sp<MediaCodecBuffer> *clientBuffer) final;
+
+    bool releaseBuffer(
+            const sp<MediaCodecBuffer> &buffer,
+            std::shared_ptr<C2Buffer> *c2buffer) override;
+
+    void flush(
+            const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
+
+    std::unique_ptr<OutputBuffers> toArrayMode(size_t size) override;
+
+    size_t numClientBuffers() const final;
+
+    /**
+     * Return an appropriate Codec2Buffer object for the type of buffers.
+     *
+     * \param buffer  C2Buffer object to wrap.
+     *
+     * \return  appropriate Codec2Buffer object to wrap |buffer|.
+     */
+    virtual sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) = 0;
+
+    /**
+     * Return an appropriate Codec2Buffer object for the type of buffers, to be
+     * used as an empty array buffer.
+     *
+     * \return  appropriate Codec2Buffer object which can copy() from C2Buffers.
+     */
+    virtual sp<Codec2Buffer> allocateArrayBuffer() = 0;
+
+private:
+    FlexBuffersImpl mImpl;
+};
+
+class LinearOutputBuffers : public FlexOutputBuffers {
+public:
+    LinearOutputBuffers(const char *componentName, const char *name = "1D-Output")
+        : FlexOutputBuffers(componentName, name) { }
+
+    void flush(
+            const std::list<std::unique_ptr<C2Work>> &flushedWork) override;
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    sp<Codec2Buffer> allocateArrayBuffer() override;
+};
+
+class GraphicOutputBuffers : public FlexOutputBuffers {
+public:
+    GraphicOutputBuffers(const char *componentName, const char *name = "2D-Output")
+        : FlexOutputBuffers(componentName, name) { }
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    sp<Codec2Buffer> allocateArrayBuffer() override;
+};
+
+class RawGraphicOutputBuffers : public FlexOutputBuffers {
+public:
+    RawGraphicOutputBuffers(
+            size_t numOutputSlots, const char *componentName, const char *name = "2D-BB-Output");
+    ~RawGraphicOutputBuffers() override = default;
+
+    sp<Codec2Buffer> wrap(const std::shared_ptr<C2Buffer> &buffer) override;
+
+    sp<Codec2Buffer> allocateArrayBuffer() override;
+
+private:
+    std::shared_ptr<LocalBufferPool> mLocalBufferPool;
+};
+
+}  // namespace android
+
+#endif  // CCODEC_BUFFERS_H_
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index ef02e74..428f032 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -486,19 +486,31 @@
     add(ConfigMapper(std::string(KEY_FEATURE_) + FEATURE_SecurePlayback,
                      C2_PARAMKEY_SECURE_MODE, "value"));
 
-    add(ConfigMapper("prepend-sps-pps-to-idr-frames",
+    add(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES,
                      C2_PARAMKEY_PREPEND_HEADER_MODE, "value")
         .limitTo(D::ENCODER & D::VIDEO)
-        .withMapper([](C2Value v) -> C2Value {
+        .withMappers([](C2Value v) -> C2Value {
             int32_t value;
-            if (v.get(&value) && value) {
-                return C2Value(C2Config::PREPEND_HEADER_TO_ALL_SYNC);
-            } else {
-                return C2Value(C2Config::PREPEND_HEADER_TO_NONE);
+            if (v.get(&value)) {
+                return value ? C2Value(C2Config::PREPEND_HEADER_TO_ALL_SYNC)
+                             : C2Value(C2Config::PREPEND_HEADER_TO_NONE);
             }
+            return C2Value();
+        }, [](C2Value v) -> C2Value {
+            C2Config::prepend_header_mode_t value;
+            using C2ValueType=typename _c2_reduce_enum_to_underlying_type<decltype(value)>::type;
+            if (v.get((C2ValueType *)&value)) {
+                switch (value) {
+                    case C2Config::PREPEND_HEADER_TO_NONE:      return 0;
+                    case C2Config::PREPEND_HEADER_TO_ALL_SYNC:  return 1;
+                    case C2Config::PREPEND_HEADER_ON_CHANGE:    [[fallthrough]];
+                    default:                                    return C2Value();
+                }
+            }
+            return C2Value();
         }));
     // remove when codecs switch to PARAMKEY
-    deprecated(ConfigMapper("prepend-sps-pps-to-idr-frames",
+    deprecated(ConfigMapper(KEY_PREPEND_HEADERS_TO_SYNC_FRAMES,
                             "coding.add-csd-to-sync-frames", "value")
                .limitTo(D::ENCODER & D::VIDEO));
     // convert to timestamp base
diff --git a/media/libaudioclient/AudioProductStrategy.cpp b/media/libaudioclient/AudioProductStrategy.cpp
index 1da1114..0e1dfac 100644
--- a/media/libaudioclient/AudioProductStrategy.cpp
+++ b/media/libaudioclient/AudioProductStrategy.cpp
@@ -86,7 +86,7 @@
              (clientAttritubes.content_type == refAttributes.content_type)) &&
             ((refAttributes.flags == AUDIO_FLAG_NONE) ||
              (clientAttritubes.flags != AUDIO_FLAG_NONE &&
-            (clientAttritubes.flags & refAttributes.flags) == clientAttritubes.flags)) &&
+            (clientAttritubes.flags & refAttributes.flags) == refAttributes.flags)) &&
             ((strlen(refAttributes.tags) == 0) ||
              (std::strcmp(clientAttritubes.tags, refAttributes.tags) == 0));
 }
diff --git a/media/libaudioclient/AudioTrack.cpp b/media/libaudioclient/AudioTrack.cpp
index e59f7e0..8d1f511 100644
--- a/media/libaudioclient/AudioTrack.cpp
+++ b/media/libaudioclient/AudioTrack.cpp
@@ -250,10 +250,7 @@
       mPreviousSchedulingGroup(SP_DEFAULT),
       mPausedPosition(0)
 {
-    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
-    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
-    mAttributes.flags = 0x0;
-    strcpy(mAttributes.tags, "");
+    mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
 
     (void)set(streamType, sampleRate, format, channelMask,
             frameCount, flags, cbf, user, notificationFrames,
@@ -286,10 +283,7 @@
       mPausedPosition(0),
       mSelectedDeviceId(AUDIO_PORT_HANDLE_NONE)
 {
-    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
-    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
-    mAttributes.flags = 0x0;
-    strcpy(mAttributes.tags, "");
+    mAttributes = AUDIO_ATTRIBUTES_INITIALIZER;
 
     (void)set(streamType, sampleRate, format, channelMask,
             0 /*frameCount*/, flags, cbf, user, notificationFrames,
@@ -2603,6 +2597,20 @@
                     ALOGV_IF(mPreviousLocation == ExtendedTimestamp::LOCATION_SERVER,
                             "%s(%d): location moved from server to kernel",
                             __func__, mPortId);
+
+                    if (ets.mPosition[ExtendedTimestamp::LOCATION_SERVER] ==
+                            ets.mPosition[ExtendedTimestamp::LOCATION_KERNEL]) {
+                        // In Q, we don't return errors as an invalid time
+                        // but instead we leave the last kernel good timestamp alone.
+                        //
+                        // If server is identical to kernel, the device data pipeline is idle.
+                        // A better start time is now.  The retrograde check ensures
+                        // timestamp monotonicity.
+                        const int64_t nowNs = systemTime();
+                        ALOGD("%s(%d) device stall, using current time %lld",
+                                __func__, mPortId, (long long)nowNs);
+                        timestamp.mTime = convertNsToTimespec(nowNs);
+                    }
                 }
 
                 // We update the timestamp time even when paused.
@@ -2740,27 +2748,32 @@
     // Prevent retrograde motion in timestamp.
     // This is sometimes caused by erratic reports of the available space in the ALSA drivers.
     if (status == NO_ERROR) {
+        // Fix stale time when checking timestamp right after start().
+        // The position is at the last reported location but the time can be stale
+        // due to pause or standby or cold start latency.
+        //
+        // We keep advancing the time (but not the position) to ensure that the
+        // stale value does not confuse the application.
+        //
+        // For offload compatibility, use a default lag value here.
+        // Any time discrepancy between this update and the pause timestamp is handled
+        // by the retrograde check afterwards.
+        int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
+        const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
+        const int64_t limitNs = mStartNs - lagNs;
+        if (currentTimeNanos < limitNs) {
+            ALOGD("%s(%d): correcting timestamp time for pause, "
+                    "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
+                    __func__, mPortId,
+                    (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
+            timestamp.mTime = convertNsToTimespec(limitNs);
+            currentTimeNanos = limitNs;
+        }
+
         // previousTimestampValid is set to false when starting after a stop or flush.
         if (previousTimestampValid) {
             const int64_t previousTimeNanos =
                     audio_utils_ns_from_timespec(&mPreviousTimestamp.mTime);
-            int64_t currentTimeNanos = audio_utils_ns_from_timespec(&timestamp.mTime);
-
-            // Fix stale time when checking timestamp right after start().
-            //
-            // For offload compatibility, use a default lag value here.
-            // Any time discrepancy between this update and the pause timestamp is handled
-            // by the retrograde check afterwards.
-            const int64_t lagNs = int64_t(mAfLatency * 1000000LL);
-            const int64_t limitNs = mStartNs - lagNs;
-            if (currentTimeNanos < limitNs) {
-                ALOGD("%s(%d): correcting timestamp time for pause, "
-                        "currentTimeNanos: %lld < limitNs: %lld < mStartNs: %lld",
-                        __func__, mPortId,
-                        (long long)currentTimeNanos, (long long)limitNs, (long long)mStartNs);
-                timestamp.mTime = convertNsToTimespec(limitNs);
-                currentTimeNanos = limitNs;
-            }
 
             // retrograde check
             if (currentTimeNanos < previousTimeNanos) {
diff --git a/media/libaudioprocessing/AudioResamplerDyn.cpp b/media/libaudioprocessing/AudioResamplerDyn.cpp
index 0a389bb..ec56b00 100644
--- a/media/libaudioprocessing/AudioResamplerDyn.cpp
+++ b/media/libaudioprocessing/AudioResamplerDyn.cpp
@@ -201,6 +201,8 @@
             "ro.audio.resampler.psd.stopband", mPropertyStopbandAttenuation);
     mPropertyCutoffPercent = property_get_int32(
             "ro.audio.resampler.psd.cutoff_percent", mPropertyCutoffPercent);
+    mPropertyTransitionBandwidthCheat = property_get_int32(
+            "ro.audio.resampler.psd.tbwcheat", mPropertyTransitionBandwidthCheat);
 }
 
 template<typename TC, typename TI, typename TO>
@@ -379,9 +381,17 @@
             halfLength = mPropertyHalfFilterLength;
             stopBandAtten = mPropertyStopbandAttenuation;
             useS32 = true;
-            fcr = mInSampleRate <= mSampleRate
-                    ? 0.5 : 0.5 * mSampleRate / mInSampleRate;
-            fcr *= mPropertyCutoffPercent / 100.;
+
+            // Use either the stopband location for design (tbwCheat)
+            // or use the 3dB cutoff location for design (fcr).
+            // This choice is exclusive and based on whether fcr > 0.
+            if (mPropertyTransitionBandwidthCheat != 0) {
+                tbwCheat = mPropertyTransitionBandwidthCheat / 100.;
+            } else {
+                fcr = mInSampleRate <= mSampleRate
+                        ? 0.5 : 0.5 * mSampleRate / mInSampleRate;
+                fcr *= mPropertyCutoffPercent / 100.;
+            }
         } else {
             // Voice quality devices have lower sampling rates
             // (and may be a consequence of downstream AMR-WB / G.722 codecs).
diff --git a/media/libaudioprocessing/AudioResamplerDyn.h b/media/libaudioprocessing/AudioResamplerDyn.h
index 479142e..82b9505 100644
--- a/media/libaudioprocessing/AudioResamplerDyn.h
+++ b/media/libaudioprocessing/AudioResamplerDyn.h
@@ -193,6 +193,13 @@
               int32_t mPropertyCutoffPercent = 100;
                       // "ro.audio.resampler.psd.cutoff_percent"
 
+              // Specify the transition bandwidth extension beyond Nyquist.
+              // If this is nonzero then mPropertyCutoffPercent is ignored.
+              // A value of 100 or greater is typically used, where 100 means the
+              // stopband is at Nyquist (this is a typical design).
+              int32_t mPropertyTransitionBandwidthCheat = 0;
+                      // "ro.audio.resampler.psd.tbwcheat"
+
     // Filter creation design parameters, see setSampleRate()
              double mStopbandAttenuationDb = 0.;
              double mPassbandRippleDb = 0.;
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
index 3a97905..3fbbc09 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.cpp
@@ -650,6 +650,9 @@
     params.SpeakerType            = LVM_HEADPHONES;
 
     pContext->pBundledContext->SampleRate = LVM_FS_44100;
+#ifdef SUPPORT_MC
+    pContext->pBundledContext->ChMask = AUDIO_CHANNEL_OUT_STEREO;
+#endif
 
     /* Concert Sound parameters */
     params.VirtualizerOperatingMode   = LVM_MODE_OFF;
@@ -695,6 +698,11 @@
     params.TE_OperatingMode       = LVM_TE_OFF;
     params.TE_EffectLevel         = 0;
 
+#ifdef SUPPORT_MC
+    params.NrChannels             =
+        audio_channel_count_from_out_mask(AUDIO_CHANNEL_OUT_STEREO);
+    params.ChMask                 = AUDIO_CHANNEL_OUT_STEREO;
+#endif
     /* Activate the initial settings */
     LvmStatus = LVM_SetControlParameters(pContext->pBundledContext->hInstance,
                                          &params);
@@ -1297,7 +1305,12 @@
         return -EINVAL;
     }
 
+#ifdef SUPPORT_MC
+    if (pContext->pBundledContext->SampleRate != SampleRate ||
+        pContext->pBundledContext->ChMask != pConfig->inputCfg.channels) {
+#else
     if(pContext->pBundledContext->SampleRate != SampleRate){
+#endif
 
         LVM_ControlParams_t     ActiveParams;
         LVM_ReturnStatus_en     LvmStatus = LVM_SUCCESS;
@@ -1323,6 +1336,9 @@
         LVM_ERROR_CHECK(LvmStatus, "LVM_SetControlParameters", "Effect_setConfig")
         ALOGV("\tEffect_setConfig Succesfully called LVM_SetControlParameters\n");
         pContext->pBundledContext->SampleRate = SampleRate;
+#ifdef SUPPORT_MC
+        pContext->pBundledContext->ChMask = pConfig->inputCfg.channels;
+#endif
 
         LvmEffect_limitLevel(pContext);
 
diff --git a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
index 6bf045d..6af4554 100644
--- a/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
+++ b/media/libeffects/lvm/wrapper/Bundle/EffectBundle.h
@@ -107,6 +107,9 @@
     LVM_FLOAT                       *pInputBuffer;
     LVM_FLOAT                       *pOutputBuffer;
 #endif
+#ifdef SUPPORT_MC
+    LVM_INT32                       ChMask;
+#endif
 };
 
 /* SessionContext : One session */
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index a529628..1d33590 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -65,7 +65,6 @@
         "MediaCodecInfo.cpp",
         "OMXBuffer.cpp",
         "omx/1.0/WGraphicBufferSource.cpp",
-        "omx/1.0/WOmx.cpp",
         "omx/1.0/WOmxBufferSource.cpp",
         "omx/1.0/WOmxNode.cpp",
         "omx/1.0/WOmxObserver.cpp",
@@ -75,13 +74,16 @@
         local_include_dirs: ["aidl"],
         export_aidl_headers: true,
     },
+    
+    local_include_dirs: [
+        "include",
+    ],
 
     shared_libs: [
         "android.hidl.token@1.0-utils",
         "android.hardware.media.omx@1.0",
         "libbinder",
         "libcutils",
-        "libgui",
         "libhidlbase",
         "libhidltransport",
         "liblog",
@@ -93,21 +95,84 @@
     export_shared_lib_headers: [
         "android.hidl.token@1.0-utils",
         "android.hardware.media.omx@1.0",
-        "libgui",
         "libstagefright_foundation",
         "libui",
     ],
 
     header_libs: [
-        "libmedia_headers",
+        "libstagefright_headers",
+        "media_plugin_headers",
     ],
 
     export_header_lib_headers: [
-        "libmedia_headers",
+        "libstagefright_headers",
+        "media_plugin_headers",
     ],
 
     export_include_dirs: [
         "aidl",
+        "include",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wno-error=deprecated-declarations",
+        "-Wall",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "unsigned-integer-overflow",
+            "signed-integer-overflow",
+        ],
+        cfi: true,
+    },
+}
+
+
+cc_library_shared {
+    name: "libmedia_omx_client",
+
+    srcs: [
+        "omx/1.0/WOmx.cpp",
+    ],
+
+    local_include_dirs: [
+        "include",
+    ],
+
+    shared_libs: [
+        "libbinder",
+        "libcutils",
+        "libgui",
+        "libhidlbase",
+        "libhidltransport",
+        "liblog",
+        "libmedia_omx",
+        "libstagefright_foundation",
+        "libui",
+        "libutils",
+    ],
+
+    export_shared_lib_headers: [
+        "libgui",
+        "libmedia_omx",
+        "libstagefright_foundation",
+        "libui",
+    ],
+
+    header_libs: [
+        "libstagefright_headers",
+        "media_plugin_headers",
+    ],
+
+    export_header_lib_headers: [
+        "libstagefright_headers",
+        "media_plugin_headers",
+    ],
+
+    export_include_dirs: [
+        "include",
     ],
 
     cflags: [
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 747b88f..bc0c2cd 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -27,7 +27,6 @@
 #include <media/openmax/OMX_IndexExt.h>
 #include <media/OMXBuffer.h>
 #include <utils/NativeHandle.h>
-#include <gui/IGraphicBufferProducer.h>
 
 #include <media/omx/1.0/WOmxNode.h>
 #include <android/IGraphicBufferSource.h>
@@ -62,79 +61,6 @@
     SET_QUIRKS,
 };
 
-class BpOMX : public BpInterface<IOMX> {
-public:
-    explicit BpOMX(const sp<IBinder> &impl)
-        : BpInterface<IOMX>(impl) {
-    }
-
-    virtual status_t listNodes(List<ComponentInfo> *list) {
-        list->clear();
-
-        Parcel data, reply;
-        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        remote()->transact(LIST_NODES, data, &reply);
-
-        int32_t n = reply.readInt32();
-        for (int32_t i = 0; i < n; ++i) {
-            list->push_back(ComponentInfo());
-            ComponentInfo &info = *--list->end();
-
-            info.mName = reply.readString8();
-            int32_t numRoles = reply.readInt32();
-            for (int32_t j = 0; j < numRoles; ++j) {
-                info.mRoles.push_back(reply.readString8());
-            }
-        }
-
-        return OK;
-    }
-
-    virtual status_t allocateNode(
-            const char *name, const sp<IOMXObserver> &observer,
-            sp<IOMXNode> *omxNode) {
-        Parcel data, reply;
-        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        data.writeCString(name);
-        data.writeStrongBinder(IInterface::asBinder(observer));
-        remote()->transact(ALLOCATE_NODE, data, &reply);
-
-        status_t err = reply.readInt32();
-        if (err == OK) {
-            *omxNode = IOMXNode::asInterface(reply.readStrongBinder());
-        } else {
-            omxNode->clear();
-        }
-
-        return err;
-    }
-
-    virtual status_t createInputSurface(
-            sp<IGraphicBufferProducer> *bufferProducer,
-            sp<IGraphicBufferSource> *bufferSource) {
-        Parcel data, reply;
-        status_t err;
-        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
-        err = remote()->transact(CREATE_INPUT_SURFACE, data, &reply);
-        if (err != OK) {
-            ALOGW("binder transaction failed: %d", err);
-            return err;
-        }
-
-        err = reply.readInt32();
-        if (err != OK) {
-            return err;
-        }
-
-        *bufferProducer = IGraphicBufferProducer::asInterface(
-                reply.readStrongBinder());
-        *bufferSource = IGraphicBufferSource::asInterface(
-                reply.readStrongBinder());
-
-        return err;
-    }
-};
-
 class BpOMXNode : public BpInterface<IOMXNode> {
 public:
     explicit BpOMXNode(const sp<IBinder> &impl)
@@ -551,7 +477,6 @@
     }
 };
 
-IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
 IMPLEMENT_HYBRID_META_INTERFACE(OMXNode, "android.hardware.IOMXNode");
 
 ////////////////////////////////////////////////////////////////////////////////
@@ -562,82 +487,6 @@
             return PERMISSION_DENIED; \
         } } while (0)
 
-status_t BnOMX::onTransact(
-    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
-    switch (code) {
-        case LIST_NODES:
-        {
-            CHECK_OMX_INTERFACE(IOMX, data, reply);
-
-            List<ComponentInfo> list;
-            listNodes(&list);
-
-            reply->writeInt32(list.size());
-            for (List<ComponentInfo>::iterator it = list.begin();
-                 it != list.end(); ++it) {
-                ComponentInfo &cur = *it;
-
-                reply->writeString8(cur.mName);
-                reply->writeInt32(cur.mRoles.size());
-                for (List<String8>::iterator role_it = cur.mRoles.begin();
-                     role_it != cur.mRoles.end(); ++role_it) {
-                    reply->writeString8(*role_it);
-                }
-            }
-
-            return NO_ERROR;
-        }
-
-        case ALLOCATE_NODE:
-        {
-            CHECK_OMX_INTERFACE(IOMX, data, reply);
-
-            const char *name = data.readCString();
-
-            sp<IOMXObserver> observer =
-                interface_cast<IOMXObserver>(data.readStrongBinder());
-
-            if (name == NULL || observer == NULL) {
-                ALOGE("b/26392700");
-                reply->writeInt32(INVALID_OPERATION);
-                return NO_ERROR;
-            }
-
-            sp<IOMXNode> omxNode;
-
-            status_t err = allocateNode(name, observer, &omxNode);
-
-            reply->writeInt32(err);
-            if (err == OK) {
-                reply->writeStrongBinder(IInterface::asBinder(omxNode));
-            }
-
-            return NO_ERROR;
-        }
-
-        case CREATE_INPUT_SURFACE:
-        {
-            CHECK_OMX_INTERFACE(IOMX, data, reply);
-
-            sp<IGraphicBufferProducer> bufferProducer;
-            sp<IGraphicBufferSource> bufferSource;
-            status_t err = createInputSurface(&bufferProducer, &bufferSource);
-
-            reply->writeInt32(err);
-
-            if (err == OK) {
-                reply->writeStrongBinder(IInterface::asBinder(bufferProducer));
-                reply->writeStrongBinder(IInterface::asBinder(bufferSource));
-            }
-
-            return NO_ERROR;
-        }
-
-        default:
-            return BBinder::onTransact(code, data, reply, flags);
-    }
-}
-
 status_t BnOMXNode::onTransact(
     uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
     switch (code) {
diff --git a/media/libmedia/include/media/IOMX.h b/media/libmedia/include/media/IOMX.h
index e69c02d..7e7c2d2 100644
--- a/media/libmedia/include/media/IOMX.h
+++ b/media/libmedia/include/media/IOMX.h
@@ -47,9 +47,8 @@
 
 using hardware::media::omx::V1_0::IOmxNode;
 
-class IOMX : public IInterface {
+class IOMX : public RefBase {
 public:
-    DECLARE_META_INTERFACE(OMX);
 
     typedef uint32_t buffer_id;
 
@@ -224,14 +223,6 @@
 };
 
 ////////////////////////////////////////////////////////////////////////////////
-
-class BnOMX : public BnInterface<IOMX> {
-public:
-    virtual status_t onTransact(
-            uint32_t code, const Parcel &data, Parcel *reply,
-            uint32_t flags = 0);
-};
-
 class BnOMXNode : public BnInterface<IOMXNode> {
 public:
     virtual status_t onTransact(
diff --git a/media/libmedia/include/media/TypeConverter.h b/media/libmedia/include/media/TypeConverter.h
index 3acfe98..2f8c209 100644
--- a/media/libmedia/include/media/TypeConverter.h
+++ b/media/libmedia/include/media/TypeConverter.h
@@ -305,8 +305,8 @@
     result << "{ Content type: " << toString(attributes.content_type)
            << " Usage: " << toString(attributes.usage)
            << " Source: " << toString(attributes.source)
-           << " Flags: " << attributes.flags
-           << " Tags: " << attributes.tags
+           << std::hex << " Flags: 0x" << attributes.flags
+           << std::dec << " Tags: " << attributes.tags
            << " }";
 
     return result.str();
diff --git a/media/libmedia/include/media/omx/1.0/Conversion.h b/media/libmedia/include/media/omx/1.0/Conversion.h
index 3700a23..babda22 100644
--- a/media/libmedia/include/media/omx/1.0/Conversion.h
+++ b/media/libmedia/include/media/omx/1.0/Conversion.h
@@ -31,12 +31,12 @@
 #include <ui/FenceTime.h>
 #include <cutils/native_handle.h>
 
+#include <ui/BufferQueueDefs.h>
 #include <ui/GraphicBuffer.h>
 #include <media/OMXFenceParcelable.h>
 #include <media/OMXBuffer.h>
 #include <media/hardware/VideoAPI.h>
 #include <media/stagefright/MediaErrors.h>
-#include <gui/IGraphicBufferProducer.h>
 
 #include <android/hardware/media/omx/1.0/types.h>
 #include <android/hardware/media/omx/1.0/IOmx.h>
@@ -282,8 +282,8 @@
     case TIMED_OUT:
     case ERROR_UNSUPPORTED:
     case UNKNOWN_ERROR:
-    case IGraphicBufferProducer::RELEASE_ALL_BUFFERS:
-    case IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION:
+    case BufferQueueDefs::RELEASE_ALL_BUFFERS:
+    case BufferQueueDefs::BUFFER_NEEDS_REALLOCATION:
         return static_cast<Status>(l);
     case NOT_ENOUGH_DATA:
         return Status::BUFFER_NEEDS_REALLOCATION;
diff --git a/media/libmedia/include/media/omx/1.0/WOmx.h b/media/libmedia/include/media/omx/1.0/WOmx.h
index f13546e..0680eec 100644
--- a/media/libmedia/include/media/omx/1.0/WOmx.h
+++ b/media/libmedia/include/media/omx/1.0/WOmx.h
@@ -47,7 +47,6 @@
 
 using ::android::List;
 using ::android::IOMX;
-using ::android::BnOMX;
 
 /**
  * Wrapper classes for conversion
@@ -58,7 +57,7 @@
  * - TW = Treble Wrapper --- It wraps a legacy object inside a Treble object.
  */
 
-struct LWOmx : public BnOMX {
+struct LWOmx : public IOMX {
     sp<IOmx> mBase;
     LWOmx(sp<IOmx> const& base);
     status_t listNodes(List<IOMX::ComponentInfo>* list) override;
diff --git a/media/libmediaplayer2/Android.bp b/media/libmediaplayer2/Android.bp
index 08519cd..dca6bb6 100644
--- a/media/libmediaplayer2/Android.bp
+++ b/media/libmediaplayer2/Android.bp
@@ -38,6 +38,7 @@
     export_shared_lib_headers: [
         "libaudioclient",
         "libbinder",
+        "libgui",
         "libmedia_omx",
     ],
 
diff --git a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
index 2993ab1..1e8a1d5 100644
--- a/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
+++ b/media/libmediaplayer2/include/mediaplayer2/mediaplayer2.h
@@ -143,6 +143,7 @@
     Mutex                       mNotifyLock;
     sp<MediaPlayer2Listener>    mListener;
     media_player2_internal_states mCurrentState;
+    bool                        mTransitionToNext;
     int64_t                     mCurrentPosition;
     MediaPlayer2SeekMode        mCurrentSeekMode;
     int64_t                     mSeekPosition;
diff --git a/media/libmediaplayer2/mediaplayer2.cpp b/media/libmediaplayer2/mediaplayer2.cpp
index ae7ac59..de65f8d 100644
--- a/media/libmediaplayer2/mediaplayer2.cpp
+++ b/media/libmediaplayer2/mediaplayer2.cpp
@@ -241,6 +241,7 @@
     mSeekPosition = -1;
     mSeekMode = MediaPlayer2SeekMode::SEEK_PREVIOUS_SYNC;
     mCurrentState = MEDIA_PLAYER2_IDLE;
+    mTransitionToNext = false;
     mLoop = false;
     mVolume = 1.0;
     mVideoWidth = mVideoHeight = 0;
@@ -389,6 +390,7 @@
         return INVALID_OPERATION;
     }
     mSrcId = srcId;
+    mTransitionToNext = true;
     return mPlayer->playNextDataSource(srcId);
 }
 
@@ -568,6 +570,7 @@
             mCurrentState = MEDIA_PLAYER2_STATE_ERROR;
         } else {
             mCurrentState = MEDIA_PLAYER2_PAUSED;
+            mTransitionToNext = false;
         }
         return ret;
     }
@@ -815,6 +818,7 @@
         } else {
             mPlayer->setListener(NULL);
             mCurrentState = MEDIA_PLAYER2_IDLE;
+            mTransitionToNext = false;
         }
         // setDataSource has to be called again to create a
         // new mediaplayer.
@@ -1026,8 +1030,10 @@
     case MEDIA2_NOP: // interface test message
         break;
     case MEDIA2_PREPARED:
-        ALOGV("MediaPlayer2::notify() prepared");
-        mCurrentState = MEDIA_PLAYER2_PREPARED;
+        ALOGV("MediaPlayer2::notify() prepared, srcId=%lld", (long long)srcId);
+        if (srcId == mSrcId) {
+            mCurrentState = MEDIA_PLAYER2_PREPARED;
+        }
         break;
     case MEDIA2_DRM_INFO:
         ALOGV("MediaPlayer2::notify() MEDIA2_DRM_INFO(%lld, %d, %d, %d, %p)",
@@ -1038,7 +1044,7 @@
         if (mCurrentState == MEDIA_PLAYER2_IDLE) {
             ALOGE("playback complete in idle state");
         }
-        if (!mLoop) {
+        if (!mLoop && srcId == mSrcId) {
             mCurrentState = MEDIA_PLAYER2_PLAYBACK_COMPLETE;
         }
         break;
@@ -1054,6 +1060,11 @@
         // ext2: Implementation dependant error code.
         if (ext1 != MEDIA2_INFO_VIDEO_TRACK_LAGGING) {
             ALOGW("info/warning (%d, %d)", ext1, ext2);
+
+            if (ext1 == MEDIA2_INFO_DATA_SOURCE_START && srcId == mSrcId && mTransitionToNext) {
+                mCurrentState = MEDIA_PLAYER2_STARTED;
+                mTransitionToNext = false;
+            }
         }
         break;
     case MEDIA2_SEEK_COMPLETE:
diff --git a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
index a8c9932..fd459df 100644
--- a/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
+++ b/media/libmediaplayer2/nuplayer2/NuPlayer2Renderer.cpp
@@ -76,6 +76,10 @@
 
 static const int64_t kMinimumAudioClockUpdatePeriodUs = 20 /* msec */ * 1000;
 
+// Default video frame display duration when only video exists.
+// Used to set max media time in MediaClock.
+static const int64_t kDefaultVideoFrameIntervalUs = 100000LL;
+
 // static
 const NuPlayer2::Renderer::PcmInfo NuPlayer2::Renderer::AUDIO_PCMINFO_INITIALIZER = {
         AUDIO_CHANNEL_NONE,
@@ -305,11 +309,11 @@
             mNotifyCompleteVideo |= notifyComplete;
             ++mVideoQueueGeneration;
             ++mVideoDrainGeneration;
+            mNextVideoTimeMediaUs = -1;
         }
 
         mMediaClock->clearAnchor();
         mVideoLateByUs = 0;
-        mNextVideoTimeMediaUs = -1;
         mSyncQueues = false;
     }
 
@@ -1288,7 +1292,7 @@
     mNextVideoTimeMediaUs = mediaTimeUs;
     if (!mHasAudio) {
         // smooth out videos >= 10fps
-        mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
+        mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs);
     }
 
     if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) {
@@ -1355,7 +1359,7 @@
                     && mediaTimeUs > mLastAudioMediaTimeUs) {
                 // If audio ends before video, video continues to drive media clock.
                 // Also smooth out videos >= 10fps.
-                mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
+                mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs);
             }
         }
     } else {
@@ -1430,7 +1434,8 @@
                 }
             } else {
                 mMediaClock->updateAnchor(
-                        mNextVideoTimeMediaUs, nowUs, mNextVideoTimeMediaUs + 100000);
+                        mNextVideoTimeMediaUs, nowUs,
+                        mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs);
             }
         }
     }
@@ -1583,6 +1588,14 @@
             notifyComplete = mNotifyCompleteAudio;
             mNotifyCompleteAudio = false;
             mLastAudioMediaTimeUs = -1;
+
+            mHasAudio = false;
+            if (mNextVideoTimeMediaUs >= 0) {
+                int64_t nowUs = ALooper::GetNowUs();
+                mMediaClock->updateAnchor(
+                        mNextVideoTimeMediaUs, nowUs,
+                        mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs);
+            }
         } else {
             notifyComplete = mNotifyCompleteVideo;
             mNotifyCompleteVideo = false;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 65d6d61..39be40d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -77,6 +77,10 @@
 
 static const int64_t kMinimumAudioClockUpdatePeriodUs = 20 /* msec */ * 1000;
 
+// Default video frame display duration when only video exists.
+// Used to set max media time in MediaClock.
+static const int64_t kDefaultVideoFrameIntervalUs = 100000LL;
+
 // static
 const NuPlayer::Renderer::PcmInfo NuPlayer::Renderer::AUDIO_PCMINFO_INITIALIZER = {
         AUDIO_CHANNEL_NONE,
@@ -314,11 +318,11 @@
             mNotifyCompleteVideo |= notifyComplete;
             ++mVideoQueueGeneration;
             ++mVideoDrainGeneration;
+            mNextVideoTimeMediaUs = -1;
         }
 
         mMediaClock->clearAnchor();
         mVideoLateByUs = 0;
-        mNextVideoTimeMediaUs = -1;
         mSyncQueues = false;
     }
 
@@ -1302,7 +1306,7 @@
     mNextVideoTimeMediaUs = mediaTimeUs;
     if (!mHasAudio) {
         // smooth out videos >= 10fps
-        mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
+        mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs);
     }
 
     if (!mVideoSampleReceived || mediaTimeUs < mAudioFirstAnchorTimeMediaUs) {
@@ -1369,7 +1373,7 @@
                     && mediaTimeUs > mLastAudioMediaTimeUs) {
                 // If audio ends before video, video continues to drive media clock.
                 // Also smooth out videos >= 10fps.
-                mMediaClock->updateMaxTimeMedia(mediaTimeUs + 100000);
+                mMediaClock->updateMaxTimeMedia(mediaTimeUs + kDefaultVideoFrameIntervalUs);
             }
         }
     } else {
@@ -1444,7 +1448,8 @@
                 }
             } else {
                 mMediaClock->updateAnchor(
-                        mNextVideoTimeMediaUs, nowUs, mNextVideoTimeMediaUs + 100000);
+                        mNextVideoTimeMediaUs, nowUs,
+                        mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs);
             }
         }
     }
@@ -1597,6 +1602,14 @@
             notifyComplete = mNotifyCompleteAudio;
             mNotifyCompleteAudio = false;
             mLastAudioMediaTimeUs = -1;
+
+            mHasAudio = false;
+            if (mNextVideoTimeMediaUs >= 0) {
+                int64_t nowUs = ALooper::GetNowUs();
+                mMediaClock->updateAnchor(
+                        mNextVideoTimeMediaUs, nowUs,
+                        mNextVideoTimeMediaUs + kDefaultVideoFrameIntervalUs);
+            }
         } else {
             notifyComplete = mNotifyCompleteVideo;
             mNotifyCompleteVideo = false;
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index cf1a6f1..d8de103 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -1776,7 +1776,7 @@
     }
 
     int32_t prependSPSPPS = 0;
-    if (encoder
+    if (encoder && mIsVideo
             && msg->findInt32("prepend-sps-pps-to-idr-frames", &prependSPSPPS)
             && prependSPSPPS != 0) {
         OMX_INDEXTYPE index;
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index b05718c..5932518 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -187,6 +187,7 @@
         "liblog",
         "libmedia",
         "libmedia_omx",
+        "libmedia_omx_client",
         "libaudioclient",
         "libmediametrics",
         "libmediautils",
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b6b7784..a7d37e5 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1934,6 +1934,13 @@
 
                 case kWhatComponentAllocated:
                 {
+                    if (mState == RELEASING || mState == UNINITIALIZED) {
+                        // In case a kWhatError or kWhatRelease message came in and replied,
+                        // we log a warning and ignore.
+                        ALOGW("allocate interrupted by error or release, current state %d",
+                              mState);
+                        break;
+                    }
                     CHECK_EQ(mState, INITIALIZING);
                     setState(INITIALIZED);
                     mFlags |= kFlagIsComponentAllocated;
diff --git a/media/libstagefright/bqhelper/GraphicBufferSource.cpp b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
index a4374c9..35df6d7 100644
--- a/media/libstagefright/bqhelper/GraphicBufferSource.cpp
+++ b/media/libstagefright/bqhelper/GraphicBufferSource.cpp
@@ -281,6 +281,27 @@
     }
 };
 
+struct GraphicBufferSource::ConsumerProxy : public BufferQueue::ConsumerListener {
+    ConsumerProxy(const sp<GraphicBufferSource> &gbs) : mGbs(gbs) {}
+
+    ~ConsumerProxy() = default;
+
+    void onFrameAvailable(const BufferItem& item) override {
+        mGbs->onFrameAvailable(item);
+    }
+
+    void onBuffersReleased() override {
+        mGbs->onBuffersReleased();
+    }
+
+    void onSidebandStreamChanged() override {
+        mGbs->onSidebandStreamChanged();
+    }
+
+private:
+    sp<GraphicBufferSource> mGbs;
+};
+
 GraphicBufferSource::GraphicBufferSource() :
     mInitCheck(UNKNOWN_ERROR),
     mNumAvailableUnacquiredBuffers(0),
@@ -313,14 +334,12 @@
     BufferQueue::createBufferQueue(&mProducer, &mConsumer);
     mConsumer->setConsumerName(name);
 
-    // Note that we can't create an sp<...>(this) in a ctor that will not keep a
-    // reference once the ctor ends, as that would cause the refcount of 'this'
-    // dropping to 0 at the end of the ctor.  Since all we need is a wp<...>
-    // that's what we create.
-    wp<BufferQueue::ConsumerListener> listener =
-            static_cast<BufferQueue::ConsumerListener*>(this);
+    // create the consumer listener interface, and hold sp so that this
+    // interface lives as long as the GraphicBufferSource.
+    mConsumerProxy = new ConsumerProxy(this);
+
     sp<IConsumerListener> proxy =
-            new BufferQueue::ProxyConsumerListener(listener);
+            new BufferQueue::ProxyConsumerListener(mConsumerProxy);
 
     mInitCheck = mConsumer->consumerConnect(proxy, false);
     if (mInitCheck != NO_ERROR) {
diff --git a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
index abc8910..99e444b 100644
--- a/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
+++ b/media/libstagefright/bqhelper/include/media/stagefright/bqhelper/GraphicBufferSource.h
@@ -68,7 +68,7 @@
  * (even if it was dropped) to reencode it after an interval if no further
  * frames are sent by the producer.
  */
-class GraphicBufferSource : public BufferQueue::ConsumerListener {
+class GraphicBufferSource : public RefBase {
 public:
     GraphicBufferSource();
 
@@ -190,8 +190,6 @@
     status_t setColorAspects(int32_t aspectsPacked);
 
 protected:
-    // BQ::ConsumerListener interface
-    // ------------------------------
 
     // BufferQueue::ConsumerListener interface, called when a new frame of
     // data is available.  If we're executing and a codec buffer is
@@ -199,19 +197,24 @@
     // into the codec buffer, and call Empty[This]Buffer.  If we're not yet
     // executing or there's no codec buffer available, we just increment
     // mNumFramesAvailable and return.
-    void onFrameAvailable(const BufferItem& item) override;
+    void onFrameAvailable(const BufferItem& item) ;
 
     // BufferQueue::ConsumerListener interface, called when the client has
     // released one or more GraphicBuffers.  We clear out the appropriate
     // set of mBufferSlot entries.
-    void onBuffersReleased() override;
+    void onBuffersReleased() ;
 
     // BufferQueue::ConsumerListener interface, called when the client has
     // changed the sideband stream. GraphicBufferSource doesn't handle sideband
     // streams so this is a no-op (and should never be called).
-    void onSidebandStreamChanged() override;
+    void onSidebandStreamChanged() ;
 
 private:
+    // BQ::ConsumerListener interface
+    // ------------------------------
+    struct ConsumerProxy;
+    sp<ConsumerProxy> mConsumerProxy;
+
     // Lock, covers all member variables.
     mutable Mutex mMutex;
 
diff --git a/media/libstagefright/codecs/aacdec/Android.bp b/media/libstagefright/codecs/aacdec/Android.bp
index e0bb5cd..46b3b8f 100644
--- a/media/libstagefright/codecs/aacdec/Android.bp
+++ b/media/libstagefright/codecs/aacdec/Android.bp
@@ -1,22 +1,12 @@
 cc_library_shared {
     name: "libstagefright_soft_aacdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: [
         "SoftAAC2.cpp",
         "DrcPresModeWrap.cpp",
     ],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -29,10 +19,7 @@
 
     static_libs: ["libFraunhoferAAC"],
 
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libcutils",
     ],
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/aacenc/Android.bp b/media/libstagefright/codecs/aacenc/Android.bp
index 0d677fe..bf789c4 100644
--- a/media/libstagefright/codecs/aacenc/Android.bp
+++ b/media/libstagefright/codecs/aacenc/Android.bp
@@ -1,19 +1,9 @@
 cc_library_shared {
     name: "libstagefright_soft_aacenc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftAACEncoder2.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -25,8 +15,4 @@
     },
 
     static_libs: ["libFraunhoferAAC"],
-
-    defaults: ["omx_soft_libs"],
-
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/amrnb/dec/Android.bp b/media/libstagefright/codecs/amrnb/dec/Android.bp
index f3b272b..e18117e 100644
--- a/media/libstagefright/codecs/amrnb/dec/Android.bp
+++ b/media/libstagefright/codecs/amrnb/dec/Android.bp
@@ -40,7 +40,6 @@
         "src/wmf_to_ets.cpp",
     ],
 
-    include_dirs: ["frameworks/av/media/libstagefright/include"],
     export_include_dirs: ["src"],
 
     cflags: [
@@ -68,23 +67,14 @@
 
 cc_library_shared {
     name: "libstagefright_soft_amrdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftAMR.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/av/media/libstagefright/codecs/amrwb/src",
-        "frameworks/native/include/media/openmax",
-    ],
     local_include_dirs: ["src"],
 
     cflags: [
         "-DOSCL_IMPORT_REF=",
-        "-Werror",
     ],
 
     version_script: "exports.lds",
@@ -101,13 +91,11 @@
         "libstagefright_amrwbdec",
     ],
 
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libstagefright_amrnb_common",
     ],
-    compile_multilib: "32",
 }
+
 //###############################################################################
 cc_test {
     name: "libstagefright_amrnbdec_test",
diff --git a/media/libstagefright/codecs/amrnb/enc/Android.bp b/media/libstagefright/codecs/amrnb/enc/Android.bp
index 1c8b511..438ed04 100644
--- a/media/libstagefright/codecs/amrnb/enc/Android.bp
+++ b/media/libstagefright/codecs/amrnb/enc/Android.bp
@@ -62,7 +62,7 @@
         "src/ton_stab.cpp",
     ],
 
-    include_dirs: ["frameworks/av/media/libstagefright/include"],
+    header_libs: ["libstagefright_headers"],
     export_include_dirs: ["src"],
 
     cflags: [
@@ -86,21 +86,12 @@
 
 cc_library_shared {
     name: "libstagefright_soft_amrnbenc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftAMRNBEncoder.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
     local_include_dirs: ["src"],
 
-    cflags: ["-Werror"],
-
     //addressing b/25409744
     //sanitize: {
     //    misc_undefined: [
@@ -110,12 +101,9 @@
 
     static_libs: ["libstagefright_amrnbenc"],
 
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libstagefright_amrnb_common",
     ],
-    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/amrwb/Android.bp b/media/libstagefright/codecs/amrwb/Android.bp
index 9fefd81..88cf7f2 100644
--- a/media/libstagefright/codecs/amrwb/Android.bp
+++ b/media/libstagefright/codecs/amrwb/Android.bp
@@ -44,7 +44,7 @@
         "src/weight_amrwb_lpc.cpp",
     ],
 
-    include_dirs: ["frameworks/av/media/libstagefright/include"],
+    header_libs: ["libstagefright_headers"],
 
     export_include_dirs: [
         "src",
diff --git a/media/libstagefright/codecs/amrwbenc/Android.bp b/media/libstagefright/codecs/amrwbenc/Android.bp
index 262962f..3beed66 100644
--- a/media/libstagefright/codecs/amrwbenc/Android.bp
+++ b/media/libstagefright/codecs/amrwbenc/Android.bp
@@ -142,20 +142,10 @@
 
 cc_library_shared {
     name: "libstagefright_soft_amrwbenc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftAMRWBEncoder.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -167,11 +157,8 @@
 
     static_libs: ["libstagefright_amrwbenc"],
 
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libstagefright_enc_common",
     ],
-    compile_multilib: "32",
 }
 
diff --git a/media/libstagefright/codecs/avcdec/Android.bp b/media/libstagefright/codecs/avcdec/Android.bp
index 567bcca..0bb6bb0 100644
--- a/media/libstagefright/codecs/avcdec/Android.bp
+++ b/media/libstagefright/codecs/avcdec/Android.bp
@@ -1,29 +1,16 @@
 cc_library_shared {
     name: "libstagefright_soft_avcdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     static_libs: ["libavcdec"],
     srcs: ["SoftAVCDec.cpp"],
 
     cflags: [
         "-Wall",
-        "-Werror",
     ],
 
     version_script: "exports.lds",
 
-    include_dirs: [
-        "external/libavc/decoder",
-        "external/libavc/common",
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    defaults: ["omx_soft_libs"],
-
     sanitize: {
         misc_undefined: [
             "signed-integer-overflow",
@@ -32,5 +19,4 @@
     },
 
     ldflags: ["-Wl,-Bsymbolic"],
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/avcenc/Android.bp b/media/libstagefright/codecs/avcenc/Android.bp
index 0cd39e1..980261c 100644
--- a/media/libstagefright/codecs/avcenc/Android.bp
+++ b/media/libstagefright/codecs/avcenc/Android.bp
@@ -1,23 +1,10 @@
 cc_library_shared {
     name: "libstagefright_soft_avcenc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     static_libs: ["libavcenc"],
     srcs: ["SoftAVCEnc.cpp"],
 
-    include_dirs: [
-        "external/libavc/encoder",
-        "external/libavc/common",
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/hardware",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    defaults: ["omx_soft_libs"],
-
     sanitize: {
         misc_undefined: [
             "signed-integer-overflow",
@@ -27,12 +14,9 @@
 
     cflags: [
         "-Wall",
-        "-Werror",
         "-Wno-unused-variable",
     ],
     ldflags: ["-Wl,-Bsymbolic"],
 
     version_script: "exports.lds",
-
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
index e0f2683..9db6465 100644
--- a/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
+++ b/media/libstagefright/codecs/avcenc/SoftAVCEnc.cpp
@@ -21,8 +21,8 @@
 
 #include "OMX_Video.h"
 
-#include <HardwareAPI.h>
-#include <MetadataBufferType.h>
+#include <media/hardware/HardwareAPI.h>
+#include <media/hardware/MetadataBufferType.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MediaErrors.h>
diff --git a/media/libstagefright/codecs/flac/dec/Android.bp b/media/libstagefright/codecs/flac/dec/Android.bp
index 18a3f6b..4064751 100644
--- a/media/libstagefright/codecs/flac/dec/Android.bp
+++ b/media/libstagefright/codecs/flac/dec/Android.bp
@@ -1,23 +1,11 @@
 cc_library_shared {
     name: "libstagefright_soft_flacdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: [
         "SoftFlacDecoder.cpp",
     ],
 
-    include_dirs: [
-        "external/flac/include",
-        "frameworks/av/media/libstagefright/flac/dec",
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -28,10 +16,7 @@
         cfi: true,
     },
 
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libstagefright_flacdec",
     ],
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/flac/enc/Android.bp b/media/libstagefright/codecs/flac/enc/Android.bp
index 4149ccd..d7d871a 100644
--- a/media/libstagefright/codecs/flac/enc/Android.bp
+++ b/media/libstagefright/codecs/flac/enc/Android.bp
@@ -1,15 +1,9 @@
 cc_library_shared {
+    name: "libstagefright_soft_flacenc",
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftFlacEncoder.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-        "external/flac/include",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -19,19 +13,10 @@
         ],
         cfi: true,
     },
-    defaults: ["omx_soft_libs"],
 
     header_libs: ["libbase_headers"],
     static_libs: [
         "libaudioutils",
         "libFLAC",
     ],
-
-    name: "libstagefright_soft_flacenc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/g711/dec/Android.bp b/media/libstagefright/codecs/g711/dec/Android.bp
index c273179..f5357f4 100644
--- a/media/libstagefright/codecs/g711/dec/Android.bp
+++ b/media/libstagefright/codecs/g711/dec/Android.bp
@@ -1,21 +1,9 @@
 cc_library_shared {
     name: "libstagefright_soft_g711dec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftG711.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    defaults: ["omx_soft_libs"],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -25,5 +13,4 @@
         ],
         cfi: true,
     },
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/gsm/dec/Android.bp b/media/libstagefright/codecs/gsm/dec/Android.bp
index 3c5ebfe..5672d89 100644
--- a/media/libstagefright/codecs/gsm/dec/Android.bp
+++ b/media/libstagefright/codecs/gsm/dec/Android.bp
@@ -1,20 +1,9 @@
 cc_library_shared {
     name: "libstagefright_soft_gsmdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftGSM.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-        "external/libgsm/inc",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -25,8 +14,5 @@
         cfi: true,
     },
 
-    defaults: ["omx_soft_libs"],
-
     static_libs: ["libgsm"],
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/hevcdec/Android.bp b/media/libstagefright/codecs/hevcdec/Android.bp
index cc91d53..ec436ce 100644
--- a/media/libstagefright/codecs/hevcdec/Android.bp
+++ b/media/libstagefright/codecs/hevcdec/Android.bp
@@ -1,28 +1,17 @@
 cc_library_shared {
     name: "libstagefright_soft_hevcdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     static_libs: ["libhevcdec"],
     srcs: ["SoftHEVC.cpp"],
 
     cflags: [
         "-Wall",
-        "-Werror",
         "-Wno-unused-variable",
     ],
 
     version_script: "exports.lds",
 
-    include_dirs: [
-        "external/libhevc/decoder",
-        "external/libhevc/common",
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
     sanitize: {
         misc_undefined: [
             "signed-integer-overflow",
@@ -30,11 +19,8 @@
         cfi: true,
     },
 
-    defaults: ["omx_soft_libs"],
-
     // We need this because the current asm generates the following link error:
     // requires unsupported dynamic reloc R_ARM_REL32; recompile with -fPIC
     // Bug: 16853291
     ldflags: ["-Wl,-Bsymbolic"],
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index 0523143..6b45ea2 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -38,9 +38,9 @@
         "src/zigzag_tab.cpp",
     ],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
+    header_libs: [
+        "media_plugin_headers",
+        "libstagefright_headers"
     ],
 
     local_include_dirs: ["src"],
@@ -67,37 +67,23 @@
 
 cc_library_shared {
     name: "libstagefright_soft_mpeg4dec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftMPEG4.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
     local_include_dirs: ["src"],
-    export_include_dirs: ["include"],
 
     cflags: [
         "-DOSCL_EXPORT_REF=",
         "-DOSCL_IMPORT_REF=",
-
-        "-Werror",
     ],
 
     static_libs: ["libstagefright_m4vh263dec"],
 
-    defaults: ["omx_soft_libs"],
-
     sanitize: {
         misc_undefined: [
             "signed-integer-overflow",
         ],
         cfi: true,
     },
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/m4v_h263/enc/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
index d38f4b1..2738187 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/enc/Android.bp
@@ -32,10 +32,6 @@
 
     version_script: "exports.lds",
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
     local_include_dirs: ["src"],
     export_include_dirs: ["include"],
 
@@ -51,41 +47,27 @@
 
 cc_library_shared {
     name: "libstagefright_soft_mpeg4enc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftMPEG4Encoder.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-        "frameworks/native/include/media/hardware",
-    ],
     local_include_dirs: ["src"],
-    export_include_dirs: ["include"],
 
     cflags: [
         "-DBX_RC",
         "-DOSCL_IMPORT_REF=",
         "-DOSCL_UNUSED_ARG(x)=(void)(x)",
         "-DOSCL_EXPORT_REF=",
-
-        "-Werror",
     ],
 
     static_libs: ["libstagefright_m4vh263enc"],
 
-    defaults: ["omx_soft_libs"],
-
     sanitize: {
         misc_undefined: [
             "signed-integer-overflow",
         ],
         cfi: true,
     },
-    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
index f6a7b0e..fa7db81 100644
--- a/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
+++ b/media/libstagefright/codecs/m4v_h263/enc/SoftMPEG4Encoder.cpp
@@ -22,8 +22,8 @@
 #include "mp4enc_api.h"
 #include "OMX_Video.h"
 
-#include <HardwareAPI.h>
-#include <MetadataBufferType.h>
+#include <media/hardware/HardwareAPI.h>
+#include <media/hardware/MetadataBufferType.h>
 #include <media/stagefright/foundation/ADebug.h>
 #include <media/stagefright/foundation/AUtils.h>
 #include <media/stagefright/MediaDefs.h>
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index 9173ed6..b630524 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -78,24 +78,15 @@
 
 cc_library_shared {
     name: "libstagefright_soft_mp3dec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftMP3.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
     local_include_dirs: [
         "src",
         "include",
     ],
 
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -105,10 +96,7 @@
         cfi: true,
     },
 
-    defaults: ["omx_soft_libs"],
-
     static_libs: ["libstagefright_mp3dec"],
-    compile_multilib: "32",
 }
 
 //###############################################################################
diff --git a/media/libstagefright/codecs/mpeg2dec/Android.bp b/media/libstagefright/codecs/mpeg2dec/Android.bp
index 26e786e..e849410 100644
--- a/media/libstagefright/codecs/mpeg2dec/Android.bp
+++ b/media/libstagefright/codecs/mpeg2dec/Android.bp
@@ -1,27 +1,17 @@
 cc_library_shared {
     name: "libstagefright_soft_mpeg2dec",
-    vendor_available: true,
+    defaults: ["libstagefright_softomx-defaults"],
 
     static_libs: ["libmpeg2dec"],
     srcs: ["SoftMPEG2.cpp"],
 
     cflags: [
         "-Wall",
-        "-Werror",
         "-Wno-unused-variable",
     ],
 
     version_script: "exports.lds",
 
-    include_dirs: [
-        "external/libmpeg2/decoder",
-        "external/libmpeg2/common",
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    defaults: ["omx_soft_libs"],
-
     ldflags: ["-Wl,-Bsymbolic"],
 
     sanitize: {
@@ -30,5 +20,4 @@
         ],
         cfi: true,
     },
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/on2/dec/Android.bp b/media/libstagefright/codecs/on2/dec/Android.bp
index abd21d7..577231c 100644
--- a/media/libstagefright/codecs/on2/dec/Android.bp
+++ b/media/libstagefright/codecs/on2/dec/Android.bp
@@ -1,23 +1,11 @@
 cc_library_shared {
     name: "libstagefright_soft_vpxdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftVPX.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
     static_libs: ["libvpx"],
 
-    defaults: ["omx_soft_libs"],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -27,5 +15,4 @@
         ],
         cfi: true,
     },
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/on2/enc/Android.bp b/media/libstagefright/codecs/on2/enc/Android.bp
index ea46bad..82c215e 100644
--- a/media/libstagefright/codecs/on2/enc/Android.bp
+++ b/media/libstagefright/codecs/on2/enc/Android.bp
@@ -1,9 +1,6 @@
 cc_library_shared {
     name: "libstagefright_soft_vpxenc",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: [
         "SoftVPXEncoder.cpp",
@@ -11,15 +8,10 @@
         "SoftVP9Encoder.cpp",
     ],
 
-    cflags: ["-Wall", "-Werror"],
+    cflags: ["-Wall"],
 
     version_script: "exports.lds",
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
     sanitize: {
         misc_undefined: [
             "signed-integer-overflow",
@@ -29,8 +21,4 @@
     },
 
     static_libs: ["libvpx"],
-
-    defaults: ["omx_soft_libs"],
-
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/opus/dec/Android.bp b/media/libstagefright/codecs/opus/dec/Android.bp
index bfcae07..71a2a0d 100644
--- a/media/libstagefright/codecs/opus/dec/Android.bp
+++ b/media/libstagefright/codecs/opus/dec/Android.bp
@@ -1,25 +1,13 @@
 cc_library_shared {
     name: "libstagefright_soft_opusdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftOpus.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libopus",
     ],
 
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -29,5 +17,4 @@
         ],
         cfi: true,
     },
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/raw/Android.bp b/media/libstagefright/codecs/raw/Android.bp
index 1c23bad..fcc7a0a 100644
--- a/media/libstagefright/codecs/raw/Android.bp
+++ b/media/libstagefright/codecs/raw/Android.bp
@@ -1,19 +1,9 @@
 cc_library_shared {
     name: "libstagefright_soft_rawdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftRaw.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -23,8 +13,4 @@
         ],
         cfi: true,
     },
-
-    defaults: ["omx_soft_libs"],
-
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/vorbis/dec/Android.bp b/media/libstagefright/codecs/vorbis/dec/Android.bp
index 2d1a922..3efb952 100644
--- a/media/libstagefright/codecs/vorbis/dec/Android.bp
+++ b/media/libstagefright/codecs/vorbis/dec/Android.bp
@@ -1,25 +1,13 @@
 cc_library_shared {
     name: "libstagefright_soft_vorbisdec",
-    vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: ["SoftVorbis.cpp"],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libvorbisidec",
     ],
 
-    cflags: ["-Werror"],
-
     version_script: "exports.lds",
 
     sanitize: {
@@ -28,5 +16,4 @@
             "unsigned-integer-overflow",
         ],
     },
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/codecs/xaacdec/Android.bp b/media/libstagefright/codecs/xaacdec/Android.bp
index e49eb8f..5385dbc 100644
--- a/media/libstagefright/codecs/xaacdec/Android.bp
+++ b/media/libstagefright/codecs/xaacdec/Android.bp
@@ -1,18 +1,12 @@
 cc_library_shared {
     name: "libstagefright_soft_xaacdec",
-    vendor_available: true,
+    defaults: ["libstagefright_softomx-defaults"],
 
     srcs: [
         "SoftXAAC.cpp",
     ],
 
-    include_dirs: [
-        "frameworks/av/media/libstagefright/include",
-        "frameworks/native/include/media/openmax",
-    ],
-
     cflags: [
-        "-Werror",
         "-DENABLE_MPEG_D_DRC"
     ],
 
@@ -24,11 +18,7 @@
 
     static_libs: ["libxaacdec"],
 
-    defaults: ["omx_soft_libs"],
-
     shared_libs: [
         "libcutils",
     ],
-
-    compile_multilib: "32",
 }
diff --git a/media/libstagefright/flac/dec/Android.bp b/media/libstagefright/flac/dec/Android.bp
index 307c9b0..b270808 100644
--- a/media/libstagefright/flac/dec/Android.bp
+++ b/media/libstagefright/flac/dec/Android.bp
@@ -11,11 +11,6 @@
 
     export_include_dirs: [ "." ],
 
-    include_dirs: [
-        "external/flac/include",
-        "frameworks/av/media/libstagefright/include",
-    ],
-
     cflags: ["-Werror"],
 
     sanitize: {
@@ -38,10 +33,17 @@
             "libFLAC",
             "libaudioutils",
         ],
+        export_static_lib_headers: [
+            "libFLAC",
+        ],
     },
 
     shared_libs: [
         "liblog",
     ],
-    header_libs: ["libmedia_headers"],
+
+    header_libs: [
+        "libmedia_headers",
+        "libFLAC-headers",
+    ],
 }
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index c62c2cd..635ecfe 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -160,6 +160,7 @@
       mPlaylistTimeUs(-1LL),
       mSeqNumber(-1),
       mNumRetries(0),
+      mNumRetriesForMonitorQueue(0),
       mStartup(true),
       mIDRFound(false),
       mSeekMode(LiveSession::kSeekModeExactPosition),
@@ -849,7 +850,17 @@
     // in the middle of an unfinished download, delay
     // playlist refresh as it'll change seq numbers
     if (!mDownloadState->hasSavedState()) {
-        refreshPlaylist();
+        status_t err = refreshPlaylist();
+        if (err != OK) {
+            if (mNumRetriesForMonitorQueue < kMaxNumRetries) {
+                ++mNumRetriesForMonitorQueue;
+            } else {
+                notifyError(err);
+            }
+            return;
+        } else {
+            mNumRetriesForMonitorQueue = 0;
+        }
     }
 
     int64_t targetDurationUs = kMinBufferedDurationUs;
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index d7db54a..5d3f9c1 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -145,6 +145,7 @@
     sp<M3UParser> mPlaylist;
     int32_t mSeqNumber;
     int32_t mNumRetries;
+    int32_t mNumRetriesForMonitorQueue;
     bool mStartup;
     bool mIDRFound;
     int32_t mSeekMode;
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 8b6944b..c84614a 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -783,6 +783,7 @@
 constexpr char KEY_OPERATING_RATE[] = "operating-rate";
 constexpr char KEY_OUTPUT_REORDER_DEPTH[] = "output-reorder-depth";
 constexpr char KEY_PCM_ENCODING[] = "pcm-encoding";
+constexpr char KEY_PREPEND_HEADERS_TO_SYNC_FRAMES[] = "prepend-sps-pps-to-idr-frames";
 constexpr char KEY_PRIORITY[] = "priority";
 constexpr char KEY_PROFILE[] = "profile";
 constexpr char KEY_PUSH_BLANK_BUFFERS_ON_STOP[] = "push-blank-buffers-on-shutdown";
diff --git a/media/libstagefright/omx/Android.bp b/media/libstagefright/omx/Android.bp
index c06aca5..0c50752 100644
--- a/media/libstagefright/omx/Android.bp
+++ b/media/libstagefright/omx/Android.bp
@@ -6,7 +6,6 @@
     },
 
     srcs: [
-        "BWGraphicBufferSource.cpp",
         "OMXMaster.cpp",
         "OMXNodeInstance.cpp",
         "OMXUtils.cpp",
@@ -39,10 +38,10 @@
         "libutils",
         "liblog",
         "libui",
-        "libgui",
         "libcutils",
         "libstagefright_foundation",
         "libstagefright_bufferqueue_helper",
+        "libstagefright_softomx",
         "libstagefright_xmlparser",
         "libdl",
         "libhidlbase",
@@ -51,11 +50,9 @@
         "libvndksupport",
         "android.hardware.media.omx@1.0",
         "android.hardware.graphics.bufferqueue@1.0",
-        "libstagefright_omx_soft",
     ],
 
     export_shared_lib_headers: [
-        "libmedia_omx",
         "libstagefright_foundation",
         "libstagefright_xmlparser",
         "libutils",
@@ -75,55 +72,77 @@
         ],
         cfi: true,
     },
+
+    compile_multilib: "32",
+}
+
+cc_library_shared {
+    name: "libstagefright_softomx",
+    vendor_available: true,
+    vndk: {
+        enabled: true,
+    },
+
+    srcs: [
+        "SimpleSoftOMXComponent.cpp",
+        "SoftOMXComponent.cpp",
+        "SoftOMXPlugin.cpp",
+        "SoftVideoDecoderOMXComponent.cpp",
+        "SoftVideoEncoderOMXComponent.cpp",
+    ],
+
+    export_include_dirs: [
+        "include",
+    ],
+
+    header_libs: [
+        "media_plugin_headers",
+    ],
+
+    export_header_lib_headers: [
+        "media_plugin_headers",
+    ],
+
+    shared_libs: [
+        "libstagefright_foundation",
+        "liblog",
+        "libui",
+        "libutils",
+    ],
+
+    cflags: [
+        "-Werror",
+        "-Wall",
+        "-Wno-unused-parameter",
+        "-Wno-documentation",
+    ],
+
+    sanitize: {
+        misc_undefined: [
+            "signed-integer-overflow",
+            "unsigned-integer-overflow",
+        ],
+        cfi: true,
+    },
 }
 
 cc_defaults {
-    name: "omx_soft_libs",
-    shared_libs: [
-        "libutils",
-        "liblog",
-        "libstagefright_foundation",
-        "libstagefright_omx_soft",
-    ],
-}
-
-cc_library_shared {
-    name: "libstagefright_omx_soft",
+    name: "libstagefright_softomx-defaults",
     vendor_available: true,
-    vndk: {
-        enabled: true,
-    },
-
-    srcs: [
-        "SimpleSoftOMXComponent.cpp",
-        "SoftOMXComponent.cpp",
-        "SoftOMXPlugin.cpp",
-        "SoftVideoDecoderOMXComponent.cpp",
-        "SoftVideoEncoderOMXComponent.cpp",
-    ],
-
-    export_include_dirs: [
-        "include",
-    ],
-
-    shared_libs: [
-        "libutils",
-        "liblog",
-        "libui",
-        "libstagefright_foundation",
-    ],
-
-    export_shared_lib_headers: [
-        "libstagefright_foundation",
-        "libutils",
-        "liblog",
-    ],
 
     cflags: [
         "-Werror",
-        "-Wall",
-        "-Wno-unused-parameter",
-        "-Wno-documentation",
+    ],
+
+    header_libs: [
+        "media_plugin_headers"
+    ],
+
+    shared_libs: [
+        "libstagefright_softomx",
+        "libstagefright_foundation",
+        "libutils",
+        "liblog",
     ],
 
     sanitize: {
@@ -133,6 +152,8 @@
         ],
         cfi: true,
     },
+
+    compile_multilib: "32",
 }
 
 cc_library_shared {
diff --git a/media/libstagefright/omx/BWGraphicBufferSource.cpp b/media/libstagefright/omx/BWGraphicBufferSource.cpp
deleted file mode 100644
index fa30a46..0000000
--- a/media/libstagefright/omx/BWGraphicBufferSource.cpp
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * 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.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "BWGraphicBufferSource"
-
-#include <media/stagefright/omx/BWGraphicBufferSource.h>
-#include <media/stagefright/omx/OMXUtils.h>
-#include <media/openmax/OMX_Component.h>
-#include <media/openmax/OMX_IndexExt.h>
-#include <media/OMXBuffer.h>
-#include <media/IOMX.h>
-
-namespace android {
-
-static const OMX_U32 kPortIndexInput = 0;
-
-struct BWGraphicBufferSource::BWOmxNodeWrapper : public IOmxNodeWrapper {
-    sp<IOMXNode> mOMXNode;
-
-    BWOmxNodeWrapper(const sp<IOMXNode> &omxNode): mOMXNode(omxNode) {
-    }
-
-    virtual status_t emptyBuffer(
-            int32_t bufferId, uint32_t flags,
-            const sp<GraphicBuffer> &buffer,
-            int64_t timestamp, int fenceFd) override {
-        return mOMXNode->emptyBuffer(bufferId, buffer, flags, timestamp, fenceFd);
-    }
-
-    virtual void dispatchDataSpaceChanged(
-            int32_t dataSpace, int32_t aspects, int32_t pixelFormat) override {
-        omx_message msg;
-        msg.type = omx_message::EVENT;
-        msg.fenceFd = -1;
-        msg.u.event_data.event = OMX_EventDataSpaceChanged;
-        msg.u.event_data.data1 = dataSpace;
-        msg.u.event_data.data2 = aspects;
-        msg.u.event_data.data3 = pixelFormat;
-        mOMXNode->dispatchMessage(msg);
-    }
-};
-
-struct BWGraphicBufferSource::BWOMXBufferSource : public BnOMXBufferSource {
-    sp<OmxGraphicBufferSource> mSource;
-
-    BWOMXBufferSource(const sp<OmxGraphicBufferSource> &source): mSource(source) {
-    }
-
-    Status onOmxExecuting() override {
-        return mSource->onOmxExecuting();
-    }
-
-    Status onOmxIdle() override {
-        return mSource->onOmxIdle();
-    }
-
-    Status onOmxLoaded() override {
-        return mSource->onOmxLoaded();
-    }
-
-    Status onInputBufferAdded(int bufferId) override {
-        return mSource->onInputBufferAdded(bufferId);
-    }
-
-    Status onInputBufferEmptied(
-            int bufferId, const OMXFenceParcelable& fenceParcel) override {
-        return mSource->onInputBufferEmptied(bufferId, fenceParcel.get());
-    }
-};
-
-BWGraphicBufferSource::BWGraphicBufferSource(
-        sp<OmxGraphicBufferSource> const& base) :
-    mBase(base),
-    mOMXBufferSource(new BWOMXBufferSource(base)) {
-}
-
-::android::binder::Status BWGraphicBufferSource::configure(
-        const sp<IOMXNode>& omxNode, int32_t dataSpace) {
-    // Do setInputSurface() first, the node will try to enable metadata
-    // mode on input, and does necessary error checking. If this fails,
-    // we can't use this input surface on the node.
-    status_t err = omxNode->setInputSurface(mOMXBufferSource);
-    if (err != NO_ERROR) {
-        ALOGE("Unable to set input surface: %d", err);
-        return Status::fromStatusT(err);
-    }
-
-    // use consumer usage bits queried from encoder, but always add
-    // HW_VIDEO_ENCODER for backward compatibility.
-    uint32_t consumerUsage;
-    if (omxNode->getParameter(
-            (OMX_INDEXTYPE)OMX_IndexParamConsumerUsageBits,
-            &consumerUsage, sizeof(consumerUsage)) != OK) {
-        consumerUsage = 0;
-    }
-
-    OMX_PARAM_PORTDEFINITIONTYPE def;
-    InitOMXParams(&def);
-    def.nPortIndex = kPortIndexInput;
-
-    err = omxNode->getParameter(
-            OMX_IndexParamPortDefinition, &def, sizeof(def));
-    if (err != NO_ERROR) {
-        ALOGE("Failed to get port definition: %d", err);
-        return Status::fromStatusT(UNKNOWN_ERROR);
-    }
-
-    return Status::fromStatusT(mBase->configure(
-              new BWOmxNodeWrapper(omxNode),
-              dataSpace,
-              def.nBufferCountActual,
-              def.format.video.nFrameWidth,
-              def.format.video.nFrameHeight,
-              consumerUsage));
-}
-
-::android::binder::Status BWGraphicBufferSource::setSuspend(
-        bool suspend, int64_t timeUs) {
-    return Status::fromStatusT(mBase->setSuspend(suspend, timeUs));
-}
-
-::android::binder::Status BWGraphicBufferSource::setRepeatPreviousFrameDelayUs(
-        int64_t repeatAfterUs) {
-    return Status::fromStatusT(mBase->setRepeatPreviousFrameDelayUs(repeatAfterUs));
-}
-
-::android::binder::Status BWGraphicBufferSource::setMaxFps(float maxFps) {
-    return Status::fromStatusT(mBase->setMaxFps(maxFps));
-}
-
-::android::binder::Status BWGraphicBufferSource::setTimeLapseConfig(
-        double fps, double captureFps) {
-    return Status::fromStatusT(mBase->setTimeLapseConfig(
-            fps, captureFps));
-}
-
-::android::binder::Status BWGraphicBufferSource::setStartTimeUs(
-        int64_t startTimeUs) {
-    return Status::fromStatusT(mBase->setStartTimeUs(startTimeUs));
-}
-
-::android::binder::Status BWGraphicBufferSource::setStopTimeUs(
-        int64_t stopTimeUs) {
-    return Status::fromStatusT(mBase->setStopTimeUs(stopTimeUs));
-}
-
-::android::binder::Status BWGraphicBufferSource::getStopTimeOffsetUs(
-        int64_t *stopTimeOffsetUs) {
-    return Status::fromStatusT(mBase->getStopTimeOffsetUs(stopTimeOffsetUs));
-}
-
-::android::binder::Status BWGraphicBufferSource::setColorAspects(
-        int32_t aspects) {
-    return Status::fromStatusT(mBase->setColorAspects(aspects));
-}
-
-::android::binder::Status BWGraphicBufferSource::setTimeOffsetUs(
-        int64_t timeOffsetsUs) {
-    return Status::fromStatusT(mBase->setTimeOffsetUs(timeOffsetsUs));
-}
-
-::android::binder::Status BWGraphicBufferSource::signalEndOfInputStream() {
-    return Status::fromStatusT(mBase->signalEndOfInputStream());
-}
-
-}  // namespace android
diff --git a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h b/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h
deleted file mode 100644
index 0efff22..0000000
--- a/media/libstagefright/omx/include/media/stagefright/omx/BWGraphicBufferSource.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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 BWGRAPHIC_BUFFER_SOURCE_H_
-#define BWGRAPHIC_BUFFER_SOURCE_H_
-
-#include <binder/Binder.h>
-#include <binder/Status.h>
-#include <android/BnGraphicBufferSource.h>
-#include <android/BnOMXBufferSource.h>
-#include <media/IOMX.h>
-
-#include "OmxGraphicBufferSource.h"
-#include "IOmxNodeWrapper.h"
-
-namespace android {
-
-using ::android::binder::Status;
-using ::android::BnGraphicBufferSource;
-using ::android::OmxGraphicBufferSource;
-using ::android::IOMXNode;
-using ::android::sp;
-
-struct BWGraphicBufferSource : public BnGraphicBufferSource {
-    struct BWOMXBufferSource;
-    struct BWOmxNodeWrapper;
-
-    sp<OmxGraphicBufferSource> mBase;
-    sp<IOMXBufferSource> mOMXBufferSource;
-
-    BWGraphicBufferSource(sp<OmxGraphicBufferSource> const &base);
-
-    Status configure(
-            const sp<IOMXNode>& omxNode, int32_t dataSpace) override;
-    Status setSuspend(bool suspend, int64_t timeUs) override;
-    Status setRepeatPreviousFrameDelayUs(
-            int64_t repeatAfterUs) override;
-    Status setMaxFps(float maxFps) override;
-    Status setTimeLapseConfig(
-            double fps, double captureFps) override;
-    Status setStartTimeUs(int64_t startTimeUs) override;
-    Status setStopTimeUs(int64_t stopTimeUs) override;
-    Status getStopTimeOffsetUs(int64_t* stopTimeOffsetUs) override;
-    Status setColorAspects(int32_t aspects) override;
-    Status setTimeOffsetUs(int64_t timeOffsetsUs) override;
-    Status signalEndOfInputStream() override;
-};
-
-}  // namespace android
-
-#endif  // ANDROID_HARDWARE_MEDIA_OMX_V1_0_WGRAPHICBUFFERSOURCE_H
diff --git a/media/libstagefright/omx/tests/Android.bp b/media/libstagefright/omx/tests/Android.bp
index fb03229..569fa88 100644
--- a/media/libstagefright/omx/tests/Android.bp
+++ b/media/libstagefright/omx/tests/Android.bp
@@ -18,12 +18,6 @@
         "libnativewindow",
         "android.hidl.allocator@1.0",
         "android.hidl.memory@1.0",
-        "android.hardware.media.omx@1.0",
-    ],
-
-    include_dirs: [
-        "frameworks/av/media/libstagefright",
-        "frameworks/native/include/media/openmax",
     ],
 
     header_libs: [
diff --git a/media/libstagefright/omx/tests/OMXHarness.cpp b/media/libstagefright/omx/tests/OMXHarness.cpp
index c2f1072..cc8c234 100644
--- a/media/libstagefright/omx/tests/OMXHarness.cpp
+++ b/media/libstagefright/omx/tests/OMXHarness.cpp
@@ -40,9 +40,8 @@
 #include <media/stagefright/MediaErrors.h>
 #include <media/stagefright/MediaExtractorFactory.h>
 #include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXClient.h>
 #include <media/stagefright/SimpleDecodingSource.h>
-#include <android/hardware/media/omx/1.0/IOmx.h>
-#include <media/omx/1.0/WOmx.h>
 #include <system/window.h>
 
 #define DEFAULT_TIMEOUT         500000
@@ -81,12 +80,13 @@
 }
 
 status_t Harness::initOMX() {
-    using namespace ::android::hardware::media::omx::V1_0;
-    sp<IOmx> tOmx = IOmx::getService();
-    if (tOmx == nullptr) {
+    OMXClient client;
+    if (client.connect() != OK) {
+        ALOGE("Failed to connect to OMX to create persistent input surface.");
         return NO_INIT;
     }
-    mOMX = new utils::LWOmx(tOmx);
+
+    mOMX = client.interface();
 
     return mOMX != 0 ? OK : NO_INIT;
 }
diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt
index 5443f2c..f7f4c36 100644
--- a/media/libstagefright/xmlparser/api/current.txt
+++ b/media/libstagefright/xmlparser/api/current.txt
@@ -37,13 +37,10 @@
 
   public class Included {
     ctor public Included();
-    method public media.codecs.Decoders getDecoders_optional();
-    method public media.codecs.Encoders getEncoders_optional();
+    method public java.util.List<media.codecs.Decoders> getDecoders_optional();
+    method public java.util.List<media.codecs.Encoders> getEncoders_optional();
     method public java.util.List<media.codecs.Include> getInclude_optional();
-    method public media.codecs.Settings getSettings_optional();
-    method public void setDecoders_optional(media.codecs.Decoders);
-    method public void setEncoders_optional(media.codecs.Encoders);
-    method public void setSettings_optional(media.codecs.Settings);
+    method public java.util.List<media.codecs.Settings> getSettings_optional();
   }
 
   public class Limit {
@@ -85,13 +82,10 @@
 
   public class MediaCodecs {
     ctor public MediaCodecs();
-    method public media.codecs.Decoders getDecoders_optional();
-    method public media.codecs.Encoders getEncoders_optional();
+    method public java.util.List<media.codecs.Decoders> getDecoders_optional();
+    method public java.util.List<media.codecs.Encoders> getEncoders_optional();
     method public java.util.List<media.codecs.Include> getInclude_optional();
-    method public media.codecs.Settings getSettings_optional();
-    method public void setDecoders_optional(media.codecs.Decoders);
-    method public void setEncoders_optional(media.codecs.Encoders);
-    method public void setSettings_optional(media.codecs.Settings);
+    method public java.util.List<media.codecs.Settings> getSettings_optional();
   }
 
   public class Quirk {
diff --git a/media/mediaserver/Android.bp b/media/mediaserver/Android.bp
index 6a1cc71..a968890 100644
--- a/media/mediaserver/Android.bp
+++ b/media/mediaserver/Android.bp
@@ -43,4 +43,5 @@
         "-Wall",
     ],
 
+    vintf_fragments: ["manifest_media_c2_software.xml"],
 }
diff --git a/media/mediaserver/manifest_media_c2_software.xml b/media/mediaserver/manifest_media_c2_software.xml
new file mode 100644
index 0000000..5196336
--- /dev/null
+++ b/media/mediaserver/manifest_media_c2_software.xml
@@ -0,0 +1,11 @@
+<manifest version="1.0" type="framework">
+    <hal>
+        <name>android.hardware.media.c2</name>
+        <transport>hwbinder</transport>
+        <version>1.0</version>
+        <interface>
+            <name>IComponentStore</name>
+            <instance>software</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/media/ndk/Android.bp b/media/ndk/Android.bp
index a4f5730..7d1c88b 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -81,7 +81,6 @@
         "libmediadrm",
         "libstagefright",
         "libstagefright_foundation",
-        "libstagefright_bufferqueue_helper",
         "liblog",
         "libutils",
         "libcutils",
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 22e15d3..baa4fc7 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -29,7 +29,7 @@
 #include <ui/PublicFormat.h>
 #include <private/android/AHardwareBufferHelpers.h>
 #include <grallocusage/GrallocUsageConversion.h>
-#include <media/stagefright/bqhelper/WGraphicBufferProducer.h>
+#include <gui/bufferqueue/1.0/WGraphicBufferProducer.h>
 
 using namespace android;
 
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 04d62fa..6e2497f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -3610,8 +3610,30 @@
 
             // only process effects if we're going to write
             if (mSleepTimeUs == 0 && mType != OFFLOAD) {
+                audio_session_t activeHapticId = AUDIO_SESSION_NONE;
+                if (mHapticChannelCount > 0 && effectChains.size() > 0) {
+                    for (auto track : mActiveTracks) {
+                        if (track->getHapticPlaybackEnabled()) {
+                            activeHapticId = track->sessionId();
+                            break;
+                        }
+                    }
+                }
                 for (size_t i = 0; i < effectChains.size(); i ++) {
                     effectChains[i]->process_l();
+                    // TODO: Write haptic data directly to sink buffer when mixing.
+                    if (activeHapticId != AUDIO_SESSION_NONE
+                            && activeHapticId == effectChains[i]->sessionId()) {
+                        // Haptic data is active in this case, copy it directly from
+                        // in buffer to out buffer.
+                        const size_t audioBufferSize = mNormalFrameCount
+                                * audio_bytes_per_frame(mChannelCount, EFFECT_BUFFER_FORMAT);
+                        memcpy_by_audio_format(
+                                (uint8_t*)effectChains[i]->outBuffer() + audioBufferSize,
+                                EFFECT_BUFFER_FORMAT,
+                                (const uint8_t*)effectChains[i]->inBuffer() + audioBufferSize,
+                                EFFECT_BUFFER_FORMAT, mNormalFrameCount * mHapticChannelCount);
+                    }
                 }
             }
         }
@@ -4169,6 +4191,7 @@
                                                                      // audio to FastMixer
         fastTrack->mFormat = mFormat; // mPipeSink format for audio to FastMixer
         fastTrack->mHapticPlaybackEnabled = mHapticChannelMask != AUDIO_CHANNEL_NONE;
+        fastTrack->mHapticIntensity = AudioMixer::HAPTIC_SCALE_NONE;
         fastTrack->mGeneration++;
         state->mFastTracksGen++;
         state->mTrackMask = 1;
@@ -7125,24 +7148,6 @@
         ALOG_ASSERT(framesRead > 0);
         mFramesRead += framesRead;
 
-        if (audio_has_proportional_frames(mFormat)
-                && loopCount == lastLoopCountRead + 1) {
-            const int64_t readPeriodNs = lastIoEndNs - mLastIoEndNs;
-            const double jitterMs =
-                    TimestampVerifier<int64_t, int64_t>::computeJitterMs(
-                            {framesRead, readPeriodNs},
-                            {0, 0} /* lastTimestamp */, mSampleRate);
-            const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6;
-
-            Mutex::Autolock _l(mLock);
-            mIoJitterMs.add(jitterMs);
-            mProcessTimeMs.add(processMs);
-        }
-        // update timing info.
-        mLastIoBeginNs = lastIoBeginNs;
-        mLastIoEndNs = lastIoEndNs;
-        lastLoopCountRead = loopCount;
-
 #ifdef TEE_SINK
         (void)mTee.write((uint8_t*)mRsmpInBuffer + rear * mFrameSize, framesRead);
 #endif
@@ -7302,6 +7307,23 @@
         // enable changes in effect chain
         unlockEffectChains(effectChains);
         // effectChains doesn't need to be cleared, since it is cleared by destructor at scope end
+        if (audio_has_proportional_frames(mFormat)
+            && loopCount == lastLoopCountRead + 1) {
+            const int64_t readPeriodNs = lastIoEndNs - mLastIoEndNs;
+            const double jitterMs =
+                TimestampVerifier<int64_t, int64_t>::computeJitterMs(
+                    {framesRead, readPeriodNs},
+                    {0, 0} /* lastTimestamp */, mSampleRate);
+            const double processMs = (lastIoBeginNs - mLastIoEndNs) * 1e-6;
+
+            Mutex::Autolock _l(mLock);
+            mIoJitterMs.add(jitterMs);
+            mProcessTimeMs.add(processMs);
+        }
+        // update timing info.
+        mLastIoBeginNs = lastIoBeginNs;
+        mLastIoEndNs = lastIoEndNs;
+        lastLoopCountRead = loopCount;
     }
 
     standbyIfNotAlreadyInStandby();
diff --git a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
index 7a9c26e..f2b51d9 100644
--- a/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
+++ b/services/audiopolicy/common/managerdefinitions/include/AudioPolicyMix.h
@@ -65,10 +65,13 @@
      * @param[in] attributes to consider fowr the research of output descriptor.
      * @param[out] desc to return if an primary output could be found.
      * @param[out] secondaryDesc other desc that the audio should be routed to.
+     * @return OK if the request is valid
+     *         otherwise if the request is not supported
      */
     status_t getOutputForAttr(const audio_attributes_t& attributes, uid_t uid,
-                sp<SwAudioOutputDescriptor> &primaryDesc,
-                std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
+                              audio_output_flags_t flags,
+                              sp<SwAudioOutputDescriptor> &primaryDesc,
+                              std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs);
 
     sp<DeviceDescriptor> getDeviceAndMixForInputSource(audio_source_t inputSource,
                                                        const DeviceVector &availableDeviceTypes,
diff --git a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
index 1b812c0..26bb354 100644
--- a/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/AudioPolicyMix.cpp
@@ -126,21 +126,33 @@
 }
 
 status_t AudioPolicyMixCollection::getOutputForAttr(
-        const audio_attributes_t& attributes, uid_t uid, sp<SwAudioOutputDescriptor> &primaryDesc,
+        const audio_attributes_t& attributes, uid_t uid,
+        audio_output_flags_t flags,
+        sp<SwAudioOutputDescriptor> &primaryDesc,
         std::vector<sp<SwAudioOutputDescriptor>> *secondaryDescs)
 {
     ALOGV("getOutputForAttr() querying %zu mixes:", size());
     primaryDesc = 0;
     for (size_t i = 0; i < size(); i++) {
         sp<AudioPolicyMix> policyMix = valueAt(i);
+        const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
+        if (!primaryOutputMix && (flags & AUDIO_OUTPUT_FLAG_MMAP_NOIRQ)) {
+            // AAudio does not support MMAP_NO_IRQ loopback render, and there is no way with
+            // the current MmapStreamInterface::start to reject a specific client added to a shared
+            // mmap stream.
+            // As a result all MMAP_NOIRQ requests have to be rejected when an loopback render
+            // policy is present. That ensures no shared mmap stream is used when an loopback
+            // render policy is registered.
+            ALOGD("%s: Rejecting MMAP_NOIRQ request due to LOOPBACK|RENDER mix present.", __func__);
+            return INVALID_OPERATION;
+        }
+
         sp<SwAudioOutputDescriptor> policyDesc = policyMix->getOutput();
         if (!policyDesc) {
             ALOGV("%s: Skiping %zu: Mix has no output", __func__, i);
             continue;
         }
 
-        const bool primaryOutputMix = !is_mix_loopback_render(policyMix->mRouteFlags);
-
         if (primaryOutputMix && primaryDesc != 0) {
             ALOGV("%s: Skiping %zu: Primary output already found", __func__, i);
             continue; // Primary output already found
@@ -170,7 +182,7 @@
             }
         }
     }
-    return (primaryDesc == nullptr && secondaryDescs->empty()) ? BAD_VALUE : NO_ERROR;
+    return NO_ERROR;
 }
 
 AudioPolicyMixCollection::MixMatchStatus AudioPolicyMixCollection::mixMatch(
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index bd53f0f..af29f87 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -963,10 +963,11 @@
     //       otherwise, fallback to the dynamic policies, if none match, query the engine.
     // Secondary outputs are always found by dynamic policies as the engine do not support them
     sp<SwAudioOutputDescriptor> policyDesc;
-    if (mPolicyMixes.getOutputForAttr(*resultAttr, uid, policyDesc, secondaryDescs) != NO_ERROR) {
-        policyDesc = nullptr; // reset getOutputForAttr in case of failure
-        secondaryDescs->clear();
+    status = mPolicyMixes.getOutputForAttr(*resultAttr, uid, *flags, policyDesc, secondaryDescs);
+    if (status != OK) {
+        return status;
     }
+
     // Explicit routing is higher priority then any dynamic policy primary output
     bool usePrimaryOutputFromPolicyMixes = requestedDevice == nullptr && policyDesc != nullptr;
 
@@ -1640,7 +1641,14 @@
     outputDesc->setClientActive(client, true);
 
     if (client->hasPreferredDevice(true)) {
-        devices = getNewOutputDevices(outputDesc, false /*fromCache*/);
+        if (outputDesc->clientsList(true /*activeOnly*/).size() == 1 &&
+                client->isPreferredDeviceForExclusiveUse()) {
+            // Preferred device may be exclusive, use only if no other active clients on this output
+            devices = DeviceVector(
+                        mAvailableOutputDevices.getDeviceFromId(client->preferredDeviceId()));
+        } else {
+            devices = getNewOutputDevices(outputDesc, false /*fromCache*/);
+        }
         if (devices != outputDesc->devices()) {
             checkStrategyRoute(clientStrategy, outputDesc->mIoHandle);
         }
@@ -2463,6 +2471,11 @@
         sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
         audio_devices_t curDevice = desc->devices().types();
 
+        if (curDevice & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
+            curDevice |= AUDIO_DEVICE_OUT_SPEAKER;
+            curDevice &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+        }
+
         // Inter / intra volume group priority management: Loop on strategies arranged by priority
         // If a higher priority strategy is active, and the output is routed to a device with a
         // HW Gain management, do not change the volume
@@ -5064,12 +5077,12 @@
     for (size_t i = 0; i < mOutputs.size(); i++) {
         const sp<SwAudioOutputDescriptor>& outputDescriptor = mOutputs[i];
         for (const sp<TrackClientDescriptor>& client : outputDescriptor->getClientIterable()) {
-            // FIXME code duplicated from getOutputForAttrInt
             sp<SwAudioOutputDescriptor> desc;
             std::vector<sp<SwAudioOutputDescriptor>> secondaryDescs;
-            mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(), desc,
-                                          &secondaryDescs);
-            if (!std::equal(client->getSecondaryOutputs().begin(),
+            status_t status = mPolicyMixes.getOutputForAttr(client->attributes(), client->uid(),
+                                                            client->flags(), desc, &secondaryDescs);
+            if (status != OK ||
+                !std::equal(client->getSecondaryOutputs().begin(),
                             client->getSecondaryOutputs().end(),
                             secondaryDescs.begin(), secondaryDescs.end())) {
                 streamsToInvalidate.insert(client->stream());
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index a700868..de447fb 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.h
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.h
@@ -348,7 +348,8 @@
         }
         virtual const DeviceVector getAvailableInputDevices() const
         {
-            return mAvailableInputDevices.filterForEngine();
+            // legacy and non-legacy remote-submix are managed by the engine, do not filter
+            return mAvailableInputDevices;
         }
         virtual const sp<DeviceDescriptor> &getDefaultOutputDevice() const
         {
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index b8036bb..2eb272e 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -225,6 +225,21 @@
     return result;
 }
 
+void AudioPolicyService::getPlaybackClientAndEffects(audio_port_handle_t portId,
+                                                     sp<AudioPlaybackClient>& client,
+                                                     sp<AudioPolicyEffects>& effects,
+                                                     const char *context)
+{
+    Mutex::Autolock _l(mLock);
+    const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
+    if (index < 0) {
+        ALOGE("%s AudioTrack client not found for portId %d", context, portId);
+        return;
+    }
+    client = mAudioPlaybackClients.valueAt(index);
+    effects = mAudioPolicyEffects;
+}
+
 status_t AudioPolicyService::startOutput(audio_port_handle_t portId)
 {
     if (mAudioPolicyManager == NULL) {
@@ -233,16 +248,9 @@
     ALOGV("startOutput()");
     sp<AudioPlaybackClient> client;
     sp<AudioPolicyEffects>audioPolicyEffects;
-    {
-        Mutex::Autolock _l(mLock);
-        const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
-        if (index < 0) {
-            ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
-            return INVALID_OPERATION;
-        }
-        client = mAudioPlaybackClients.valueAt(index);
-        audioPolicyEffects = mAudioPolicyEffects;
-    }
+
+    getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__);
+
     if (audioPolicyEffects != 0) {
         // create audio processors according to stream
         status_t status = audioPolicyEffects->addOutputSessionEffects(
@@ -275,17 +283,9 @@
     ALOGV("doStopOutput");
     sp<AudioPlaybackClient> client;
     sp<AudioPolicyEffects>audioPolicyEffects;
-    {
-        Mutex::Autolock _l(mLock);
 
-        const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
-        if (index < 0) {
-            ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
-            return INVALID_OPERATION;
-        }
-        client = mAudioPlaybackClients.valueAt(index);
-        audioPolicyEffects = mAudioPolicyEffects;
-    }
+    getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__);
+
     if (audioPolicyEffects != 0) {
         // release audio processors from the stream
         status_t status = audioPolicyEffects->releaseOutputSessionEffects(
@@ -315,13 +315,17 @@
 void AudioPolicyService::doReleaseOutput(audio_port_handle_t portId)
 {
     ALOGV("doReleaseOutput from tid %d", gettid());
-    Mutex::Autolock _l(mLock);
-    const ssize_t index = mAudioPlaybackClients.indexOfKey(portId);
-    if (index < 0) {
-        ALOGE("%s AudioTrack client not found for portId %d", __FUNCTION__, portId);
-        return;
+    sp<AudioPlaybackClient> client;
+    sp<AudioPolicyEffects> audioPolicyEffects;
+
+    getPlaybackClientAndEffects(portId, client, audioPolicyEffects, __func__);
+
+    if (audioPolicyEffects != 0 && client->active) {
+        // clean up effects if output was not stopped before being released
+        audioPolicyEffects->releaseOutputSessionEffects(
+            client->io, client->stream, client->session);
     }
-    sp<AudioPlaybackClient> client = mAudioPlaybackClients.valueAt(index);
+    Mutex::Autolock _l(mLock);
     mAudioPlaybackClients.removeItem(portId);
 
     // called from internal thread: no need to clear caller identity
diff --git a/services/audiopolicy/service/AudioPolicyService.h b/services/audiopolicy/service/AudioPolicyService.h
index efdba56..189322f 100644
--- a/services/audiopolicy/service/AudioPolicyService.h
+++ b/services/audiopolicy/service/AudioPolicyService.h
@@ -782,6 +782,12 @@
         const audio_stream_type_t stream;
     };
 
+    void getPlaybackClientAndEffects(audio_port_handle_t portId,
+                                     sp<AudioPlaybackClient>& client,
+                                     sp<AudioPolicyEffects>& effects,
+                                     const char *context);
+
+
     // A class automatically clearing and restoring binder caller identity inside
     // a code block (scoped variable)
     // Declare one systematically before calling AudioPolicyManager methods so that they are
diff --git a/services/camera/libcameraservice/CameraService.cpp b/services/camera/libcameraservice/CameraService.cpp
index 8113c3f..cca4049 100644
--- a/services/camera/libcameraservice/CameraService.cpp
+++ b/services/camera/libcameraservice/CameraService.cpp
@@ -117,6 +117,9 @@
 
 static const String16 sManageCameraPermission("android.permission.MANAGE_CAMERA");
 
+Mutex CameraService::sProxyMutex;
+sp<hardware::ICameraServiceProxy> CameraService::sCameraServiceProxy;
+
 CameraService::CameraService() :
         mEventLog(DEFAULT_EVENT_LOG_LENGTH),
         mNumberOfCameras(0),
@@ -203,18 +206,20 @@
 }
 
 sp<ICameraServiceProxy> CameraService::getCameraServiceProxy() {
-    sp<ICameraServiceProxy> proxyBinder = nullptr;
 #ifndef __BRILLO__
-    sp<IServiceManager> sm = defaultServiceManager();
-    // Use checkService because cameraserver normally starts before the
-    // system server and the proxy service. So the long timeout that getService
-    // has before giving up is inappropriate.
-    sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
-    if (binder != nullptr) {
-        proxyBinder = interface_cast<ICameraServiceProxy>(binder);
+    Mutex::Autolock al(sProxyMutex);
+    if (sCameraServiceProxy == nullptr) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        // Use checkService because cameraserver normally starts before the
+        // system server and the proxy service. So the long timeout that getService
+        // has before giving up is inappropriate.
+        sp<IBinder> binder = sm->checkService(String16("media.camera.proxy"));
+        if (binder != nullptr) {
+            sCameraServiceProxy = interface_cast<ICameraServiceProxy>(binder);
+        }
     }
 #endif
-    return proxyBinder;
+    return sCameraServiceProxy;
 }
 
 void CameraService::pingCameraServiceProxy() {
@@ -858,6 +863,25 @@
     }
 }
 
+static status_t getUidForPackage(String16 packageName, int userId, /*inout*/uid_t& uid, int err) {
+    PermissionController pc;
+    uid = pc.getPackageUid(packageName, 0);
+    if (uid <= 0) {
+        ALOGE("Unknown package: '%s'", String8(packageName).string());
+        dprintf(err, "Unknown package: '%s'\n", String8(packageName).string());
+        return BAD_VALUE;
+    }
+
+    if (userId < 0) {
+        ALOGE("Invalid user: %d", userId);
+        dprintf(err, "Invalid user: %d\n", userId);
+        return BAD_VALUE;
+    }
+
+    uid = multiuser_get_uid(userId, uid);
+    return NO_ERROR;
+}
+
 Status CameraService::validateConnectLocked(const String8& cameraId,
         const String8& clientName8, /*inout*/int& clientUid, /*inout*/int& clientPid,
         /*out*/int& originalClientPid) const {
@@ -3315,11 +3339,11 @@
     if (in == BAD_TYPE || out == BAD_TYPE || err == BAD_TYPE) {
         return BAD_VALUE;
     }
-    if (args.size() == 3 && args[0] == String16("set-uid-state")) {
+    if (args.size() >= 3 && args[0] == String16("set-uid-state")) {
         return handleSetUidState(args, err);
-    } else if (args.size() == 2 && args[0] == String16("reset-uid-state")) {
+    } else if (args.size() >= 2 && args[0] == String16("reset-uid-state")) {
         return handleResetUidState(args, err);
-    } else if (args.size() == 2 && args[0] == String16("get-uid-state")) {
+    } else if (args.size() >= 2 && args[0] == String16("get-uid-state")) {
         return handleGetUidState(args, out, err);
     } else if (args.size() == 1 && args[0] == String16("help")) {
         printHelp(out);
@@ -3330,13 +3354,8 @@
 }
 
 status_t CameraService::handleSetUidState(const Vector<String16>& args, int err) {
-    PermissionController pc;
-    int uid = pc.getPackageUid(args[1], 0);
-    if (uid <= 0) {
-        ALOGE("Unknown package: '%s'", String8(args[1]).string());
-        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
-        return BAD_VALUE;
-    }
+    String16 packageName = args[1];
+
     bool active = false;
     if (args[2] == String16("active")) {
         active = true;
@@ -3344,31 +3363,52 @@
         ALOGE("Expected active or idle but got: '%s'", String8(args[2]).string());
         return BAD_VALUE;
     }
-    mUidPolicy->addOverrideUid(uid, args[1], active);
+
+    int userId = 0;
+    if (args.size() >= 5 && args[3] == String16("--user")) {
+        userId = atoi(String8(args[4]));
+    }
+
+    uid_t uid;
+    if (getUidForPackage(packageName, userId, uid, err) == BAD_VALUE) {
+        return BAD_VALUE;
+    }
+
+    mUidPolicy->addOverrideUid(uid, packageName, active);
     return NO_ERROR;
 }
 
 status_t CameraService::handleResetUidState(const Vector<String16>& args, int err) {
-    PermissionController pc;
-    int uid = pc.getPackageUid(args[1], 0);
-    if (uid < 0) {
-        ALOGE("Unknown package: '%s'", String8(args[1]).string());
-        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+    String16 packageName = args[1];
+
+    int userId = 0;
+    if (args.size() >= 4 && args[2] == String16("--user")) {
+        userId = atoi(String8(args[3]));
+    }
+
+    uid_t uid;
+    if (getUidForPackage(packageName, userId, uid, err) == BAD_VALUE) {
         return BAD_VALUE;
     }
-    mUidPolicy->removeOverrideUid(uid, args[1]);
+
+    mUidPolicy->removeOverrideUid(uid, packageName);
     return NO_ERROR;
 }
 
 status_t CameraService::handleGetUidState(const Vector<String16>& args, int out, int err) {
-    PermissionController pc;
-    int uid = pc.getPackageUid(args[1], 0);
-    if (uid <= 0) {
-        ALOGE("Unknown package: '%s'", String8(args[1]).string());
-        dprintf(err, "Unknown package: '%s'\n", String8(args[1]).string());
+    String16 packageName = args[1];
+
+    int userId = 0;
+    if (args.size() >= 4 && args[2] == String16("--user")) {
+        userId = atoi(String8(args[3]));
+    }
+
+    uid_t uid;
+    if (getUidForPackage(packageName, userId, uid, err) == BAD_VALUE) {
         return BAD_VALUE;
     }
-    if (mUidPolicy->isUidActive(uid, args[1])) {
+
+    if (mUidPolicy->isUidActive(uid, packageName)) {
         return dprintf(out, "active\n");
     } else {
         return dprintf(out, "idle\n");
@@ -3377,9 +3417,9 @@
 
 status_t CameraService::printHelp(int out) {
     return dprintf(out, "Camera service commands:\n"
-        "  get-uid-state <PACKAGE> gets the uid state\n"
-        "  set-uid-state <PACKAGE> <active|idle> overrides the uid state\n"
-        "  reset-uid-state <PACKAGE> clears the uid state override\n"
+        "  get-uid-state <PACKAGE> [--user USER_ID] gets the uid state\n"
+        "  set-uid-state <PACKAGE> <active|idle> [--user USER_ID] overrides the uid state\n"
+        "  reset-uid-state <PACKAGE> [--user USER_ID] clears the uid state override\n"
         "  help print this message\n");
 }
 
diff --git a/services/camera/libcameraservice/CameraService.h b/services/camera/libcameraservice/CameraService.h
index 344dd92..4bcdeb2 100644
--- a/services/camera/libcameraservice/CameraService.h
+++ b/services/camera/libcameraservice/CameraService.h
@@ -930,6 +930,11 @@
     static StatusInternal mapToInternal(hardware::camera::common::V1_0::CameraDeviceStatus status);
     static int32_t mapToInterface(StatusInternal status);
 
+    // Guard mCameraServiceProxy
+    static Mutex sProxyMutex;
+    // Cached interface to the camera service proxy in system service
+    static sp<hardware::ICameraServiceProxy> sCameraServiceProxy;
+
     static sp<hardware::ICameraServiceProxy> getCameraServiceProxy();
     static void pingCameraServiceProxy();
 
diff --git a/services/camera/libcameraservice/common/CameraProviderManager.cpp b/services/camera/libcameraservice/common/CameraProviderManager.cpp
index d6789a4..09638d0 100644
--- a/services/camera/libcameraservice/common/CameraProviderManager.cpp
+++ b/services/camera/libcameraservice/common/CameraProviderManager.cpp
@@ -120,8 +120,8 @@
         std::vector<std::string> providerDeviceIds = provider->mUniqueAPI1CompatibleCameraIds;
 
         // API1 app doesn't handle logical and physical camera devices well. So
-        // for each [logical, physical1, physical2, ...] id combo, only take the
-        // first id advertised by HAL, and filter out the rest.
+        // for each camera facing, only take the first id advertised by HAL in
+        // all [logical, physical1, physical2, ...] id combos, and filter out the rest.
         filterLogicalCameraIdsLocked(providerDeviceIds);
 
         deviceIds.insert(deviceIds.end(), providerDeviceIds.begin(), providerDeviceIds.end());
@@ -2500,8 +2500,11 @@
 void CameraProviderManager::filterLogicalCameraIdsLocked(
         std::vector<std::string>& deviceIds) const
 {
-    std::unordered_set<std::string> removedIds;
+    // Map between camera facing and camera IDs related to logical camera.
+    std::map<int, std::unordered_set<std::string>> idCombos;
 
+    // Collect all logical and its underlying physical camera IDs for each
+    // facing.
     for (auto& deviceId : deviceIds) {
         auto deviceInfo = findDeviceInfoLocked(deviceId);
         if (deviceInfo == nullptr) continue;
@@ -2509,25 +2512,38 @@
         if (!deviceInfo->mIsLogicalCamera) {
             continue;
         }
-        // idCombo contains the ids of a logical camera and its physical cameras
-        std::vector<std::string> idCombo = deviceInfo->mPhysicalIds;
-        idCombo.push_back(deviceId);
 
+        // combo contains the ids of a logical camera and its physical cameras
+        std::vector<std::string> combo = deviceInfo->mPhysicalIds;
+        combo.push_back(deviceId);
+
+        hardware::CameraInfo info;
+        status_t res = deviceInfo->getCameraInfo(&info);
+        if (res != OK) {
+            ALOGE("%s: Error reading camera info: %s (%d)", __FUNCTION__, strerror(-res), res);
+            continue;
+        }
+        idCombos[info.facing].insert(combo.begin(), combo.end());
+    }
+
+    // Only expose one camera ID per facing for all logical and underlying
+    // physical camera IDs.
+    for (auto& r : idCombos) {
+        auto& removedIds = r.second;
         for (auto& id : deviceIds) {
-            auto foundId = std::find(idCombo.begin(), idCombo.end(), id);
-            if (foundId == idCombo.end()) {
+            auto foundId = std::find(removedIds.begin(), removedIds.end(), id);
+            if (foundId == removedIds.end()) {
                 continue;
             }
 
-            idCombo.erase(foundId);
-            removedIds.insert(idCombo.begin(), idCombo.end());
+            removedIds.erase(foundId);
             break;
         }
+        deviceIds.erase(std::remove_if(deviceIds.begin(), deviceIds.end(),
+                [&removedIds](const std::string& s) {
+                return removedIds.find(s) != removedIds.end();}),
+                deviceIds.end());
     }
-
-    deviceIds.erase(std::remove_if(deviceIds.begin(), deviceIds.end(),
-            [&removedIds](const std::string& s) {return removedIds.find(s) != removedIds.end();}),
-            deviceIds.end());
 }
 
 } // namespace android
diff --git a/services/mediacodec/Android.mk b/services/mediacodec/Android.mk
index 9cc19a3..473e21c 100644
--- a/services/mediacodec/Android.mk
+++ b/services/mediacodec/Android.mk
@@ -32,7 +32,6 @@
 endif
 LOCAL_SRC_FILES := main_codecservice.cpp
 LOCAL_SHARED_LIBRARIES := \
-    libmedia_omx \
     libbinder \
     libutils \
     liblog \
diff --git a/services/mediacodec/main_codecservice.cpp b/services/mediacodec/main_codecservice.cpp
index 6d47be6..6ffbd26 100644
--- a/services/mediacodec/main_codecservice.cpp
+++ b/services/mediacodec/main_codecservice.cpp
@@ -25,7 +25,6 @@
 #include <media/stagefright/omx/1.0/Omx.h>
 #include <media/stagefright/omx/1.0/OmxStore.h>
 
-#include <media/CodecServiceRegistrant.h>
 #include <dlfcn.h>
 
 using namespace android;
diff --git a/services/mediacodec/seccomp_policy/mediacodec-x86.policy b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
index 845f84b..d9c4045 100644
--- a/services/mediacodec/seccomp_policy/mediacodec-x86.policy
+++ b/services/mediacodec/seccomp_policy/mediacodec-x86.policy
@@ -45,6 +45,7 @@
 exit_group: 1
 rt_sigreturn: 1
 ugetrlimit: 1
+readlink: 1
 readlinkat: 1
 _llseek: 1
 fstatfs64: 1