• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "plugin.h"
17 #include <utility>
18 #include "hilog/log.h"
19 #include "impl_class_mgr.h"
20 #include "json.hpp"
21 #include "json_helper.h"
22 #include "log_tags.h"
23 #include "platform_adp.h"
24 #include "singleton.h"
25 #ifdef _WIN32
26 #include <windows.h>
27 HMODULE hDll = NULL;
28 #endif
29 
30 namespace OHOS {
31 namespace MultimediaPlugin {
32 using nlohmann::json;
33 using std::istream;
34 using std::istringstream;
35 using std::recursive_mutex;
36 using std::size_t;
37 using std::string;
38 using std::weak_ptr;
39 using namespace OHOS::HiviewDFX;
40 
41 enum class VersionParseStep : int32_t { STEP_MAJOR = 0, STEP_MINOR, STEP_MICRO, STEP_NANO, STEP_FINISHED };
42 
43 struct VersionNum {
44     uint16_t major = 0;
45     uint16_t minor = 0;
46     uint16_t micro = 0;
47     uint16_t nano = 0;
48 };
49 
50 static constexpr HiLogLabel LABEL = { LOG_CORE, LOG_TAG_DOMAIN_ID_PLUGIN, "Plugin" };
51 
Plugin()52 Plugin::Plugin()
53     : platformAdp_(DelayedRefSingleton<PlatformAdp>::GetInstance()),
54       implClassMgr_(DelayedRefSingleton<ImplClassMgr>::GetInstance()) {}
55 
~Plugin()56 Plugin::~Plugin()
57 {
58     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
59     if (refNum_ != 0) {
60         // this situation does not happen in design.
61         // the process context can guarantee that this will not happen.
62         // the judgment statement here is for protection and positioning purposes only.
63         HiLog::Error(LABEL, "release plugin: refNum: %{public}u.", refNum_);
64     }
65 
66     implClassMgr_.DeleteClass(plugin_);
67     FreeLibrary();
68 }
69 
Register(istream & metadata,string && libraryPath,weak_ptr<Plugin> & plugin)70 uint32_t Plugin::Register(istream &metadata, string &&libraryPath, weak_ptr<Plugin> &plugin)
71 {
72     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
73     if (state_ != PluginState::PLUGIN_STATE_UNREGISTER) {
74         guard.unlock();
75         HiLog::Error(LABEL, "repeat registration.");
76         return ERR_INTERNAL;
77     }
78 
79     auto ret = RegisterMetadata(metadata, plugin);
80     if (ret != SUCCESS) {
81         guard.unlock();
82         HiLog::Error(LABEL, "failed to register metadata, ERRNO: %{public}u.", ret);
83         return ret;
84     }
85 
86     libraryPath_ = std::move(libraryPath);
87     plugin_ = plugin;
88     state_ = PluginState::PLUGIN_STATE_REGISTERED;
89     return SUCCESS;
90 }
91 
CfiStartFunc_(PluginStartFunc startFunc_)92 bool CfiStartFunc_(PluginStartFunc startFunc_) __attribute__((no_sanitize("cfi")))
93 {
94     return startFunc_();
95 }
96 
Ref()97 uint32_t Plugin::Ref()
98 {
99     // once the client make a ref, it can use the plugin at any time,
100     // so we do the necessary preparations here.
101     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
102     if (state_ == PluginState::PLUGIN_STATE_REGISTERED) {
103         if (ResolveLibrary() != SUCCESS) {
104             guard.unlock();
105             HiLog::Error(LABEL, "failed to resolve library.");
106             return ERR_GENERAL;
107         }
108         state_ = PluginState::PLUGIN_STATE_RESOLVED;
109     }
110 
111     if (state_ == PluginState::PLUGIN_STATE_RESOLVED) {
112         // maybe asynchronous, or for reduce the locking time
113         state_ = PluginState::PLUGIN_STATE_STARTING;
114         if (!CfiStartFunc_(startFunc_)) {
115             HiLog::Error(LABEL, "failed to start plugin.");
116             FreeLibrary();
117             state_ = PluginState::PLUGIN_STATE_REGISTERED;
118             return ERR_GENERAL;
119         }
120         state_ = PluginState::PLUGIN_STATE_ACTIVE;
121     }
122 
123     if (state_ != PluginState::PLUGIN_STATE_ACTIVE) {
124         HiLog::Error(LABEL, "plugin ref: state error, state: %{public}d.", state_);
125         return ERR_GENERAL;
126     }
127 
128     ++refNum_;
129     HiLog::Debug(LABEL, "plugin refNum: %{public}d.", refNum_);
130     return SUCCESS;
131 }
132 
DeRef()133 void Plugin::DeRef()
134 {
135     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
136     if (refNum_ == 0) {
137         // this situation does not happen in design.
138         // the process context can guarantee that this will not happen.
139         // the judgment statement here is for protection and positioning purposes only.
140         guard.unlock();
141         HiLog::Error(LABEL, "DeRef while RefNum is zero.");
142         return;
143     }
144 
145     --refNum_;
146     HiLog::Debug(LABEL, "plugin refNum: %{public}d.", refNum_);
147 }
148 
Block()149 void Plugin::Block()
150 {
151     // used to protect against business interruptions during plugin upgrades.
152     // after the plugin is upgraded, if the original .so is being used,
153     // it cannot be released immediately and should be locked,
154     // and the subsequent requests are migrated to the new .so.
155     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
156     blocked_ = true;
157 }
158 
Unblock()159 void Plugin::Unblock()
160 {
161     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
162     blocked_ = false;
163 }
164 
GetCreateFunc()165 PluginCreateFunc Plugin::GetCreateFunc()
166 {
167     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
168     if ((state_ != PluginState::PLUGIN_STATE_ACTIVE) || (refNum_ == 0)) {
169         // In this case, we can't guarantee that the pointer is lasting valid.
170         HiLog::Error(LABEL, "failed to get create func, State: %{public}d, RefNum: %{public}u.", state_, refNum_);
171         return nullptr;
172     }
173 
174     return createFunc_;
175 }
176 
GetLibraryPath() const177 const string &Plugin::GetLibraryPath() const
178 {
179     return libraryPath_;
180 }
181 
GetPackageName() const182 const string &Plugin::GetPackageName() const
183 {
184     return packageName_;
185 }
186 
187 // ------------------------------- private method -------------------------------
ResolveLibrary()188 uint32_t Plugin::ResolveLibrary()
189 {
190     std::string pluginStartSymbol = "PluginExternalStart";
191     std::string pluginStopSymbol = "PluginExternalStop";
192     std::string pluginCreateSymbol = "PluginExternalCreate";
193 
194 #ifdef _WIN32
195     hDll = platformAdp_.AdpLoadLibrary(libraryPath_);
196     if (hDll == NULL) {
197         HiLog::Error(LABEL, "failed to load library.");
198         return ERR_GENERAL;
199     }
200 
201     startFunc_ = (PluginStartFunc)platformAdp_.AdpGetSymAddress(hDll, pluginStartSymbol);
202     stopFunc_ = (PluginStopFunc)platformAdp_.AdpGetSymAddress(hDll, pluginStopSymbol);
203     createFunc_ = (PluginCreateFunc)platformAdp_.AdpGetSymAddress(hDll, pluginCreateSymbol);
204     if (startFunc_ == NULL || stopFunc_ == NULL || createFunc_ == NULL) {
205         HiLog::Error(LABEL, "failed to get export symbol for the plugin.");
206         FreeLibrary();
207         return ERR_GENERAL;
208     }
209 
210     return SUCCESS;
211 #elif defined(A_PLATFORM) || defined(IOS_PLATFORM)
212     startFunc_ = PluginExternalStart;
213     stopFunc_ = PluginExternalStop;
214     createFunc_ = PluginExternalCreate;
215     return SUCCESS;
216 #else
217     handle_ = platformAdp_.LoadLibrary(libraryPath_);
218     if (handle_ == nullptr) {
219         HiLog::Error(LABEL, "failed to load library.");
220         return ERR_GENERAL;
221     }
222 
223     startFunc_ = (PluginStartFunc)platformAdp_.GetSymAddress(handle_, pluginStartSymbol);
224     stopFunc_ = (PluginStopFunc)platformAdp_.GetSymAddress(handle_, pluginStopSymbol);
225     createFunc_ = (PluginCreateFunc)platformAdp_.GetSymAddress(handle_, pluginCreateSymbol);
226     if (startFunc_ == nullptr || stopFunc_ == nullptr || createFunc_ == nullptr) {
227         HiLog::Error(LABEL, "failed to get export symbol for the plugin.");
228         FreeLibrary();
229         return ERR_GENERAL;
230     }
231 
232     return SUCCESS;
233 #endif
234 }
235 
FreeLibrary()236 void Plugin::FreeLibrary()
237 {
238 #ifdef _WIN32
239     if (state_ == PluginState::PLUGIN_STATE_STARTING || state_ == PluginState::PLUGIN_STATE_ACTIVE) {
240         if (stopFunc_ != NULL) {
241             stopFunc_();
242         }
243     }
244     if (handle_ == NULL) {
245         return;
246     }
247     platformAdp_.AdpFreeLibrary(hDll);
248     hDll = NULL;
249     startFunc_ = NULL;
250     stopFunc_ = NULL;
251     createFunc_ = NULL;
252 #elif defined(A_PLATFORM) || defined(IOS_PLATFORM)
253     startFunc_ = nullptr;
254     stopFunc_ = nullptr;
255     createFunc_ = nullptr;
256 #else
257     if (state_ == PluginState::PLUGIN_STATE_STARTING || state_ == PluginState::PLUGIN_STATE_ACTIVE) {
258         if (stopFunc_ != nullptr) {
259             stopFunc_();
260         }
261     }
262 
263     if (handle_ == nullptr) {
264         return;
265     }
266 
267     platformAdp_.FreeLibrary(handle_);
268     handle_ = nullptr;
269     startFunc_ = nullptr;
270     stopFunc_ = nullptr;
271     createFunc_ = nullptr;
272 #endif
273 }
274 
RegisterMetadata(istream & metadata,weak_ptr<Plugin> & plugin)275 uint32_t Plugin::RegisterMetadata(istream &metadata, weak_ptr<Plugin> &plugin)
276 {
277     json root;
278     metadata >> root;
279     if (JsonHelper::GetStringValue(root, "packageName", packageName_) != SUCCESS) {
280         HiLog::Error(LABEL, "read packageName failed.");
281         return ERR_INVALID_PARAMETER;
282     }
283 
284     string targetVersion;
285     if (JsonHelper::GetStringValue(root, "targetVersion", targetVersion) != SUCCESS) {
286         HiLog::Error(LABEL, "read targetVersion failed.");
287         return ERR_INVALID_PARAMETER;
288     }
289     uint32_t ret = CheckTargetVersion(targetVersion);
290     if (ret != SUCCESS) {
291         // target version is not compatible
292         HiLog::Error(LABEL, "check targetVersion failed, Version: %{public}s, ERRNO: %{public}u.",
293                      targetVersion.c_str(), ret);
294         return ret;
295     }
296 
297     if (JsonHelper::GetStringValue(root, "version", version_) != SUCCESS) {
298         HiLog::Error(LABEL, "read version failed.");
299         return ERR_INVALID_PARAMETER;
300     }
301     VersionNum versionNum;
302     ret = AnalyzeVersion(version_, versionNum);
303     if (ret != SUCCESS) {
304         HiLog::Error(LABEL, "check version failed, Version: %{public}s, ERRNO: %{public}u.", version_.c_str(), ret);
305         return ret;
306     }
307 
308     size_t classNum;
309     if (JsonHelper::GetArraySize(root, "classes", classNum) != SUCCESS) {
310         HiLog::Error(LABEL, "get array size of classes failed.");
311         return ERR_INVALID_PARAMETER;
312     }
313     HiLog::Debug(LABEL, "parse class num: %{public}zu.", classNum);
314     for (size_t i = 0; i < classNum; i++) {
315         const json &classInfo = root["classes"][i];
316         if (implClassMgr_.AddClass(plugin, classInfo) != SUCCESS) {
317             HiLog::Error(LABEL, "failed to add class, index: %{public}zu.", i);
318             continue;
319         }
320     }
321 
322     return SUCCESS;
323 }
324 
CheckTargetVersion(const string & targetVersion)325 uint32_t Plugin::CheckTargetVersion(const string &targetVersion)
326 {
327     VersionNum versionNum;
328     auto ret = AnalyzeVersion(targetVersion, versionNum);
329     if (ret != SUCCESS) {
330         HiLog::Error(LABEL, "failed to analyze version, ERRNO: %{public}u.", ret);
331         return ret;
332     }
333 
334     return SUCCESS;
335 }
336 
AnalyzeVersion(const string & versionInfo,VersionNum & versionNum)337 uint32_t Plugin::AnalyzeVersion(const string &versionInfo, VersionNum &versionNum)
338 {
339     VersionParseStep step = VersionParseStep::STEP_MAJOR;
340     istringstream versionInput(versionInfo);
341     uint16_t versionArray[VERSION_ARRAY_SIZE] = { 0 };  // major, minor, micro, nano.
342     string tmp;
343 
344     while (getline(versionInput, tmp, '.')) {
345         auto ret = ExecuteVersionAnalysis(tmp, step, versionArray);
346         if (ret != SUCCESS) {
347             HiLog::Error(LABEL, "failed to execute version analysis, ERRNO: %{public}u.", ret);
348             return ret;
349         }
350     }
351 
352     if (step == VersionParseStep::STEP_NANO) {
353         // we treat nano version as optional, and default 0.
354         HiLog::Debug(LABEL, "default nano version 0.");
355         versionArray[VERSION_NANO_INDEX] = 0;
356         step = VersionParseStep::STEP_FINISHED;
357     }
358 
359     if (step != VersionParseStep::STEP_FINISHED) {
360         HiLog::Error(LABEL, "analysis version failed, step = %{public}d.", step);
361         return ERR_INVALID_PARAMETER;
362     }
363 
364     versionNum.major = versionArray[VERSION_MAJOR_INDEX];
365     versionNum.minor = versionArray[VERSION_MINOR_INDEX];
366     versionNum.micro = versionArray[VERSION_MICRO_INDEX];
367     versionNum.nano = versionArray[VERSION_NANO_INDEX];
368 
369     HiLog::Debug(LABEL, "analysis result: %{public}u.%{public}u.%{public}u.%{public}u.", versionNum.major,
370                  versionNum.minor, versionNum.micro, versionNum.nano);
371 
372     return SUCCESS;
373 }
374 
ExecuteVersionAnalysis(const string & input,VersionParseStep & step,uint16_t (& versionNum)[VERSION_ARRAY_SIZE])375 uint32_t Plugin::ExecuteVersionAnalysis(const string &input, VersionParseStep &step,
376                                         uint16_t (&versionNum)[VERSION_ARRAY_SIZE])
377 {
378     switch (step) {
379         case VersionParseStep::STEP_MAJOR: {
380             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_MAJOR_INDEX]);
381             if (ret != SUCCESS) {
382                 HiLog::Error(LABEL, "read major version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
383                              ret);
384                 return ret;
385             }
386             step = VersionParseStep::STEP_MINOR;
387             break;
388         }
389         case VersionParseStep::STEP_MINOR: {
390             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_MINOR_INDEX]);
391             if (ret != SUCCESS) {
392                 HiLog::Error(LABEL, "read minor version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
393                              ret);
394                 return ret;
395             }
396             step = VersionParseStep::STEP_MICRO;
397             break;
398         }
399         case VersionParseStep::STEP_MICRO: {
400             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_MICRO_INDEX]);
401             if (ret != SUCCESS) {
402                 HiLog::Error(LABEL, "read micro version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
403                              ret);
404                 return ret;
405             }
406             step = VersionParseStep::STEP_NANO;
407             break;
408         }
409         case VersionParseStep::STEP_NANO: {
410             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_NANO_INDEX]);
411             if (ret != SUCCESS) {
412                 HiLog::Error(LABEL, "read nano version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
413                              ret);
414                 return ret;
415             }
416             step = VersionParseStep::STEP_FINISHED;
417             break;
418         }
419         default: {
420             HiLog::Error(LABEL, "read redundant version data, input: %{public}s.", input.c_str());
421             return ERR_INVALID_PARAMETER;
422         }
423     }
424 
425     return SUCCESS;
426 }
427 
GetUint16ValueFromDecimal(const string & source,uint16_t & result)428 uint32_t Plugin::GetUint16ValueFromDecimal(const string &source, uint16_t &result)
429 {
430     if (source.empty() || source.size() > UINT16_MAX_DECIMAL_DIGITS) {
431         HiLog::Error(LABEL, "invalid string of uint16: %{public}s.", source.c_str());
432         return ERR_INVALID_PARAMETER;
433     }
434 
435     // determine if all characters are numbers.
436     for (const auto &character : source) {
437         if (character < '0' || character > '9') {
438             HiLog::Error(LABEL, "character out of the range of digital: %{public}s.", source.c_str());
439             return ERR_INVALID_PARAMETER;
440         }
441     }
442 
443     unsigned long tmp = stoul(source);
444     if (tmp > UINT16_MAX_VALUE) {
445         HiLog::Error(LABEL, "result out of the range of uint16: %{public}s.", source.c_str());
446         return ERR_INVALID_PARAMETER;
447     }
448 
449     result = static_cast<uint16_t>(tmp);
450     return SUCCESS;
451 }
452 } // namespace MultimediaPlugin
453 } // namespace OHOS
454