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