1 /*
2 * Copyright (c) 2022 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 #include <string>
16 #include <set>
17
18 #include "hilog_wrapper.h"
19 #include "js_context_utils.h"
20 #include "js_data_struct_converter.h"
21 #include "js_error_utils.h"
22 #include "js_runtime.h"
23 #include "js_runtime_utils.h"
24 #include "napi_common_want.h"
25 #include "napi_remote_object.h"
26 #include "ability_runtime/js_caller_complex.h"
27
28 namespace OHOS {
29 namespace AbilityRuntime {
30 namespace { // nameless
31 static std::map<NativeValueType, std::string> logcast = {
32 { NATIVE_UNDEFINED, std::string("NATIVE_UNDEFINED") },
33 { NATIVE_NULL, std::string("NATIVE_NULL") },
34 { NATIVE_BOOLEAN, std::string("NATIVE_BOOLEAN") },
35 { NATIVE_NUMBER, std::string("NATIVE_NUMBER") },
36 { NATIVE_STRING, std::string("NATIVE_STRING") },
37 { NATIVE_SYMBOL, std::string("NATIVE_SYMBOL") },
38 { NATIVE_OBJECT, std::string("NATIVE_OBJECT") },
39 { NATIVE_FUNCTION, std::string("NATIVE_FUNCTION") },
40 { NATIVE_EXTERNAL, std::string("NATIVE_EXTERNAL") },
41 { NATIVE_BIGINT, std::string("NATIVE_BIGINT") },
42 };
43
44 class JsCallerComplex {
45 public:
46 enum class OBJSTATE {
47 OBJ_NORMAL,
48 OBJ_EXECUTION,
49 OBJ_RELEASE
50 };
51
JsCallerComplex(NativeEngine & engine,ReleaseCallFunc releaseCallFunc,sptr<IRemoteObject> callee,std::shared_ptr<CallerCallBack> callerCallBack)52 explicit JsCallerComplex(
53 NativeEngine& engine, ReleaseCallFunc releaseCallFunc, sptr<IRemoteObject> callee,
54 std::shared_ptr<CallerCallBack> callerCallBack) : releaseCallFunc_(releaseCallFunc),
55 callee_(callee), releaseCallBackEngine_(engine),
56 callerCallBackObj_(callerCallBack), jsReleaseCallBackObj_(nullptr)
57 {
58 AddJsCallerComplex(this);
59 handler_ = std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::GetMainEventRunner());
60 currentState_ = OBJSTATE::OBJ_NORMAL;
61 };
~JsCallerComplex()62 virtual~JsCallerComplex()
63 {
64 RemoveJsCallerComplex(this);
65 };
66
ReleaseObject(JsCallerComplex * data)67 static bool ReleaseObject(JsCallerComplex* data)
68 {
69 HILOG_DEBUG("ReleaseObject begin %{public}p", data);
70 if (data == nullptr) {
71 HILOG_ERROR("ReleaseObject begin, but input parameters is nullptr");
72 return false;
73 }
74
75 if (!data->ChangeCurrentState(OBJSTATE::OBJ_RELEASE)) {
76 auto handler = data->GetEventHandler();
77 if (handler == nullptr) {
78 HILOG_ERROR("ReleaseObject error end, Get eventHandler failed");
79 return false;
80 }
81 auto releaseObjTask = [pdata = data] () {
82 if (!FindJsCallerComplex(pdata)) {
83 HILOG_ERROR("ReleaseObject error end, but input parameters does not found");
84 return;
85 }
86 ReleaseObject(pdata);
87 };
88
89 handler->PostTask(releaseObjTask, "FinalizerRelease");
90 return false;
91 } else {
92 // when the object is about to be destroyed, does not reset state
93 std::unique_ptr<JsCallerComplex> delObj(data);
94 }
95 HILOG_DEBUG("ReleaseObject success end");
96 return true;
97 }
98
Finalizer(NativeEngine * engine,void * data,void * hint)99 static void Finalizer(NativeEngine* engine, void* data, void* hint)
100 {
101 HILOG_DEBUG("JsCallerComplex::%{public}s begin.", __func__);
102 if (data == nullptr) {
103 HILOG_ERROR("JsCallerComplex::%{public}s is called, but input parameters is nullptr", __func__);
104 return;
105 }
106
107 auto ptr = static_cast<JsCallerComplex*>(data);
108 if (!FindJsCallerComplex(ptr)) {
109 HILOG_ERROR("JsCallerComplex::%{public}s is called, but input parameters does not found", __func__);
110 return;
111 }
112
113 ReleaseObject(ptr);
114 HILOG_DEBUG("JsCallerComplex::%{public}s end.", __func__);
115 }
116
JsReleaseCall(NativeEngine * engine,NativeCallbackInfo * info)117 static NativeValue* JsReleaseCall(NativeEngine* engine, NativeCallbackInfo* info)
118 {
119 if (engine == nullptr || info == nullptr) {
120 HILOG_ERROR("JsCallerComplex::%{public}s is called, but input parameters %{public}s is nullptr",
121 __func__,
122 ((engine == nullptr) ? "engine" : "info"));
123 return nullptr;
124 }
125
126 auto object = CheckParamsAndGetThis<JsCallerComplex>(engine, info);
127 if (object == nullptr) {
128 HILOG_ERROR("JsCallerComplex::%{public}s is called, CheckParamsAndGetThis return nullptr", __func__);
129 return nullptr;
130 }
131
132 return object->ReleaseCallInner(*engine, *info);
133 }
134
JsSetOnReleaseCallBack(NativeEngine * engine,NativeCallbackInfo * info)135 static NativeValue* JsSetOnReleaseCallBack(NativeEngine* engine, NativeCallbackInfo* info)
136 {
137 if (engine == nullptr || info == nullptr) {
138 HILOG_ERROR("JsCallerComplex::%{public}s is called, but input parameters %{public}s is nullptr",
139 __func__,
140 ((engine == nullptr) ? "engine" : "info"));
141 return nullptr;
142 }
143
144 auto object = CheckParamsAndGetThis<JsCallerComplex>(engine, info);
145 if (object == nullptr) {
146 HILOG_ERROR("JsCallerComplex::%{public}s is called, CheckParamsAndGetThis return nullptr", __func__);
147 return nullptr;
148 }
149
150 return object->SetOnReleaseCallBackInner(*engine, *info);
151 }
152
AddJsCallerComplex(JsCallerComplex * ptr)153 static bool AddJsCallerComplex(JsCallerComplex* ptr)
154 {
155 if (ptr == nullptr) {
156 HILOG_ERROR("JsAbilityContext::%{public}s, input parameters is nullptr", __func__);
157 return false;
158 }
159
160 std::lock_guard<std::mutex> lck (jsCallerComplexMutex);
161 auto iter = jsCallerComplexManagerList.find(ptr);
162 if (iter != jsCallerComplexManagerList.end()) {
163 HILOG_ERROR("JsAbilityContext::%{public}s, address exists", __func__);
164 return false;
165 }
166
167 auto iterRet = jsCallerComplexManagerList.emplace(ptr);
168 HILOG_DEBUG("JsAbilityContext::%{public}s, execution ends and retval is %{public}s",
169 __func__, iterRet.second ? "true" : "false");
170 return iterRet.second;
171 }
172
RemoveJsCallerComplex(JsCallerComplex * ptr)173 static bool RemoveJsCallerComplex(JsCallerComplex* ptr)
174 {
175 if (ptr == nullptr) {
176 HILOG_ERROR("JsAbilityContext::%{public}s, input parameters is nullptr", __func__);
177 return false;
178 }
179
180 std::lock_guard<std::mutex> lck (jsCallerComplexMutex);
181 auto iter = jsCallerComplexManagerList.find(ptr);
182 if (iter == jsCallerComplexManagerList.end()) {
183 HILOG_ERROR("JsAbilityContext::%{public}s, input parameters not found", __func__);
184 return false;
185 }
186
187 jsCallerComplexManagerList.erase(ptr);
188 HILOG_DEBUG("JsAbilityContext::%{public}s, called", __func__);
189 return true;
190 }
191
FindJsCallerComplex(JsCallerComplex * ptr)192 static bool FindJsCallerComplex(JsCallerComplex* ptr)
193 {
194 if (ptr == nullptr) {
195 HILOG_ERROR("JsAbilityContext::%{public}s, input parameters is nullptr", __func__);
196 return false;
197 }
198 auto ret = true;
199 std::lock_guard<std::mutex> lck (jsCallerComplexMutex);
200 auto iter = jsCallerComplexManagerList.find(ptr);
201 if (iter == jsCallerComplexManagerList.end()) {
202 ret = false;
203 }
204 HILOG_DEBUG("JsAbilityContext::%{public}s, execution ends and retval is %{public}s",
205 __func__, ret ? "true" : "false");
206 return ret;
207 }
208
FindJsCallerComplexAndChangeState(JsCallerComplex * ptr,OBJSTATE state)209 static bool FindJsCallerComplexAndChangeState(JsCallerComplex* ptr, OBJSTATE state)
210 {
211 if (ptr == nullptr) {
212 HILOG_ERROR("JsAbilityContext::%{public}s, input parameters is nullptr", __func__);
213 return false;
214 }
215
216 std::lock_guard<std::mutex> lck (jsCallerComplexMutex);
217 auto iter = jsCallerComplexManagerList.find(ptr);
218 if (iter == jsCallerComplexManagerList.end()) {
219 HILOG_ERROR("JsAbilityContext::%{public}s, execution end, but not found", __func__);
220 return false;
221 }
222
223 auto ret = ptr->ChangeCurrentState(state);
224 HILOG_DEBUG("JsAbilityContext::%{public}s, execution ends and ChangeCurrentState retval is %{public}s",
225 __func__, ret ? "true" : "false");
226
227 return ret;
228 }
229
GetRemoteObject()230 sptr<IRemoteObject> GetRemoteObject()
231 {
232 return callee_;
233 }
234
GetEventHandler()235 std::shared_ptr<AppExecFwk::EventHandler> GetEventHandler()
236 {
237 return handler_;
238 }
239
ChangeCurrentState(OBJSTATE state)240 bool ChangeCurrentState(OBJSTATE state)
241 {
242 auto ret = false;
243 if (stateMechanismMutex_.try_lock() == false) {
244 HILOG_ERROR("mutex try_lock false");
245 return ret;
246 }
247
248 if (currentState_ == OBJSTATE::OBJ_NORMAL) {
249 currentState_ = state;
250 ret = true;
251 HILOG_DEBUG("currentState_ == OBJSTATE::OBJ_NORMAL");
252 } else if (currentState_ == state) {
253 ret = true;
254 HILOG_DEBUG("currentState_ == state");
255 } else {
256 ret = false;
257 HILOG_DEBUG("ret = false");
258 }
259
260 stateMechanismMutex_.unlock();
261 return ret;
262 }
263
GetCurrentState()264 OBJSTATE GetCurrentState()
265 {
266 return currentState_;
267 }
268
StateReset()269 void StateReset()
270 {
271 currentState_ = OBJSTATE::OBJ_NORMAL;
272 }
273
274 private:
275
OnReleaseNotify(const std::string & str)276 void OnReleaseNotify(const std::string &str)
277 {
278 HILOG_DEBUG("OnReleaseNotify begin");
279 if (handler_ == nullptr) {
280 HILOG_ERROR("handler parameters error");
281 return;
282 }
283
284 auto task = [notify = this, &str] () {
285 if (!FindJsCallerComplex(notify)) {
286 HILOG_ERROR("ptr not found, address error");
287 return;
288 }
289 notify->OnReleaseNotifyTask(str);
290 };
291 handler_->PostSyncTask(task, "OnReleaseNotify");
292 HILOG_DEBUG("OnReleaseNotify end");
293 }
294
OnReleaseNotifyTask(const std::string & str)295 void OnReleaseNotifyTask(const std::string &str)
296 {
297 HILOG_DEBUG("OnReleaseNotifyTask begin");
298 if (jsReleaseCallBackObj_ == nullptr) {
299 HILOG_ERROR("JsCallerComplex::%{public}s, jsreleaseObj is nullptr", __func__);
300 return;
301 }
302
303 NativeValue* value = jsReleaseCallBackObj_->Get();
304 NativeValue* callback = jsReleaseCallBackObj_->Get();
305 NativeValue* args[] = { CreateJsValue(releaseCallBackEngine_, str) };
306 releaseCallBackEngine_.CallFunction(value, callback, args, 1);
307 HILOG_DEBUG("OnReleaseNotifyTask CallFunction call done");
308 callee_ = nullptr;
309 StateReset();
310 HILOG_DEBUG("OnReleaseNotifyTask end");
311 }
312
ReleaseCallInner(NativeEngine & engine,NativeCallbackInfo & info)313 NativeValue* ReleaseCallInner(NativeEngine& engine, NativeCallbackInfo& info)
314 {
315 HILOG_DEBUG("JsCallerComplex::%{public}s, called", __func__);
316 if (callerCallBackObj_ == nullptr) {
317 HILOG_ERROR("JsCallerComplex::%{public}s, CallBacker is nullptr", __func__);
318 ThrowError(engine, AbilityErrorCode::ERROR_CODE_INNER);
319 }
320
321 if (!releaseCallFunc_) {
322 HILOG_ERROR("JsCallerComplex::%{public}s, releaseFunc is nullptr", __func__);
323 ThrowError(engine, AbilityErrorCode::ERROR_CODE_INNER);
324 }
325 int32_t innerErrorCode = releaseCallFunc_(callerCallBackObj_);
326 if (innerErrorCode != ERR_OK) {
327 HILOG_ERROR("JsCallerComplex::%{public}s, ReleaseAbility failed %{public}d",
328 __func__, static_cast<int>(innerErrorCode));
329 ThrowError(engine, innerErrorCode);
330 }
331
332 return engine.CreateUndefined();
333 }
334
SetOnReleaseCallBackInner(NativeEngine & engine,NativeCallbackInfo & info)335 NativeValue* SetOnReleaseCallBackInner(NativeEngine& engine, NativeCallbackInfo& info)
336 {
337 HILOG_DEBUG("JsCallerComplex::%{public}s, begin", __func__);
338 constexpr size_t argcOne = 1;
339 if (info.argc < argcOne) {
340 HILOG_ERROR("JsCallerComplex::%{public}s, Invalid input params", __func__);
341 ThrowTooFewParametersError(engine);
342 }
343 if (!info.argv[0]->IsCallable()) {
344 HILOG_ERROR("JsCallerComplex::%{public}s, IsCallable is %{public}s.",
345 __func__, ((info.argv[0]->IsCallable()) ? "true" : "false"));
346 ThrowError(engine, AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
347 }
348
349 if (callerCallBackObj_ == nullptr) {
350 HILOG_ERROR("JsCallerComplex::%{public}s, CallBacker is nullptr", __func__);
351 ThrowError(engine, AbilityErrorCode::ERROR_CODE_INNER);
352 }
353
354 auto param1 = info.argv[0];
355 if (param1 == nullptr) {
356 HILOG_ERROR("JsCallerComplex::%{public}s, param1 is nullptr", __func__);
357 ThrowError(engine, AbilityErrorCode::ERROR_CODE_INNER);
358 }
359
360 jsReleaseCallBackObj_.reset(releaseCallBackEngine_.CreateReference(param1, 1));
361 auto task = [notify = this] (const std::string &str) {
362 if (!FindJsCallerComplexAndChangeState(notify, OBJSTATE::OBJ_EXECUTION)) {
363 HILOG_ERROR("ptr not found, address error");
364 return;
365 }
366 notify->OnReleaseNotify(str);
367 };
368 callerCallBackObj_->SetOnRelease(task);
369 HILOG_DEBUG("JsCallerComplex::%{public}s, end", __func__);
370 return engine.CreateUndefined();
371 }
372
373 private:
374 ReleaseCallFunc releaseCallFunc_;
375 sptr<IRemoteObject> callee_;
376 NativeEngine& releaseCallBackEngine_;
377 std::shared_ptr<CallerCallBack> callerCallBackObj_;
378 std::unique_ptr<NativeReference> jsReleaseCallBackObj_;
379 std::shared_ptr<AppExecFwk::EventHandler> handler_;
380 std::mutex stateMechanismMutex_;
381 OBJSTATE currentState_;
382
383 static std::set<JsCallerComplex*> jsCallerComplexManagerList;
384 static std::mutex jsCallerComplexMutex;
385 };
386
387 std::set<JsCallerComplex*> JsCallerComplex::jsCallerComplexManagerList;
388 std::mutex JsCallerComplex::jsCallerComplexMutex;
389 } // nameless
390
CreateJsCallerComplex(NativeEngine & engine,ReleaseCallFunc releaseCallFunc,sptr<IRemoteObject> callee,std::shared_ptr<CallerCallBack> callerCallBack)391 NativeValue* CreateJsCallerComplex(
392 NativeEngine& engine, ReleaseCallFunc releaseCallFunc, sptr<IRemoteObject> callee,
393 std::shared_ptr<CallerCallBack> callerCallBack)
394 {
395 HILOG_DEBUG("JsCallerComplex::%{public}s, begin", __func__);
396 if (callee == nullptr || callerCallBack == nullptr || releaseCallFunc == nullptr) {
397 HILOG_ERROR("%{public}s is called, input params error. %{public}s is nullptr", __func__,
398 (callee == nullptr) ? ("callee") :
399 ((releaseCallFunc == nullptr) ? ("releaseCallFunc") : ("callerCallBack")));
400 return engine.CreateUndefined();
401 }
402
403 NativeValue* objValue = engine.CreateObject();
404 NativeObject* object = ConvertNativeValueTo<NativeObject>(objValue);
405
406 auto jsCaller = std::make_unique<JsCallerComplex>(engine, releaseCallFunc, callee, callerCallBack);
407 auto remoteObj = jsCaller->GetRemoteObject();
408 if (remoteObj == nullptr) {
409 HILOG_ERROR("%{public}s is called,remoteObj is nullptr", __func__);
410 return engine.CreateUndefined();
411 }
412
413 object->SetNativePointer(jsCaller.release(), JsCallerComplex::Finalizer, nullptr);
414 object->SetProperty("callee", CreateJsCalleeRemoteObject(engine, remoteObj));
415 const char *moduleName = "JsCallerComplex";
416 BindNativeFunction(engine, *object, "release", moduleName, JsCallerComplex::JsReleaseCall);
417 BindNativeFunction(engine, *object, "onRelease", moduleName, JsCallerComplex::JsSetOnReleaseCallBack);
418
419 HILOG_DEBUG("JsCallerComplex::%{public}s, end", __func__);
420 return objValue;
421 }
422
CreateJsCalleeRemoteObject(NativeEngine & engine,sptr<IRemoteObject> callee)423 NativeValue* CreateJsCalleeRemoteObject(NativeEngine& engine, sptr<IRemoteObject> callee)
424 {
425 if (callee == nullptr) {
426 HILOG_ERROR("%{public}s is called, input params is nullptr", __func__);
427 return engine.CreateUndefined();
428 }
429 napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(
430 reinterpret_cast<napi_env>(&engine), callee);
431 NativeValue* nativeRemoteObject = reinterpret_cast<NativeValue*>(napiRemoteObject);
432
433 if (nativeRemoteObject == nullptr) {
434 HILOG_ERROR("%{public}s is called, but remoteObj is nullptr", __func__);
435 }
436
437 return nativeRemoteObject;
438 }
439 } // AbilityRuntime
440 } // OHOS
441