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