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 "impl_class.h"
17 #include <algorithm>
18 #include "hilog/log.h"
19 #include "impl_class_key.h"
20 #include "json_helper.h"
21 #include "log_tags.h"
22 #include "plugin.h"
23 #include "plugin_class_base.h"
24 #include "plugin_common_type.h"
25 #include "plugin_export.h"
26
27 namespace OHOS {
28 namespace MultimediaPlugin {
29 using nlohmann::json;
30 using std::map;
31 using std::recursive_mutex;
32 using std::set;
33 using std::shared_ptr;
34 using std::size_t;
35 using std::string;
36 using std::weak_ptr;
37 using namespace OHOS::HiviewDFX;
38
39 static constexpr HiLogLabel LABEL = { LOG_CORE, LOG_TAG_DOMAIN_ID_PLUGIN, "ImplClass" };
40 string ImplClass::emptyString_;
41
ImplClass()42 ImplClass::ImplClass() : selfKey_(*this)
43 {}
44
Register(const weak_ptr<Plugin> & plugin,const json & classInfo)45 uint32_t ImplClass::Register(const weak_ptr<Plugin> &plugin, const json &classInfo)
46 {
47 if (state_ != ClassState::CLASS_STATE_UNREGISTER) {
48 // repeat registration
49 HiLog::Error(LABEL, "repeat registration.");
50 return ERR_INTERNAL;
51 }
52
53 if (JsonHelper::GetStringValue(classInfo, "className", className_) != SUCCESS) {
54 HiLog::Error(LABEL, "read className failed.");
55 return ERR_INVALID_PARAMETER;
56 }
57 HiLog::Debug(LABEL, "register class: %{public}s.", className_.c_str());
58
59 if (!AnalysisServices(classInfo)) {
60 HiLog::Error(LABEL, "failed to analysis services for class %{public}s.", className_.c_str());
61 return ERR_INVALID_PARAMETER;
62 }
63
64 uint32_t result = JsonHelper::GetUint16Value(classInfo, "priority", priority_);
65 if (result != SUCCESS) {
66 if (result != ERR_NO_TARGET) {
67 HiLog::Error(LABEL, "read priority failed, result: %{public}u.", result);
68 return ERR_INVALID_PARAMETER;
69 }
70 // priority is optional, and default zero.
71 priority_ = 0;
72 }
73 HiLog::Debug(LABEL, "get class priority: %{public}u.", priority_);
74
75 if (!AnalysisMaxInstance(classInfo)) {
76 HiLog::Error(LABEL, "failed to analysis maxInstance for class %{public}s.", className_.c_str());
77 return ERR_INVALID_PARAMETER;
78 }
79 HiLog::Debug(LABEL, "get class maxInstance: %{public}u.", maxInstance_);
80
81 if (JsonHelper::CheckElementExistence(classInfo, "capabilities") == SUCCESS) {
82 capability_.SetCapability(classInfo["capabilities"]);
83 }
84 pluginRef_ = plugin;
85 state_ = ClassState::CLASS_STATE_REGISTERED;
86 return SUCCESS;
87 }
88
CreateObject(uint32_t & errorCode)89 PluginClassBase *ImplClass::CreateObject(uint32_t &errorCode)
90 {
91 errorCode = ERR_INTERNAL;
92 if (state_ != ClassState::CLASS_STATE_REGISTERED) {
93 HiLog::Error(LABEL, "failed to create for unregistered, className: %{public}s.", className_.c_str());
94 return nullptr;
95 }
96
97 auto sharedPlugin = pluginRef_.lock();
98 if (sharedPlugin == nullptr) {
99 HiLog::Error(LABEL, "failed to dereference Plugin, className: %{public}s.", className_.c_str());
100 return nullptr;
101 }
102
103 HiLog::Debug(LABEL, "create object, className: %{public}s.", className_.c_str());
104
105 std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
106 if (maxInstance_ != INSTANCE_NO_LIMIT_NUM && instanceNum_ >= maxInstance_) {
107 HiLog::Error(LABEL, "failed to create for limit, currentNum: %{public}u, maxNum: %{public}u, \
108 className: %{public}s.",
109 instanceNum_, maxInstance_, className_.c_str());
110 guard.unlock();
111 errorCode = ERR_INSTANCE_LIMIT;
112 return nullptr;
113 }
114
115 if (instanceNum_ == 0) {
116 if (sharedPlugin->Ref() != SUCCESS) {
117 return nullptr;
118 }
119 }
120
121 PluginClassBase *object = DoCreateObject(sharedPlugin);
122 if (object == nullptr) {
123 HiLog::Error(LABEL, "create object result null, className: %{public}s.", className_.c_str());
124 goto CREATE_INTERNAL_ERROR_EXIT;
125 }
126
127 ++instanceNum_;
128 HiLog::Debug(LABEL, "create object success, InstanceNum: %{public}u.", instanceNum_);
129 guard.unlock();
130
131 errorCode = SUCCESS;
132 return object;
133
134 CREATE_INTERNAL_ERROR_EXIT:
135 if (instanceNum_ == 0) {
136 sharedPlugin->DeRef();
137 }
138 return nullptr;
139 }
140
GetPluginRef() const141 weak_ptr<Plugin> ImplClass::GetPluginRef() const
142 {
143 if (state_ != ClassState::CLASS_STATE_REGISTERED) {
144 return weak_ptr<Plugin>();
145 }
146
147 return pluginRef_;
148 }
149
GetClassName() const150 const string &ImplClass::GetClassName() const
151 {
152 return className_;
153 }
154
GetPackageName() const155 const string &ImplClass::GetPackageName() const
156 {
157 if (state_ != ClassState::CLASS_STATE_REGISTERED) {
158 HiLog::Error(LABEL, "get package name, className: %{public}s, state error: %{public}d.", className_.c_str(),
159 state_);
160 return emptyString_;
161 }
162
163 auto sharedPlugin = pluginRef_.lock();
164 if (sharedPlugin == nullptr) {
165 HiLog::Error(LABEL, "get package name, failed to dereference Plugin, className: %{public}s.",
166 className_.c_str());
167 return emptyString_;
168 }
169
170 return sharedPlugin->GetPackageName();
171 }
172
IsSupport(uint16_t interfaceID) const173 bool ImplClass::IsSupport(uint16_t interfaceID) const
174 {
175 HiLog::Debug(LABEL, "search for support iid: %{public}u, className: %{public}s.", interfaceID, className_.c_str());
176 for (uint32_t serviceFlag : services_) {
177 if (MakeIID(serviceFlag) == interfaceID) {
178 return true;
179 }
180 }
181
182 HiLog::Debug(LABEL, "there is no matching interfaceID");
183 return false;
184 }
185
OnObjectDestroy()186 void ImplClass::OnObjectDestroy()
187 {
188 // this situation does not happen in design.
189 // the process context can guarantee that this will not happen.
190 // the judgment statement here is for protection and positioning purposes only.
191 if (state_ != ClassState::CLASS_STATE_REGISTERED) {
192 HiLog::Error(LABEL, "failed to destroy object because class unregistered, className: %{public}s.",
193 className_.c_str());
194 return;
195 }
196
197 std::unique_lock<std::recursive_mutex> guard(dynDataLock_);
198 // this situation does not happen in design.
199 if (instanceNum_ == 0) {
200 guard.unlock();
201 HiLog::Error(LABEL, "destroy object while instanceNum is zero.");
202 return;
203 }
204
205 --instanceNum_;
206
207 auto sharedPlugin = pluginRef_.lock();
208 // this situation does not happen in design.
209 if (sharedPlugin == nullptr) {
210 guard.unlock();
211 HiLog::Error(LABEL, "destroy object failed because failed to dereference Plugin, className: %{public}s.",
212 className_.c_str());
213 return;
214 }
215
216 HiLog::Debug(LABEL, "destroy object: className: %{public}s", className_.c_str());
217 if (instanceNum_ == 0) {
218 sharedPlugin->DeRef();
219 }
220
221 HiLog::Debug(LABEL, "destroy object success, InstanceNum: %{public}u.", instanceNum_);
222 }
223
GetServices() const224 const set<uint32_t> &ImplClass::GetServices() const
225 {
226 return services_;
227 }
228
IsCompatible(const map<string,AttrData> & caps) const229 bool ImplClass::IsCompatible(const map<string, AttrData> &caps) const
230 {
231 return capability_.IsCompatible(caps);
232 }
233
GetCapability(const string & key) const234 const AttrData *ImplClass::GetCapability(const string &key) const
235 {
236 if (state_ != ClassState::CLASS_STATE_REGISTERED) {
237 return nullptr;
238 }
239
240 return capability_.GetCapability(key);
241 }
242
GetCapability() const243 const std::map<std::string, AttrData> &ImplClass::GetCapability() const
244 {
245 return capability_.GetCapability();
246 }
247
248 // ------------------------------- private method -------------------------------
AnalysisServices(const json & classInfo)249 bool ImplClass::AnalysisServices(const json &classInfo)
250 {
251 size_t serviceNum;
252 if (JsonHelper::GetArraySize(classInfo, "services", serviceNum) != SUCCESS) {
253 HiLog::Error(LABEL, "read array size of services failed.");
254 return false;
255 }
256 HiLog::Debug(LABEL, "class service num: %{public}zu.", serviceNum);
257
258 uint16_t interfaceID;
259 #ifndef PLUGIN_FLAG_RTTI_ENABLE
260 uint32_t lastInterfaceID = UINT32_MAX_VALUE;
261 #endif
262 uint16_t serviceType;
263 bool serviceAdded = false;
264 const json &servicesInfo = classInfo["services"];
265 for (size_t i = 0; i < serviceNum; i++) {
266 const json &serviceInfo = servicesInfo[i];
267 if (JsonHelper::GetUint16Value(serviceInfo, "interfaceID", interfaceID) != SUCCESS) {
268 HiLog::Error(LABEL, "read interfaceID failed at %{public}zu.", i);
269 #ifndef PLUGIN_FLAG_RTTI_ENABLE
270 // when -frtti is not enable, to ensure correct base class side-to-side conversion, we require that
271 // the plugin class inherit only one service interface class and the PluginClassBase class,
272 // while the location of the service interface class is in front of the PluginClassBase.
273 // below, we check only one business interface class is allowed to inherit.
274 HiLog::Error(LABEL, "no valid service info or encounter the risk of more than one business \
275 interface base class.");
276 return false;
277 #else
278 continue;
279 #endif
280 }
281
282 #ifndef PLUGIN_FLAG_RTTI_ENABLE
283 // check only one business interface class is allowed to inherit.
284 if (lastInterfaceID != UINT32_MAX_VALUE && lastInterfaceID != interfaceID) {
285 HiLog::Error(LABEL, "more than one business interface base class.");
286 return false;
287 }
288 lastInterfaceID = interfaceID;
289 #endif
290 uint32_t result = JsonHelper::GetUint16Value(serviceInfo, "serviceType", serviceType);
291 if (result != SUCCESS) {
292 if (result != ERR_NO_TARGET) {
293 HiLog::Error(LABEL, "read serviceType failed at %{public}zu.", i);
294 continue;
295 }
296 // serviceType is optional, and default zero.
297 serviceType = 0;
298 }
299
300 HiLog::Debug(LABEL, "insert service iid: %{public}hu, serviceType: %{public}hu.", interfaceID, serviceType);
301 services_.insert(MakeServiceFlag(interfaceID, serviceType));
302 serviceAdded = true;
303 }
304
305 return serviceAdded;
306 }
307
AnalysisMaxInstance(const json & classInfo)308 bool ImplClass::AnalysisMaxInstance(const json &classInfo)
309 {
310 uint32_t result = JsonHelper::GetUint16Value(classInfo, "maxInstance", maxInstance_);
311 if (result == SUCCESS) {
312 HiLog::Debug(LABEL, "class maxInstance num: %{public}u.", maxInstance_);
313 if (maxInstance_ == 0) {
314 HiLog::Error(LABEL, "class maxInstance num is invalid zero.");
315 return false;
316 }
317 return true;
318 }
319
320 if (result != ERR_NO_TARGET) {
321 HiLog::Error(LABEL, "read maxInstance failed.");
322 return false;
323 }
324
325 // maxInstance is optional, and value for this case is not limited.
326 maxInstance_ = INSTANCE_NO_LIMIT_NUM;
327 return true;
328 }
329
CfiFactory(PluginCreateFunc factory,const string & className)330 PluginClassBase *CfiFactory(PluginCreateFunc factory, const string &className) __attribute__((no_sanitize("cfi")))
331 {
332 return factory(className);
333 }
334
DoCreateObject(shared_ptr<Plugin> & plugin)335 PluginClassBase *ImplClass::DoCreateObject(shared_ptr<Plugin> &plugin)
336 {
337 // since the plugin library may be unloaded and reloaded, the pointer cannot guarantee a constant value,
338 // so it is reread every time here.
339 PluginCreateFunc factory = plugin->GetCreateFunc();
340 if (factory == nullptr) {
341 HiLog::Error(LABEL, "failed to get create func, className: %{public}s.", className_.c_str());
342 return nullptr;
343 }
344
345 PluginClassBase *pluginBaseObj = CfiFactory(factory, className_);
346 if (pluginBaseObj == nullptr) {
347 HiLog::Error(LABEL, "create object result null, className: %{public}s.", className_.c_str());
348 return nullptr;
349 }
350
351 #ifndef PLUGIN_FLAG_RTTI_ENABLE
352 // when -frtti is not enable, to ensure correct base class side-to-side conversion,
353 // we require that the plugin class inherit only one service interface class and the PluginClassBase class,
354 // while the location of the service interface class is in front of the PluginClassBase.
355 // below, we check the inherited position constraint.
356 void *obj = dynamic_cast<void *>(pluginBaseObj); // adjust pointer position when multiple inheritance.
357 if (obj == pluginBaseObj) {
358 // PluginClassBase is the first base class, not allowed.
359 HiLog::Error(LABEL, "service interface class is not the first base class. className: %{public}s.",
360 className_.c_str());
361 delete pluginBaseObj;
362 return nullptr;
363 }
364 #endif
365
366 if (pluginBaseObj->SetImplClassKey(selfKey_) != PluginClassBase::MAGIC_CODE) {
367 HiLog::Error(LABEL, "failed to set key, className: %{public}s.", className_.c_str());
368 delete pluginBaseObj;
369 return nullptr;
370 }
371
372 return pluginBaseObj;
373 }
374 } // namespace MultimediaPlugin
375 } // namespace OHOS
376