1 /*
2  * Copyright (C) 2010 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 __PLUGIN_MANAGER_H__
18 #define __PLUGIN_MANAGER_H__
19 
20 #include <dlfcn.h>
21 #include <sys/types.h>
22 #include <dirent.h>
23 
24 #include <utils/String8.h>
25 #include <utils/Vector.h>
26 #include <utils/KeyedVector.h>
27 
28 #include <filesystem>
29 
30 namespace android {
31 
32 const char* const PLUGIN_MANAGER_CREATE = "create";
33 const char* const PLUGIN_MANAGER_DESTROY = "destroy";
34 const char* const PLUGIN_EXTENSION = ".so";
35 
36 /**
37  * This is the template class for Plugin manager.
38  *
39  * The DrmManager uses this class to handle the plugins.
40  *
41  */
42 template<typename Type>
43 class TPlugInManager {
44 private:
45     typedef void*      HANDLE;
46     typedef Type*      create_t(void);
47     typedef void       destroy_t(Type*);
48     typedef create_t*  FPCREATE;
49     typedef destroy_t* FPDESTORY;
50 
51     typedef struct _PlugInContainer {
52         String8   sPath;
53         HANDLE    hHandle;
54         FPCREATE  fpCreate;
55         FPDESTORY fpDestory;
56         Type*     pInstance;
57 
_PlugInContainer_PlugInContainer58         _PlugInContainer():
59             sPath("")
60             ,hHandle(NULL)
61             ,fpCreate(NULL)
62             ,fpDestory(NULL)
63             ,pInstance(NULL)
64             {}
65     } PlugInContainer;
66 
67     typedef KeyedVector<String8, PlugInContainer*> PlugInMap;
68     PlugInMap m_plugInMap;
69 
70     typedef Vector<String8> PlugInIdList;
71     PlugInIdList m_plugInIdList;
72 
73 public:
74     /**
75      * Load all the plug-ins in the specified directory
76      *
77      * @param[in] rsPlugInDirPath
78      *     Directory path which plug-ins (dynamic library) are stored
79      * @note Plug-ins should be implemented according to the specification
80      */
loadPlugIns(const String8 & rsPlugInDirPath)81     void loadPlugIns(const String8& rsPlugInDirPath) {
82         Vector<String8> plugInFileList = getPlugInPathList(rsPlugInDirPath);
83 
84         if (!plugInFileList.isEmpty()) {
85             for (size_t i = 0; i < plugInFileList.size(); ++i) {
86                 loadPlugIn(plugInFileList[i]);
87             }
88         }
89     }
90 
91     /**
92      * Unload all the plug-ins
93      *
94      */
unloadPlugIns()95     void unloadPlugIns() {
96         for (size_t i = 0; i < m_plugInIdList.size(); ++i) {
97             unloadPlugIn(m_plugInIdList[i]);
98         }
99         m_plugInIdList.clear();
100     }
101 
102     /**
103      * Get all the IDs of available plug-ins
104      *
105      * @return[in] plugInIdList
106      *     String type Vector in which all plug-in IDs are stored
107      */
getPlugInIdList()108     Vector<String8> getPlugInIdList() const {
109         return m_plugInIdList;
110     }
111 
112     /**
113      * Get a plug-in reference of specified ID
114      *
115      * @param[in] rsPlugInId
116      *     Plug-in ID to be used
117      * @return plugIn
118      *     Reference of specified plug-in instance
119      */
getPlugIn(const String8 & rsPlugInId)120     Type& getPlugIn(const String8& rsPlugInId) {
121         if (!contains(rsPlugInId)) {
122             // This error case never happens
123         }
124         return *(m_plugInMap.valueFor(rsPlugInId)->pInstance);
125     }
126 
127 public:
128     /**
129      * Load a plug-in stored in the specified path
130      *
131      * @param[in] rsPlugInPath
132      *     Plug-in (dynamic library) file path
133      * @note Plug-in should be implemented according to the specification
134      */
loadPlugIn(const String8 & rsPlugInPath)135     void loadPlugIn(const String8& rsPlugInPath) {
136         if (contains(rsPlugInPath)) {
137             return;
138         }
139 
140         PlugInContainer* pPlugInContainer = new PlugInContainer();
141 
142         pPlugInContainer->hHandle = dlopen(rsPlugInPath.c_str(), RTLD_LAZY);
143 
144         if (NULL == pPlugInContainer->hHandle) {
145             delete pPlugInContainer;
146             pPlugInContainer = NULL;
147             return;
148         }
149 
150         pPlugInContainer->sPath = rsPlugInPath;
151         pPlugInContainer->fpCreate
152                 = (FPCREATE)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_CREATE);
153         pPlugInContainer->fpDestory
154                 = (FPDESTORY)dlsym(pPlugInContainer->hHandle, PLUGIN_MANAGER_DESTROY);
155 
156         if (NULL != pPlugInContainer->fpCreate && NULL != pPlugInContainer->fpDestory) {
157             pPlugInContainer->pInstance = (Type*)pPlugInContainer->fpCreate();
158             m_plugInIdList.add(rsPlugInPath);
159             m_plugInMap.add(rsPlugInPath, pPlugInContainer);
160         } else {
161             dlclose(pPlugInContainer->hHandle);
162             delete pPlugInContainer;
163             pPlugInContainer = NULL;
164             return;
165         }
166     }
167 
168     /**
169      * Unload a plug-in stored in the specified path
170      *
171      * @param[in] rsPlugInPath
172      *     Plug-in (dynamic library) file path
173      */
unloadPlugIn(const String8 & rsPlugInPath)174     void unloadPlugIn(const String8& rsPlugInPath) {
175         if (!contains(rsPlugInPath)) {
176             return;
177         }
178 
179         PlugInContainer* pPlugInContainer = m_plugInMap.valueFor(rsPlugInPath);
180         pPlugInContainer->fpDestory(pPlugInContainer->pInstance);
181         dlclose(pPlugInContainer->hHandle);
182 
183         m_plugInMap.removeItem(rsPlugInPath);
184         delete pPlugInContainer;
185         pPlugInContainer = NULL;
186     }
187 
188 private:
189     /**
190      * True if TPlugInManager contains rsPlugInId
191      */
contains(const String8 & rsPlugInId)192     bool contains(const String8& rsPlugInId) {
193         return m_plugInMap.indexOfKey(rsPlugInId) != NAME_NOT_FOUND;
194     }
195 
196     /**
197      * Return file path list of plug-ins stored in the specified directory
198      *
199      * @param[in] rsDirPath
200      *     Directory path in which plug-ins are stored
201      * @return plugInFileList
202      *     String type Vector in which file path of plug-ins are stored
203      */
getPlugInPathList(const String8 & rsDirPath)204     Vector<String8> getPlugInPathList(const String8& rsDirPath) {
205         Vector<String8> fileList;
206         DIR* pDir = opendir(rsDirPath.c_str());
207         struct dirent* pEntry;
208 
209         while (NULL != pDir && NULL != (pEntry = readdir(pDir))) {
210             if (!isPlugIn(pEntry)) {
211                 continue;
212             }
213             String8 plugInPath;
214             plugInPath += rsDirPath;
215             plugInPath += "/";
216             plugInPath += pEntry->d_name;
217 
218             fileList.add(plugInPath);
219         }
220 
221         if (NULL != pDir) {
222             closedir(pDir);
223         }
224 
225         return fileList;
226     }
227 
228     /**
229      * True if the input name denotes plug-in
230      */
isPlugIn(const struct dirent * pEntry)231     bool isPlugIn(const struct dirent* pEntry) const {
232         const auto extension = std::filesystem::path(pEntry->d_name).extension();
233         // Note that the plug-in extension must exactly match case
234         return extension.string() == PLUGIN_EXTENSION;
235     }
236 
237     /**
238      * True if input entry is directory
239      */
isDirectory(const struct dirent * pEntry)240     bool isDirectory(const struct dirent* pEntry) const {
241         return DT_DIR == pEntry->d_type;
242     }
243 
244     /**
245      * True if input entry is regular file
246      */
isRegularFile(const struct dirent * pEntry)247     bool isRegularFile(const struct dirent* pEntry) const {
248         return DT_REG == pEntry->d_type;
249     }
250 
251     /**
252      * True if input entry is link
253      */
isLink(const struct dirent * pEntry)254     bool isLink(const struct dirent* pEntry) const {
255         return DT_LNK == pEntry->d_type;
256     }
257 };
258 
259 };
260 
261 #endif /* __PLUGIN_MANAGER_H__ */
262 
263