1 /*
2  * Copyright (C) 2022 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 #include <android-base/strings.h>
18 #include <dirent.h>
19 #include <dlfcn.h>
20 #include <media/cas/CasAPI.h>
21 #include <utils/KeyedVector.h>
22 #include <utils/Mutex.h>
23 #include "SharedLibrary.h"
24 
25 using namespace std;
26 
27 namespace aidl {
28 namespace android {
29 namespace hardware {
30 namespace cas {
31 
32 using namespace ::android;
33 
34 template <class T>
35 class FactoryLoader {
36   public:
FactoryLoader(const char * name)37     FactoryLoader(const char* name) : mFactory(NULL), mCreateFactoryFuncName(name) {}
38 
~FactoryLoader()39     virtual ~FactoryLoader() { closeFactory(); }
40 
41     bool findFactoryForScheme(int32_t CA_system_id, shared_ptr<SharedLibrary>* library = NULL,
42                               T** factory = NULL);
43 
44     bool enumeratePlugins(vector<AidlCasPluginDescriptor>* results);
45 
46   private:
47     typedef T* (*CreateFactoryFunc)();
48 
49     Mutex mMapLock;
50     T* mFactory;
51     const char* mCreateFactoryFuncName;
52     shared_ptr<SharedLibrary> mLibrary;
53     KeyedVector<int32_t, String8> mCASystemIdToLibraryPathMap;
54     KeyedVector<String8, shared_ptr<SharedLibrary>> mLibraryPathToOpenLibraryMap;
55 
56     bool loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
57                                       shared_ptr<SharedLibrary>* library, T** factory);
58 
59     bool queryPluginsFromPath(const String8& path, vector<AidlCasPluginDescriptor>* results);
60 
61     bool openFactory(const String8& path);
62     void closeFactory();
63 };
64 
65 template <class T>
findFactoryForScheme(int32_t CA_system_id,shared_ptr<SharedLibrary> * library,T ** factory)66 bool FactoryLoader<T>::findFactoryForScheme(int32_t CA_system_id,
67                                             shared_ptr<SharedLibrary>* library, T** factory) {
68     if (library != NULL) {
69         library->reset();
70     }
71     if (factory != NULL) {
72         *factory = NULL;
73     }
74 
75     Mutex::Autolock autoLock(mMapLock);
76 
77     // first check cache
78     ssize_t index = mCASystemIdToLibraryPathMap.indexOfKey(CA_system_id);
79     if (index >= 0) {
80         return loadFactoryForSchemeFromPath(mCASystemIdToLibraryPathMap[index], CA_system_id,
81                                             library, factory);
82     }
83 
84     // no luck, have to search
85 #ifdef __LP64__
86     String8 dirPath("/vendor/lib64/mediacas");
87 #else
88     String8 dirPath("/vendor/lib/mediacas");
89 #endif
90     DIR* pDir = opendir(dirPath.c_str());
91 
92     if (pDir == NULL) {
93         ALOGE("Failed to open plugin directory %s", dirPath.c_str());
94         return false;
95     }
96 
97     struct dirent* pEntry;
98     while ((pEntry = readdir(pDir))) {
99         String8 pluginPath = dirPath + "/" + pEntry->d_name;
100         if (base::EndsWith(pluginPath.c_str(), ".so")) {
101             if (loadFactoryForSchemeFromPath(pluginPath, CA_system_id, library, factory)) {
102                 mCASystemIdToLibraryPathMap.add(CA_system_id, pluginPath);
103                 closedir(pDir);
104 
105                 return true;
106             }
107         }
108     }
109 
110     closedir(pDir);
111 
112     ALOGE("Failed to find plugin");
113     return false;
114 }
115 
116 template <class T>
enumeratePlugins(vector<AidlCasPluginDescriptor> * results)117 bool FactoryLoader<T>::enumeratePlugins(vector<AidlCasPluginDescriptor>* results) {
118     ALOGI("enumeratePlugins");
119 
120     results->clear();
121 
122 #ifdef __LP64__
123     String8 dirPath("/vendor/lib64/mediacas");
124 #else
125     String8 dirPath("/vendor/lib/mediacas");
126 #endif
127     DIR* pDir = opendir(dirPath.c_str());
128 
129     if (pDir == NULL) {
130         ALOGE("Failed to open plugin directory %s", dirPath.c_str());
131         return false;
132     }
133 
134     Mutex::Autolock autoLock(mMapLock);
135 
136     struct dirent* pEntry;
137     while ((pEntry = readdir(pDir))) {
138         String8 pluginPath = dirPath + "/" + pEntry->d_name;
139         if (base::EndsWith(pluginPath.c_str(), ".so")) {
140             queryPluginsFromPath(pluginPath, results);
141         }
142     }
143     closedir(pDir);
144     return true;
145 }
146 
147 template <class T>
loadFactoryForSchemeFromPath(const String8 & path,int32_t CA_system_id,shared_ptr<SharedLibrary> * library,T ** factory)148 bool FactoryLoader<T>::loadFactoryForSchemeFromPath(const String8& path, int32_t CA_system_id,
149                                                     shared_ptr<SharedLibrary>* library,
150                                                     T** factory) {
151     closeFactory();
152 
153     if (!openFactory(path) || !mFactory->isSystemIdSupported(CA_system_id)) {
154         closeFactory();
155         return false;
156     }
157 
158     if (library != NULL) {
159         *library = mLibrary;
160     }
161     if (factory != NULL) {
162         *factory = mFactory;
163     }
164     return true;
165 }
166 
167 template <class T>
queryPluginsFromPath(const String8 & path,vector<AidlCasPluginDescriptor> * results)168 bool FactoryLoader<T>::queryPluginsFromPath(const String8& path,
169                                             vector<AidlCasPluginDescriptor>* results) {
170     closeFactory();
171 
172     vector<CasPluginDescriptor> descriptors;
173     if (!openFactory(path) || mFactory->queryPlugins(&descriptors) != OK) {
174         closeFactory();
175         return false;
176     }
177 
178     for (auto it = descriptors.begin(); it != descriptors.end(); it++) {
179         results->push_back(
180                 AidlCasPluginDescriptor{.caSystemId = it->CA_system_id, .name = it->name.c_str()});
181     }
182     return true;
183 }
184 
185 template <class T>
openFactory(const String8 & path)186 bool FactoryLoader<T>::openFactory(const String8& path) {
187     // get strong pointer to open shared library
188     ssize_t index = mLibraryPathToOpenLibraryMap.indexOfKey(path);
189     if (index >= 0) {
190         mLibrary = mLibraryPathToOpenLibraryMap[index];
191     } else {
192         index = mLibraryPathToOpenLibraryMap.add(path, NULL);
193     }
194 
195     if (!mLibrary.get()) {
196         mLibrary = ::ndk::SharedRefBase::make<SharedLibrary>(path);
197         if (!*mLibrary) {
198             return false;
199         }
200 
201         mLibraryPathToOpenLibraryMap.replaceValueAt(index, mLibrary);
202     }
203 
204     CreateFactoryFunc createFactory = (CreateFactoryFunc)mLibrary->lookup(mCreateFactoryFuncName);
205     if (createFactory == NULL || (mFactory = createFactory()) == NULL) {
206         return false;
207     }
208     return true;
209 }
210 
211 template <class T>
closeFactory()212 void FactoryLoader<T>::closeFactory() {
213     delete mFactory;
214     mFactory = NULL;
215     mLibrary.reset();
216 }
217 
218 }  // namespace cas
219 }  // namespace hardware
220 }  // namespace android
221 }  // namespace aidl
222