Merge "NuPlayerRenderer: allow video rendering after flushing audio" into qt-dev
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/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..f92d1af 100644
--- a/media/codec2/hidl/client/Android.bp
+++ b/media/codec2/hidl/client/Android.bp
@@ -30,8 +30,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/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/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/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/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/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/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