• 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 
Ref()92 uint32_t Plugin::Ref()
93 {
94     // once the client make a ref, it can use the plugin at any time,
95     // so we do the necessary preparations here.
96     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
97     if (state_ == PluginState::PLUGIN_STATE_REGISTERED) {
98         if (ResolveLibrary() != SUCCESS) {
99             guard.unlock();
100             HiLog::Error(LABEL, "failed to resolve library.");
101             return ERR_GENERAL;
102         }
103         state_ = PluginState::PLUGIN_STATE_RESOLVED;
104     }
105 
106     if (state_ == PluginState::PLUGIN_STATE_RESOLVED) {
107         // maybe asynchronous, or for reduce the locking time
108         state_ = PluginState::PLUGIN_STATE_STARTING;
109         if (!startFunc_()) {
110             HiLog::Error(LABEL, "failed to start plugin.");
111             FreeLibrary();
112             state_ = PluginState::PLUGIN_STATE_REGISTERED;
113             return ERR_GENERAL;
114         }
115         state_ = PluginState::PLUGIN_STATE_ACTIVE;
116     }
117 
118     if (state_ != PluginState::PLUGIN_STATE_ACTIVE) {
119         HiLog::Error(LABEL, "plugin ref: state error, state: %{public}d.", state_);
120         return ERR_GENERAL;
121     }
122 
123     ++refNum_;
124     HiLog::Debug(LABEL, "plugin refNum: %{public}d.", refNum_);
125     return SUCCESS;
126 }
127 
DeRef()128 void Plugin::DeRef()
129 {
130     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
131     if (refNum_ == 0) {
132         // this situation does not happen in design.
133         // the process context can guarantee that this will not happen.
134         // the judgment statement here is for protection and positioning purposes only.
135         guard.unlock();
136         HiLog::Error(LABEL, "DeRef while RefNum is zero.");
137         return;
138     }
139 
140     --refNum_;
141     HiLog::Debug(LABEL, "plugin refNum: %{public}d.", refNum_);
142 }
143 
Block()144 void Plugin::Block()
145 {
146     // used to protect against business interruptions during plugin upgrades.
147     // after the plugin is upgraded, if the original .so is being used,
148     // it cannot be released immediately and should be locked,
149     // and the subsequent requests are migrated to the new .so.
150     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
151     blocked_ = true;
152 }
153 
Unblock()154 void Plugin::Unblock()
155 {
156     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
157     blocked_ = false;
158 }
159 
GetCreateFunc()160 PluginCreateFunc Plugin::GetCreateFunc()
161 {
162     std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
163     if ((state_ != PluginState::PLUGIN_STATE_ACTIVE) || (refNum_ == 0)) {
164         // In this case, we can't guarantee that the pointer is lasting valid.
165         HiLog::Error(LABEL, "failed to get create func, State: %{public}d, RefNum: %{public}u.", state_, refNum_);
166         return nullptr;
167     }
168 
169     return createFunc_;
170 }
171 
GetLibraryPath() const172 const string &Plugin::GetLibraryPath() const
173 {
174     return libraryPath_;
175 }
176 
GetPackageName() const177 const string &Plugin::GetPackageName() const
178 {
179     return packageName_;
180 }
181 
182 // ------------------------------- private method -------------------------------
ResolveLibrary()183 uint32_t Plugin::ResolveLibrary()
184 {
185     std::string pluginStartSymbol = "PluginExternalStart";
186     std::string pluginStopSymbol = "PluginExternalStop";
187     std::string pluginCreateSymbol = "PluginExternalCreate";
188 
189 #ifdef _WIN32
190     hDll = platformAdp_.AdpLoadLibrary(libraryPath_);
191     if (hDll == NULL) {
192         HiLog::Error(LABEL, "failed to load library.");
193         return ERR_GENERAL;
194     }
195 
196     startFunc_ = (PluginStartFunc)platformAdp_.AdpGetSymAddress(hDll, pluginStartSymbol);
197     stopFunc_ = (PluginStopFunc)platformAdp_.AdpGetSymAddress(hDll, pluginStopSymbol);
198     createFunc_ = (PluginCreateFunc)platformAdp_.AdpGetSymAddress(hDll, pluginCreateSymbol);
199     if (startFunc_ == NULL || stopFunc_ == NULL || createFunc_ == NULL) {
200         HiLog::Error(LABEL, "failed to get export symbol for the plugin.");
201         FreeLibrary();
202         return ERR_GENERAL;
203     }
204 
205     return SUCCESS;
206 #else
207     handle_ = platformAdp_.LoadLibrary(libraryPath_);
208     if (handle_ == nullptr) {
209         HiLog::Error(LABEL, "failed to load library.");
210         return ERR_GENERAL;
211     }
212 
213     startFunc_ = (PluginStartFunc)platformAdp_.GetSymAddress(handle_, pluginStartSymbol);
214     stopFunc_ = (PluginStopFunc)platformAdp_.GetSymAddress(handle_, pluginStopSymbol);
215     createFunc_ = (PluginCreateFunc)platformAdp_.GetSymAddress(handle_, pluginCreateSymbol);
216     if (startFunc_ == nullptr || stopFunc_ == nullptr || createFunc_ == nullptr) {
217         HiLog::Error(LABEL, "failed to get export symbol for the plugin.");
218         FreeLibrary();
219         return ERR_GENERAL;
220     }
221 
222     return SUCCESS;
223 #endif
224 }
225 
FreeLibrary()226 void Plugin::FreeLibrary()
227 {
228 #ifdef _WIN32
229     if (state_ == PluginState::PLUGIN_STATE_STARTING || state_ == PluginState::PLUGIN_STATE_ACTIVE) {
230         if (stopFunc_ != NULL) {
231             stopFunc_();
232         }
233     }
234     if (handle_ == NULL) {
235         return;
236     }
237     platformAdp_.AdpFreeLibrary(hDll);
238     hDll = NULL;
239     startFunc_ = NULL;
240     stopFunc_ = NULL;
241     createFunc_ = NULL;
242 #else
243     if (state_ == PluginState::PLUGIN_STATE_STARTING || state_ == PluginState::PLUGIN_STATE_ACTIVE) {
244         if (stopFunc_ != nullptr) {
245             stopFunc_();
246         }
247     }
248 
249     if (handle_ == nullptr) {
250         return;
251     }
252 
253     platformAdp_.FreeLibrary(handle_);
254     handle_ = nullptr;
255     startFunc_ = nullptr;
256     stopFunc_ = nullptr;
257     createFunc_ = nullptr;
258 #endif
259 }
260 
RegisterMetadata(istream & metadata,weak_ptr<Plugin> & plugin)261 uint32_t Plugin::RegisterMetadata(istream &metadata, weak_ptr<Plugin> &plugin)
262 {
263     json root;
264     metadata >> root;
265     if (JsonHelper::GetStringValue(root, "packageName", packageName_) != SUCCESS) {
266         HiLog::Error(LABEL, "read packageName failed.");
267         return ERR_INVALID_PARAMETER;
268     }
269 
270     string targetVersion;
271     if (JsonHelper::GetStringValue(root, "targetVersion", targetVersion) != SUCCESS) {
272         HiLog::Error(LABEL, "read targetVersion failed.");
273         return ERR_INVALID_PARAMETER;
274     }
275     uint32_t ret = CheckTargetVersion(targetVersion);
276     if (ret != SUCCESS) {
277         // target version is not compatible
278         HiLog::Error(LABEL, "check targetVersion failed, Version: %{public}s, ERRNO: %{public}u.",
279                      targetVersion.c_str(), ret);
280         return ret;
281     }
282 
283     if (JsonHelper::GetStringValue(root, "version", version_) != SUCCESS) {
284         HiLog::Error(LABEL, "read version failed.");
285         return ERR_INVALID_PARAMETER;
286     }
287     VersionNum versionNum;
288     ret = AnalyzeVersion(version_, versionNum);
289     if (ret != SUCCESS) {
290         HiLog::Error(LABEL, "check version failed, Version: %{public}s, ERRNO: %{public}u.", version_.c_str(), ret);
291         return ret;
292     }
293 
294     size_t classNum;
295     if (JsonHelper::GetArraySize(root, "classes", classNum) != SUCCESS) {
296         HiLog::Error(LABEL, "get array size of classes failed.");
297         return ERR_INVALID_PARAMETER;
298     }
299     HiLog::Debug(LABEL, "parse class num: %{public}zu.", classNum);
300     for (size_t i = 0; i < classNum; i++) {
301         const json &classInfo = root["classes"][i];
302         if (implClassMgr_.AddClass(plugin, classInfo) != SUCCESS) {
303             HiLog::Error(LABEL, "failed to add class, index: %{public}zu.", i);
304             continue;
305         }
306     }
307 
308     return SUCCESS;
309 }
310 
CheckTargetVersion(const string & targetVersion)311 uint32_t Plugin::CheckTargetVersion(const string &targetVersion)
312 {
313     VersionNum versionNum;
314     auto ret = AnalyzeVersion(targetVersion, versionNum);
315     if (ret != SUCCESS) {
316         HiLog::Error(LABEL, "failed to analyze version, ERRNO: %{public}u.", ret);
317         return ret;
318     }
319 
320     return SUCCESS;
321 }
322 
AnalyzeVersion(const string & versionInfo,VersionNum & versionNum)323 uint32_t Plugin::AnalyzeVersion(const string &versionInfo, VersionNum &versionNum)
324 {
325     VersionParseStep step = VersionParseStep::STEP_MAJOR;
326     istringstream versionInput(versionInfo);
327     uint16_t versionArray[VERSION_ARRAY_SIZE] = { 0 };  // major, minor, micro, nano.
328     string tmp;
329 
330     while (getline(versionInput, tmp, '.')) {
331         auto ret = ExecuteVersionAnalysis(tmp, step, versionArray);
332         if (ret != SUCCESS) {
333             HiLog::Error(LABEL, "failed to execute version analysis, ERRNO: %{public}u.", ret);
334             return ret;
335         }
336     }
337 
338     if (step == VersionParseStep::STEP_NANO) {
339         // we treat nano version as optional, and default 0.
340         HiLog::Debug(LABEL, "default nano version 0.");
341         versionArray[VERSION_NANO_INDEX] = 0;
342         step = VersionParseStep::STEP_FINISHED;
343     }
344 
345     if (step != VersionParseStep::STEP_FINISHED) {
346         HiLog::Error(LABEL, "analysis version failed, step = %{public}d.", step);
347         return ERR_INVALID_PARAMETER;
348     }
349 
350     versionNum.major = versionArray[VERSION_MAJOR_INDEX];
351     versionNum.minor = versionArray[VERSION_MINOR_INDEX];
352     versionNum.micro = versionArray[VERSION_MICRO_INDEX];
353     versionNum.nano = versionArray[VERSION_NANO_INDEX];
354 
355     HiLog::Debug(LABEL, "analysis result: %{public}u.%{public}u.%{public}u.%{public}u.", versionNum.major,
356                  versionNum.minor, versionNum.micro, versionNum.nano);
357 
358     return SUCCESS;
359 }
360 
ExecuteVersionAnalysis(const string & input,VersionParseStep & step,uint16_t (& versionNum)[VERSION_ARRAY_SIZE])361 uint32_t Plugin::ExecuteVersionAnalysis(const string &input, VersionParseStep &step,
362                                         uint16_t (&versionNum)[VERSION_ARRAY_SIZE])
363 {
364     switch (step) {
365         case VersionParseStep::STEP_MAJOR: {
366             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_MAJOR_INDEX]);
367             if (ret != SUCCESS) {
368                 HiLog::Error(LABEL, "read major version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
369                              ret);
370                 return ret;
371             }
372             step = VersionParseStep::STEP_MINOR;
373             break;
374         }
375         case VersionParseStep::STEP_MINOR: {
376             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_MINOR_INDEX]);
377             if (ret != SUCCESS) {
378                 HiLog::Error(LABEL, "read minor version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
379                              ret);
380                 return ret;
381             }
382             step = VersionParseStep::STEP_MICRO;
383             break;
384         }
385         case VersionParseStep::STEP_MICRO: {
386             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_MICRO_INDEX]);
387             if (ret != SUCCESS) {
388                 HiLog::Error(LABEL, "read micro version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
389                              ret);
390                 return ret;
391             }
392             step = VersionParseStep::STEP_NANO;
393             break;
394         }
395         case VersionParseStep::STEP_NANO: {
396             auto ret = GetUint16ValueFromDecimal(input, versionNum[VERSION_NANO_INDEX]);
397             if (ret != SUCCESS) {
398                 HiLog::Error(LABEL, "read nano version failed, input: %{public}s, ERRNO: %{public}u.", input.c_str(),
399                              ret);
400                 return ret;
401             }
402             step = VersionParseStep::STEP_FINISHED;
403             break;
404         }
405         default: {
406             HiLog::Error(LABEL, "read redundant version data, input: %{public}s.", input.c_str());
407             return ERR_INVALID_PARAMETER;
408         }
409     }
410 
411     return SUCCESS;
412 }
413 
GetUint16ValueFromDecimal(const string & source,uint16_t & result)414 uint32_t Plugin::GetUint16ValueFromDecimal(const string &source, uint16_t &result)
415 {
416     if (source.empty() || source.size() > UINT16_MAX_DECIMAL_DIGITS) {
417         HiLog::Error(LABEL, "invalid string of uint16: %{public}s.", source.c_str());
418         return ERR_INVALID_PARAMETER;
419     }
420 
421     // determine if all characters are numbers.
422     for (auto &character : source) {
423         if (character < '0' || character > '9') {
424             HiLog::Error(LABEL, "character out of the range of digital: %{public}s.", source.c_str());
425             return ERR_INVALID_PARAMETER;
426         }
427     }
428 
429     unsigned long tmp = stoul(source);
430     if (tmp > UINT16_MAX_VALUE) {
431         HiLog::Error(LABEL, "result out of the range of uint16: %{public}s.", source.c_str());
432         return ERR_INVALID_PARAMETER;
433     }
434 
435     result = static_cast<uint16_t>(tmp);
436     return SUCCESS;
437 }
438 } // namespace MultimediaPlugin
439 } // namespace OHOS
440