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