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