Home | History | Annotate | Download | only in factory
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #define LOG_TAG "EffectsFactoryConfigLoader"
     18 //#define LOG_NDEBUG 0
     19 
     20 #include <dlfcn.h>
     21 #include <set>
     22 #include <stdlib.h>
     23 #include <string>
     24 
     25 #include <log/log.h>
     26 
     27 #include <media/EffectsConfig.h>
     28 
     29 #include "EffectsConfigLoader.h"
     30 #include "EffectsFactoryState.h"
     31 #include "EffectsXmlConfigLoader.h"
     32 
     33 namespace android {
     34 
     35 using namespace effectsConfig;
     36 
     37 /////////////////////////////////////////////////
     38 //      Local functions
     39 /////////////////////////////////////////////////
     40 
     41 namespace {
     42 
     43 /** Similarly to dlopen, looks for the provided path in LD_EFFECT_LIBRARY_PATH.
     44  * @return true if the library is found and set resolvedPath to its absolute path.
     45  *         false if not found
     46  */
     47 bool resolveLibrary(const std::string& path, std::string* resolvedPath) {
     48     for (auto* libraryDirectory : LD_EFFECT_LIBRARY_PATH) {
     49         std::string candidatePath = std::string(libraryDirectory) + '/' + path;
     50         if (access(candidatePath.c_str(), R_OK) == 0) {
     51             *resolvedPath = std::move(candidatePath);
     52             return true;
     53         }
     54     }
     55     return false;
     56 }
     57 
     58 /** Loads a library given its relative path and stores the result in libEntry.
     59  * @return true on success with libEntry's path, handle and desc filled
     60  *         false on success with libEntry's path filled with the path of the failed lib
     61  * The caller MUST free the resources path (free) and handle (dlclose) if filled.
     62  */
     63 bool loadLibrary(const char* relativePath, lib_entry_t* libEntry) noexcept {
     64 
     65     std::string absolutePath;
     66     if (!resolveLibrary(relativePath, &absolutePath)) {
     67         ALOGE("Could not find library in effect directories: %s", relativePath);
     68         libEntry->path = strdup(relativePath);
     69         return false;
     70     }
     71     const char* path = absolutePath.c_str();
     72     libEntry->path = strdup(path);
     73 
     74     // Make sure the lib is closed on early return
     75     std::unique_ptr<void, decltype(dlclose)*> libHandle(dlopen(path, RTLD_NOW),
     76                                                        dlclose);
     77     if (libHandle == nullptr) {
     78         ALOGE("Could not dlopen library %s: %s", path, dlerror());
     79         return false;
     80     }
     81 
     82     auto* description = static_cast<audio_effect_library_t*>(
     83           dlsym(libHandle.get(), AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR));
     84     if (description == nullptr) {
     85         ALOGE("Invalid effect library, failed not find symbol '%s' in %s: %s",
     86               AUDIO_EFFECT_LIBRARY_INFO_SYM_AS_STR, path, dlerror());
     87         return false;
     88     }
     89 
     90     if (description->tag != AUDIO_EFFECT_LIBRARY_TAG) {
     91         ALOGE("Bad tag %#08x in description structure, expected %#08x for library %s",
     92               description->tag, AUDIO_EFFECT_LIBRARY_TAG, path);
     93         return false;
     94     }
     95 
     96     uint32_t majorVersion = EFFECT_API_VERSION_MAJOR(description->version);
     97     uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_LIBRARY_API_VERSION);
     98     if (majorVersion != expectedMajorVersion) {
     99         ALOGE("Unsupported major version %#08x, expected %#08x for library %s",
    100               majorVersion, expectedMajorVersion, path);
    101         return false;
    102     }
    103 
    104     libEntry->handle = libHandle.release();
    105     libEntry->desc = description;
    106     return true;
    107 }
    108 
    109 /** Because the structures will be destroyed by c code, using new to allocate shared structure
    110  * is not possible. Provide a equivalent of unique_ptr for malloc/freed structure to make sure
    111  * they are not leaked in the c++ code.
    112  @{ */
    113 struct FreeDeleter {
    114     void operator()(void* p) {
    115         free(p);
    116     }
    117 };
    118 /** unique_ptr for object created with malloc. */
    119 template <class T>
    120 using UniqueCPtr = std::unique_ptr<T, FreeDeleter>;
    121 
    122 /** c version of std::make_unique. Uses malloc and free. */
    123 template <class T>
    124 UniqueCPtr<T> makeUniqueC() {
    125     T* ptr = new (malloc(sizeof(T))) T{}; // Use placement new to initialize the structure
    126     return UniqueCPtr<T>{ptr};
    127 }
    128 
    129 /** @} */
    130 
    131 /** Push an not owned element in a list_elem link list with an optional lock. */
    132 template <class T, class ListElem>
    133 void listPush(T* object, ListElem** list, pthread_mutex_t* mutex = nullptr) noexcept {
    134     auto listElem = makeUniqueC<ListElem>();
    135     listElem->object = object;
    136     if (mutex != nullptr) {
    137         pthread_mutex_lock(mutex);
    138     }
    139     listElem->next = *list;
    140     *list = listElem.release();
    141     if (mutex != nullptr) {
    142         pthread_mutex_unlock(mutex);
    143     }
    144 }
    145 
    146 /** Push an owned element in a list_elem link list with an optional lock. */
    147 template <class T, class ListElem>
    148 void listPush(UniqueCPtr<T>&& object, ListElem** list, pthread_mutex_t* mutex = nullptr) noexcept {
    149     listPush(object.release(), list, mutex);
    150 }
    151 
    152 size_t loadLibraries(const effectsConfig::Libraries& libs,
    153                      list_elem_t** libList, pthread_mutex_t* libListLock,
    154                      list_elem_t** libFailedList)
    155 {
    156     size_t nbSkippedElement = 0;
    157     for (auto& library : libs) {
    158 
    159         // Construct a lib entry
    160         auto libEntry = makeUniqueC<lib_entry_t>();
    161         libEntry->name = strdup(library.name.c_str());
    162         libEntry->effects = nullptr;
    163         pthread_mutex_init(&libEntry->lock, nullptr);
    164 
    165         if (!loadLibrary(library.path.c_str(), libEntry.get())) {
    166             // Register library load failure
    167             listPush(std::move(libEntry), libFailedList);
    168             ++nbSkippedElement;
    169             continue;
    170         }
    171         listPush(std::move(libEntry), libList, libListLock);
    172     }
    173     return nbSkippedElement;
    174 }
    175 
    176 /** Find a library with the given name in the given list. */
    177 lib_entry_t* findLibrary(const char* name, list_elem_t* list) {
    178 
    179     while (list != nullptr) {
    180         auto* object = static_cast<lib_entry_t*>(list->object);
    181         if (strcmp(object->name, name) == 0) {
    182             return object;
    183         }
    184         list = list->next;
    185     }
    186     return nullptr;
    187 }
    188 
    189 struct UuidStr {
    190     /** Length of an uuid represented as string. @TODO: use a constant instead of 40. */
    191     char buff[40];
    192 };
    193 
    194 /** @return a string representing the provided uuid.
    195  * By not providing an output buffer, it is implicitly created in the caller context.
    196  * In such case the return pointer has the same lifetime as the expression containing uuidToString()
    197  */
    198 char* uuidToString(const effect_uuid_t& uuid, UuidStr&& str = {}) {
    199     uuidToString(&uuid, str.buff, sizeof(str.buff));
    200     return str.buff;
    201 }
    202 
    203 struct LoadEffectResult {
    204     /** true if the effect is usable (aka, existing lib, desc, right version, unique uuid) */
    205     bool success = false;
    206     /** Set if the effect lib was found*/
    207     lib_entry_t* lib = nullptr;
    208     //* Set if the description was successfuly retrieved from the lib */
    209     UniqueCPtr<effect_descriptor_t> effectDesc;
    210 };
    211 
    212 LoadEffectResult loadEffect(const EffectImpl& effect, const std::string& name,
    213                             list_elem_t* libList) {
    214     LoadEffectResult result;
    215 
    216     // Find the effect library
    217     result.lib = findLibrary(effect.library->name.c_str(), libList);
    218     if (result.lib == nullptr) {
    219         ALOGE("Could not find library %s to load effect %s",
    220               effect.library->name.c_str(), name.c_str());
    221         return result;
    222     }
    223 
    224     result.effectDesc = makeUniqueC<effect_descriptor_t>();
    225 
    226     // Get the effect descriptor
    227     if (result.lib->desc->get_descriptor(&effect.uuid, result.effectDesc.get()) != 0) {
    228         ALOGE("Error querying effect %s on lib %s",
    229               uuidToString(effect.uuid), result.lib->name);
    230         result.effectDesc.reset();
    231         return result;
    232     }
    233 
    234     // Dump effect for debug
    235 #if (LOG_NDEBUG==0)
    236     char s[512];
    237     dumpEffectDescriptor(result.effectDesc.get(), s, sizeof(s), 0 /* indent */);
    238     ALOGV("loadEffect() read descriptor %p:%s", result.effectDesc.get(), s);
    239 #endif
    240 
    241     // Check effect is supported
    242     uint32_t expectedMajorVersion = EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION);
    243     if (EFFECT_API_VERSION_MAJOR(result.effectDesc->apiVersion) != expectedMajorVersion) {
    244         ALOGE("Bad API version %#08x for effect %s in lib %s, expected major %#08x",
    245               result.effectDesc->apiVersion, name.c_str(), result.lib->name, expectedMajorVersion);
    246         return result;
    247     }
    248 
    249     lib_entry_t *_;
    250     if (findEffect(nullptr, &effect.uuid, &_, nullptr) == 0) {
    251         ALOGE("Effect %s uuid %s already exist", uuidToString(effect.uuid), name.c_str());
    252         return result;
    253     }
    254 
    255     result.success = true;
    256     return result;
    257 }
    258 
    259 size_t loadEffects(const Effects& effects, list_elem_t* libList, list_elem_t** skippedEffects,
    260                    list_sub_elem_t** subEffectList) {
    261     size_t nbSkippedElement = 0;
    262 
    263     for (auto& effect : effects) {
    264 
    265         auto effectLoadResult = loadEffect(effect, effect.name, libList);
    266         if (!effectLoadResult.success) {
    267             if (effectLoadResult.effectDesc != nullptr) {
    268                 listPush(std::move(effectLoadResult.effectDesc), skippedEffects);
    269             }
    270             ++nbSkippedElement;
    271             continue;
    272         }
    273 
    274         if (effect.isProxy) {
    275             auto swEffectLoadResult = loadEffect(effect.libSw, effect.name + " libsw", libList);
    276             auto hwEffectLoadResult = loadEffect(effect.libHw, effect.name + " libhw", libList);
    277             if (!swEffectLoadResult.success || !hwEffectLoadResult.success) {
    278                 // Push the main effect in the skipped list even if only a subeffect is invalid
    279                 // as the main effect is not usable without its subeffects.
    280                 listPush(std::move(effectLoadResult.effectDesc), skippedEffects);
    281                 ++nbSkippedElement;
    282                 continue;
    283             }
    284             listPush(effectLoadResult.effectDesc.get(), subEffectList);
    285 
    286             // Since we return a dummy descriptor for the proxy during
    287             // get_descriptor call, we replace it with the corresponding
    288             // sw effect descriptor, but keep the Proxy UUID
    289             *effectLoadResult.effectDesc = *swEffectLoadResult.effectDesc;
    290             effectLoadResult.effectDesc->uuid = effect.uuid;
    291 
    292             effectLoadResult.effectDesc->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
    293 
    294             auto registerSubEffect = [subEffectList](auto&& result) {
    295                 auto entry = makeUniqueC<sub_effect_entry_t>();
    296                 entry->object = result.effectDesc.release();
    297                 // lib_entry_t is stored since the sub effects are not linked to the library
    298                 entry->lib = result.lib;
    299                 listPush(std::move(entry), &(*subEffectList)->sub_elem);
    300             };
    301             registerSubEffect(std::move(swEffectLoadResult));
    302             registerSubEffect(std::move(hwEffectLoadResult));
    303         }
    304 
    305         listPush(std::move(effectLoadResult.effectDesc), &effectLoadResult.lib->effects);
    306     }
    307     return nbSkippedElement;
    308 }
    309 
    310 } // namespace
    311 
    312 /////////////////////////////////////////////////
    313 //      Interface function
    314 /////////////////////////////////////////////////
    315 
    316 extern "C" ssize_t EffectLoadXmlEffectConfig(const char* path)
    317 {
    318     using effectsConfig::parse;
    319     auto result = path ? parse(path) : parse();
    320     if (result.parsedConfig == nullptr) {
    321         ALOGE("Failed to parse XML configuration file");
    322         return -1;
    323     }
    324     result.nbSkippedElement += loadLibraries(result.parsedConfig->libraries,
    325                                              &gLibraryList, &gLibLock, &gLibraryFailedList) +
    326                                loadEffects(result.parsedConfig->effects, gLibraryList,
    327                                            &gSkippedEffects, &gSubEffectList);
    328 
    329     ALOGE_IF(result.nbSkippedElement != 0, "%zu errors during loading of configuration: %s",
    330              result.nbSkippedElement,
    331              result.configPath.empty() ? "No config file found" : result.configPath.c_str());
    332 
    333     return result.nbSkippedElement;
    334 }
    335 
    336 } // namespace android
    337