AddOn manager
[platform/core/uifw/dali-adaptor.git] / dali / internal / addons / linux / addon-manager-impl-linux.cpp
1 /*
2  * Copyright (c) 2020 Samsung Electronics Co., Ltd.
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
18 // INTERNAL INCLUDES
19 #include <dali/internal/addons/linux/addon-manager-impl-linux.h>
20 #include <dali/devel-api/adaptor-framework/environment-variable.h>
21 #include <dali/internal/system/common/environment-variables.h>
22
23 // EXTERNAL INCLUDES
24 #include <dali/integration-api/debug.h>
25
26 #include <dlfcn.h>
27 #include <functional>
28 #include <algorithm>
29 #include <iterator>
30 #include <sstream>
31
32 namespace Dali
33 {
34 namespace Internal
35 {
36
37 AddOnManagerLinux::AddOnManagerLinux() = default;
38
39 AddOnManagerLinux::~AddOnManagerLinux() = default;
40
41 void AddOnManagerLinux::RegisterAddOnDispatchTable( const AddOnDispatchTable* dispatchTable )
42 {
43   mAddOnNames.emplace_back(dispatchTable->name);
44   mAddOnCache.emplace_back();
45   mAddOnCache.back().GetGlobalProc = dispatchTable->GetGlobalProc;
46   mAddOnCache.back().GetInstanceProc = dispatchTable->GetInstanceProc;
47   mAddOnCache.back().GetAddOnInfo = dispatchTable->GetAddOnInfo;
48
49   auto& callbacks = mAddOnCache.back().lifecycleCallbacks;
50   auto initEvent = [&callbacks](uint32_t event, void(*fn)() ) {
51     callbacks[event].function = fn;
52     callbacks[event].initialized = true;
53   };
54
55   initEvent( LifecycleCallback::EVENT_START, dispatchTable->OnStart );
56   initEvent( LifecycleCallback::EVENT_STOP, dispatchTable->OnStop );
57   initEvent( LifecycleCallback::EVENT_PAUSE, dispatchTable->OnPause );
58   initEvent( LifecycleCallback::EVENT_RESUME, dispatchTable->OnResume );
59 }
60
61 std::vector<std::string> AddOnManagerLinux::EnumerateAddOns()
62 {
63   if( mAddOnNames.empty() )
64   {
65     // AddOn libs must be separated with ':' character
66     const char *addonsLibs = Dali::EnvironmentVariable::GetEnvironmentVariable( DALI_ENV_ADDONS_LIBS );
67     if (!addonsLibs)
68     {
69       return {};
70     }
71
72     // Get the path where addon libs are stored
73     const char *addonsPath = Dali::EnvironmentVariable::GetEnvironmentVariable( DALI_ENV_ADDONS_PATH );
74     std::string addonsPathStr(addonsPath ? addonsPath : "/usr/lib");
75
76     // Split libs
77     std::string addonLibsStr(addonsLibs);
78     std::vector<std::string> results;
79     results.emplace_back();
80
81     std::find_if(addonLibsStr.begin(), addonLibsStr.end(), [&results](char &c)
82     {
83       if (c == ':')
84       {
85         results.emplace_back();
86       }
87       else
88       {
89         results.back() += c;
90       }
91       return false;
92     });
93
94     const char *EXTENSION_PATH = (addonsPath) ? addonsPath : "/usr/lib";
95
96     for (auto &name : results)
97     {
98       std::string fullPath(EXTENSION_PATH);
99       fullPath += "/";
100       fullPath += name;
101
102       // open lib, look for essential symbols. The libary is opened with RTLD_DEEPBIND flag
103       // to make sure the local symbol table is going to be used during lookup first.
104       auto* handle = dlopen(fullPath.c_str(), RTLD_DEEPBIND|RTLD_LAZY);
105       if (handle)
106       {
107         auto& cacheEntry = mAddOnCache.back();
108         AddOnInfo info{};
109         cacheEntry.GetAddOnInfo(info);
110         cacheEntry.info             = info;
111         cacheEntry.addOnLib         = fullPath;
112         cacheEntry.libHandle        = handle;
113         cacheEntry.opened           = false;
114       }
115       else
116       {
117         DALI_LOG_ERROR("Can't open library: %s, error: %s\n", fullPath.c_str(), dlerror());
118       }
119     }
120   }
121   return mAddOnNames;
122 }
123
124 bool AddOnManagerLinux::GetAddOnInfo(const std::string& name, AddOnInfo& info )
125 {
126   if( mAddOnNames.empty() )
127   {
128     EnumerateAddOns();
129   }
130
131   if( mAddOnNames.empty() )
132   {
133     return false;
134   }
135
136   auto iter = std::find_if( mAddOnCache.begin(), mAddOnCache.end(), [name]( AddOnCacheEntry& item )
137   {
138     return (item.info.name == name);
139   });
140
141   if (iter == mAddOnCache.end())
142   {
143     return false;
144   }
145
146   info = iter->info;
147   return true;
148 }
149
150 std::vector<Dali::AddOnLibrary> AddOnManagerLinux::LoadAddOns( const std::vector<std::string>& addonNames )
151 {
152   std::vector<AddOnLibrary> retval{};
153   retval.resize( addonNames.size() );
154   std::fill( retval.begin(), retval.end(), nullptr );
155
156   if( mAddOnCache.empty() )
157   {
158     EnumerateAddOns();
159     if(mAddOnCache.empty())
160     {
161       // no any extensions
162       return retval;
163     }
164   }
165
166   auto nameIndex = 0u;
167   for( const auto& name : addonNames )
168   {
169     auto index = 0u;
170     nameIndex++;
171     auto iter = std::find_if( mAddOnCache.begin(), mAddOnCache.end(), [&index, name]( AddOnCacheEntry& item )
172     {
173       ++index;
174       return (item.info.name == name);
175     });
176
177     if(iter == mAddOnCache.end())
178     {
179       continue;
180     }
181
182     if(!iter->opened && iter->libHandle)
183     {
184       // Open library, pull symbols and keep the handle
185       auto& entry = *iter;
186       entry.opened = true;
187     }
188
189     // Store cache index of extension for indirect calling
190     // Stored number in this implementation is always +1 (0 is nullptr, unsuccessful)
191     retval[nameIndex-1] = reinterpret_cast<void*>( index );
192   }
193
194   return retval;
195 }
196
197 void* AddOnManagerLinux::GetGlobalProc( const Dali::AddOnLibrary& addonHandle, const char* procName )
198 {
199   if( !addonHandle )
200   {
201     return nullptr;
202   }
203
204   auto index = (intptr_t(addonHandle));
205   const auto& entry = mAddOnCache[ index-1 ];
206
207   if(entry.opened && entry.libHandle )
208   {
209     // First call into dispatch table
210     auto retval = entry.GetGlobalProc( procName );
211     if( !retval )
212     {
213       // fallback
214       retval = dlsym( entry.libHandle, procName );
215     }
216     return retval;
217   }
218   else
219   {
220     DALI_LOG_ERROR("AddOn: GetGlobalProc() library failed!\n");
221   }
222   return nullptr;
223 }
224
225 void* AddOnManagerLinux::GetInstanceProc( const Dali::AddOnLibrary& addonHandle, const char* procName )
226 {
227   if( !addonHandle )
228   {
229     return nullptr;
230   }
231
232   auto index = (intptr_t(addonHandle));
233   const auto& entry = mAddOnCache[ index-1 ];
234   if(entry.opened && entry.libHandle )
235   {
236     // First call into dispatch table
237     auto retval = entry.GetInstanceProc( procName );
238     if( !retval )
239     {
240       // fallback
241       retval = dlsym( entry.libHandle, procName );
242     }
243     return retval;
244   }
245   return nullptr;
246 }
247
248 void AddOnManagerLinux::Pause()
249 {
250   InvokeLifecycleFunction( LifecycleCallback::EVENT_PAUSE );
251 }
252
253 void AddOnManagerLinux::Resume()
254 {
255   InvokeLifecycleFunction( LifecycleCallback::EVENT_RESUME );
256 }
257
258 void AddOnManagerLinux::Start()
259 {
260   InvokeLifecycleFunction( LifecycleCallback::EVENT_START );
261 }
262
263 void AddOnManagerLinux::Stop()
264 {
265   InvokeLifecycleFunction( LifecycleCallback::EVENT_STOP );
266 }
267
268 void AddOnManagerLinux::InvokeLifecycleFunction( uint32_t lifecycleEvent )
269 {
270   for( auto& entry : mAddOnCache )
271   {
272     auto& callback = entry.lifecycleCallbacks[lifecycleEvent];
273
274     // If AddOn didn't auto-register try to pull symbols
275     // directly out of the addon
276     if(!callback.initialized)
277     {
278       callback.function    = reinterpret_cast<decltype(callback.function)>(entry.GetGlobalProc(callback.functionName.c_str()));
279       callback.initialized = true;
280     }
281     if(callback.function)
282     {
283       callback.function();
284     }
285   }
286 }
287
288 } // namespace Internal
289 } // namespace Dali