1 /*
2 * Copyright (c) 2023 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 "js_driver_extension.h"
17
18 #include "ability_info.h"
19 #include "hitrace_meter.h"
20 #include "hilog_wrapper.h"
21 #include "js_extension_common.h"
22 #include "js_extension_context.h"
23 #include "js_runtime.h"
24 #include "js_runtime_utils.h"
25 #include "js_driver_extension_context.h"
26 #include "napi/native_api.h"
27 #include "napi/native_node_api.h"
28 #include "napi_common_configuration.h"
29 #include "napi_common_want.h"
30 #include "napi_remote_object.h"
31
32 namespace OHOS {
33 namespace AbilityRuntime {
34 namespace {
35 constexpr size_t ARGC_ONE = 1;
36 }
37
38 namespace {
PromiseCallback(napi_env env,napi_callback_info info)39 napi_value PromiseCallback(napi_env env, napi_callback_info info)
40 {
41 if (info == nullptr) {
42 HILOG_ERROR("PromiseCallback, Invalid input info.");
43 return nullptr;
44 }
45 void *data = nullptr;
46 napi_get_cb_info(env, info, nullptr, nullptr, nullptr, &data);
47 if (data == nullptr) {
48 HILOG_ERROR("PromiseCallback, Invalid input data info.");
49 return nullptr;
50 }
51
52 auto *callbackInfo = static_cast<AppExecFwk::AbilityTransactionCallbackInfo<> *>(data);
53 callbackInfo->Call();
54 AppExecFwk::AbilityTransactionCallbackInfo<>::Destroy(callbackInfo);
55 data = nullptr;
56
57 return nullptr;
58 }
59
OnConnectPromiseCallback(napi_env env,napi_callback_info info)60 napi_value OnConnectPromiseCallback(napi_env env, napi_callback_info info)
61 {
62 if (info == nullptr) {
63 HILOG_ERROR("PromiseCallback, Invalid input info.");
64 return nullptr;
65 }
66 size_t argc = 1;
67 napi_value argv[1] = {nullptr};
68 void *data = nullptr;
69 napi_get_cb_info(env, info, &argc, argv, nullptr, &data);
70 if (data == nullptr) {
71 HILOG_ERROR("PromiseCallback, Invalid input data info.");
72 return nullptr;
73 }
74
75 auto *callbackInfo = static_cast<AppExecFwk::AbilityTransactionCallbackInfo<sptr<IRemoteObject>> *>(data);
76 sptr<IRemoteObject> service = nullptr;
77 if (argc > 0) {
78 service = NAPI_ohos_rpc_getNativeRemoteObject(env, argv[0]);
79 }
80 callbackInfo->Call(service);
81 AppExecFwk::AbilityTransactionCallbackInfo<sptr<IRemoteObject>>::Destroy(callbackInfo);
82 data = nullptr;
83
84 return nullptr;
85 }
86 }
87
88 using namespace OHOS::AppExecFwk;
89
AttachDriverExtensionContext(napi_env env,void * value,void *)90 napi_value AttachDriverExtensionContext(napi_env env, void *value, void *)
91 {
92 HILOG_INFO("AttachDriverExtensionContext");
93 if (value == nullptr) {
94 HILOG_WARN("invalid parameter.");
95 return nullptr;
96 }
97 auto ptr = reinterpret_cast<std::weak_ptr<DriverExtensionContext> *>(value)->lock();
98 if (ptr == nullptr) {
99 HILOG_WARN("invalid context.");
100 return nullptr;
101 }
102 napi_value object = CreateJsDriverExtensionContext(env, ptr);
103 auto contextObjRef = JsRuntime::LoadSystemModuleByEngine(env,
104 "application.DriverExtensionContext", &object, 1);
105 napi_value contextObj = contextObjRef->GetNapiValue();
106
107 napi_coerce_to_native_binding_object(env, contextObj, DetachCallbackFunc, AttachDriverExtensionContext,
108 value, nullptr);
109
110 auto workContext = new (std::nothrow) std::weak_ptr<DriverExtensionContext>(ptr);
111 napi_status status = napi_wrap(env, contextObj, workContext,
112 [](napi_env env, void *data, void *) {
113 HILOG_INFO("Finalizer for weak_ptr driver extension context is called");
114 delete static_cast<std::weak_ptr<DriverExtensionContext> *>(data);
115 }, nullptr, nullptr);
116 if (status != napi_ok) {
117 HILOG_ERROR("Failed to wrap js instance with native object");
118 delete workContext;
119 return nullptr;
120 }
121 return contextObj;
122 }
123
Create(const std::unique_ptr<Runtime> & runtime)124 JsDriverExtension* JsDriverExtension::Create(const std::unique_ptr<Runtime>& runtime)
125 {
126 return new JsDriverExtension(static_cast<JsRuntime&>(*runtime));
127 }
128
JsDriverExtension(JsRuntime & jsRuntime)129 JsDriverExtension::JsDriverExtension(JsRuntime& jsRuntime) : jsRuntime_(jsRuntime) {}
130 JsDriverExtension::~JsDriverExtension() = default;
131
Init(const std::shared_ptr<AbilityLocalRecord> & record,const std::shared_ptr<OHOSApplication> & application,std::shared_ptr<AbilityHandler> & handler,const sptr<IRemoteObject> & token)132 void JsDriverExtension::Init(const std::shared_ptr<AbilityLocalRecord> &record,
133 const std::shared_ptr<OHOSApplication> &application, std::shared_ptr<AbilityHandler> &handler,
134 const sptr<IRemoteObject> &token)
135 {
136 DriverExtension::Init(record, application, handler, token);
137 std::string srcPath = "";
138 GetSrcPath(srcPath);
139 if (srcPath.empty()) {
140 HILOG_ERROR("Failed to get srcPath");
141 return;
142 }
143
144 std::string moduleName(Extension::abilityInfo_->moduleName);
145 moduleName.append("::").append(abilityInfo_->name);
146 HILOG_DEBUG("JsStaticSubscriberExtension::Init moduleName:%{public}s,srcPath:%{public}s.",
147 moduleName.c_str(), srcPath.c_str());
148 HandleScope handleScope(jsRuntime_);
149 auto env = jsRuntime_.GetNapiEnv();
150
151 jsObj_ = jsRuntime_.LoadModule(
152 moduleName, srcPath, abilityInfo_->hapPath, abilityInfo_->compileMode == CompileMode::ES_MODULE);
153 if (jsObj_ == nullptr) {
154 HILOG_ERROR("Failed to get jsObj_");
155 return;
156 }
157 HILOG_INFO("JsDriverExtension::Init ConvertNativeValueTo.");
158
159 napi_value obj = jsObj_->GetNapiValue();
160 BindContext(env, obj);
161 SetExtensionCommon(JsExtensionCommon::Create(jsRuntime_, static_cast<NativeReference&>(*jsObj_), shellContextRef_));
162 }
163
BindContext(napi_env env,napi_value obj)164 void JsDriverExtension::BindContext(napi_env env, napi_value obj)
165 {
166 auto context = GetContext();
167 if (context == nullptr) {
168 HILOG_ERROR("Failed to get context");
169 return;
170 }
171 HILOG_INFO("JsDriverExtension::Init CreateJsDriverExtensionContext.");
172 napi_value contextObj = CreateJsDriverExtensionContext(env, context);
173 shellContextRef_ = JsRuntime::LoadSystemModuleByEngine(env, "application.DriverExtensionContext",
174 &contextObj, ARGC_ONE);
175
176 napi_value nativeObj = shellContextRef_->GetNapiValue();
177
178 auto workContext = new (std::nothrow) std::weak_ptr<DriverExtensionContext>(context);
179 napi_coerce_to_native_binding_object(env, nativeObj, DetachCallbackFunc, AttachDriverExtensionContext,
180 workContext, nullptr);
181
182 HILOG_INFO("JsDriverExtension::Init Bind.");
183 context->Bind(jsRuntime_, shellContextRef_.get());
184 HILOG_INFO("JsDriverExtension::SetProperty.");
185 napi_set_named_property(env, obj, "context", contextObj);
186 HILOG_INFO("Set driver extension context");
187 napi_status status = napi_wrap(env, nativeObj, workContext,
188 [](napi_env, void* data, void*) {
189 HILOG_INFO("Finalizer for weak_ptr driver extension context is called");
190 delete static_cast<std::weak_ptr<DriverExtensionContext>*>(data);
191 }, nullptr, nullptr);
192 if (status != napi_ok) {
193 HILOG_ERROR("Failed to wrap js instance with native object");
194 delete workContext;
195 }
196 HILOG_INFO("JsDriverExtension::Init end.");
197 }
198
OnStart(const AAFwk::Want & want)199 void JsDriverExtension::OnStart(const AAFwk::Want &want)
200 {
201 Extension::OnStart(want);
202 HILOG_INFO("JsDriverExtension OnStart begin..");
203 HandleScope handleScope(jsRuntime_);
204 napi_env env = jsRuntime_.GetNapiEnv();
205 napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
206 napi_value argv[] = {napiWant};
207 CallObjectMethod(env, "onInit", argv, ARGC_ONE);
208 HILOG_INFO("%{public}s end.", __func__);
209 }
210
OnStop()211 void JsDriverExtension::OnStop()
212 {
213 DriverExtension::OnStop();
214 HILOG_INFO("JsDriverExtension OnStop begin.");
215 napi_env env = jsRuntime_.GetNapiEnv();
216 CallObjectMethod(env, "onRelease");
217 bool ret = ConnectionManager::GetInstance().DisconnectCaller(GetContext()->GetToken());
218 if (ret) {
219 ConnectionManager::GetInstance().ReportConnectionLeakEvent(getpid(), gettid());
220 HILOG_INFO("The driver extension connection is not disconnected.");
221 }
222 HILOG_INFO("%{public}s end.", __func__);
223 }
224
OnConnect(const AAFwk::Want & want)225 sptr<IRemoteObject> JsDriverExtension::OnConnect(const AAFwk::Want &want)
226 {
227 HandleScope handleScope(jsRuntime_);
228 napi_value result = CallOnConnect(want);
229 napi_env env = jsRuntime_.GetNapiEnv();
230 auto remoteObj = NAPI_ohos_rpc_getNativeRemoteObject(env, result);
231 if (remoteObj == nullptr) {
232 HILOG_ERROR("remoteObj nullptr.");
233 }
234 return remoteObj;
235 }
236
OnConnect(const AAFwk::Want & want,AppExecFwk::AbilityTransactionCallbackInfo<sptr<IRemoteObject>> * callbackInfo,bool & isAsyncCallback)237 sptr<IRemoteObject> JsDriverExtension::OnConnect(const AAFwk::Want &want,
238 AppExecFwk::AbilityTransactionCallbackInfo<sptr<IRemoteObject>> *callbackInfo, bool &isAsyncCallback)
239 {
240 HandleScope handleScope(jsRuntime_);
241 napi_value result = CallOnConnect(want);
242 napi_env env = jsRuntime_.GetNapiEnv();
243 bool isPromise = CheckPromise(result);
244 if (!isPromise) {
245 isAsyncCallback = false;
246 sptr<IRemoteObject> remoteObj = NAPI_ohos_rpc_getNativeRemoteObject(env, result);
247 if (remoteObj == nullptr) {
248 HILOG_ERROR("remoteObj nullptr.");
249 }
250 return remoteObj;
251 }
252
253 bool callResult = false;
254 do {
255 napi_value then = nullptr;
256 napi_get_named_property(env, result, "then", &then);
257 bool isCallable = false;
258 napi_is_callable(env, then, &isCallable);
259 if (!isCallable) {
260 HILOG_ERROR("CallPromise, property then is not callable.");
261 break;
262 }
263 napi_value promiseCallback = nullptr;
264 napi_create_function(env, "promiseCallback", strlen("promiseCallback"),
265 OnConnectPromiseCallback, callbackInfo, &promiseCallback);
266 napi_value argv[1] = { promiseCallback };
267 napi_call_function(env, result, then, 1, argv, nullptr);
268 callResult = true;
269 } while (false);
270
271 if (!callResult) {
272 HILOG_ERROR("Failed to call promise.");
273 isAsyncCallback = false;
274 } else {
275 isAsyncCallback = true;
276 }
277 return nullptr;
278 }
279
OnDisconnect(const AAFwk::Want & want)280 void JsDriverExtension::OnDisconnect(const AAFwk::Want &want)
281 {
282 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
283 Extension::OnDisconnect(want);
284 HILOG_DEBUG("%{public}s begin.", __func__);
285 CallOnDisconnect(want, false);
286 HILOG_DEBUG("%{public}s end.", __func__);
287 }
288
OnDisconnect(const AAFwk::Want & want,AppExecFwk::AbilityTransactionCallbackInfo<> * callbackInfo,bool & isAsyncCallback)289 void JsDriverExtension::OnDisconnect(const AAFwk::Want &want,
290 AppExecFwk::AbilityTransactionCallbackInfo<> *callbackInfo, bool &isAsyncCallback)
291 {
292 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
293 Extension::OnDisconnect(want);
294 HILOG_DEBUG("%{public}s begin.", __func__);
295 napi_value result = CallOnDisconnect(want, true);
296 bool isPromise = CheckPromise(result);
297 if (!isPromise) {
298 isAsyncCallback = false;
299 return;
300 }
301 bool callResult = CallPromise(result, callbackInfo);
302 if (!callResult) {
303 HILOG_ERROR("Failed to call promise.");
304 isAsyncCallback = false;
305 } else {
306 isAsyncCallback = true;
307 }
308
309 HILOG_DEBUG("%{public}s end.", __func__);
310 }
311
CallObjectMethod(napi_env env,const char * name,const napi_value * argv,size_t argc)312 napi_value JsDriverExtension::CallObjectMethod(napi_env env, const char* name, const napi_value* argv, size_t argc)
313 {
314 HILOG_INFO("JsDriverExtension::CallObjectMethod(%{public}s), begin", name);
315
316 if (!jsObj_) {
317 HILOG_WARN("Not found DriverExtension.js");
318 return nullptr;
319 }
320 napi_value obj = jsObj_->GetNapiValue();
321
322 napi_value method = nullptr;
323 napi_get_named_property(env, obj, name, &method);
324 napi_valuetype type = napi_undefined;
325 napi_typeof(env, method, &type);
326 if (type != napi_function) {
327 HILOG_ERROR("Failed to get '%{public}s' from DriverExtension object", name);
328 return nullptr;
329 }
330 HILOG_INFO("JsDriverExtension::CallFunction(%{public}s), success", name);
331 napi_value result = nullptr;
332 napi_call_function(env, obj, method, argc, argv, &result);
333 return result;
334 }
335
GetSrcPath(std::string & srcPath)336 void JsDriverExtension::GetSrcPath(std::string &srcPath)
337 {
338 if (!Extension::abilityInfo_->isModuleJson) {
339 /* temporary compatibility api8 + config.json */
340 srcPath.append(Extension::abilityInfo_->package);
341 srcPath.append("/assets/js/");
342 if (!Extension::abilityInfo_->srcPath.empty()) {
343 srcPath.append(Extension::abilityInfo_->srcPath);
344 }
345 srcPath.append("/").append(Extension::abilityInfo_->name).append(".abc");
346 return;
347 }
348
349 if (!Extension::abilityInfo_->srcEntrance.empty()) {
350 srcPath.append(Extension::abilityInfo_->moduleName + "/");
351 srcPath.append(Extension::abilityInfo_->srcEntrance);
352 srcPath.erase(srcPath.rfind('.'));
353 srcPath.append(".abc");
354 }
355 }
356
CallOnConnect(const AAFwk::Want & want)357 napi_value JsDriverExtension::CallOnConnect(const AAFwk::Want &want)
358 {
359 HITRACE_METER_NAME(HITRACE_TAG_ABILITY_MANAGER, __PRETTY_FUNCTION__);
360 Extension::OnConnect(want);
361 HILOG_DEBUG("%{public}s begin.", __func__);
362 napi_env env = jsRuntime_.GetNapiEnv();
363 napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
364 napi_value argv[] = {napiWant};
365 return CallObjectMethod(env, "onConnect", argv, ARGC_ONE);
366 }
367
CallOnDisconnect(const AAFwk::Want & want,bool withResult)368 napi_value JsDriverExtension::CallOnDisconnect(const AAFwk::Want &want, bool withResult)
369 {
370 HandleEscape handleEscape(jsRuntime_);
371 napi_env env = jsRuntime_.GetNapiEnv();
372 napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
373 napi_value argv[] = { napiWant };
374 napi_value result = CallObjectMethod(env, "onDisconnect", argv, ARGC_ONE);
375 if (withResult) {
376 return handleEscape.Escape(result);
377 } else {
378 return nullptr;
379 }
380 }
381
CheckPromise(napi_value result)382 bool JsDriverExtension::CheckPromise(napi_value result)
383 {
384 if (result == nullptr) {
385 HILOG_DEBUG("CheckPromise, result is null, no need to call promise.");
386 return false;
387 }
388 bool isPromise = false;
389 napi_env env = jsRuntime_.GetNapiEnv();
390 napi_is_promise(env, result, &isPromise);
391 if (!isPromise) {
392 HILOG_DEBUG("CheckPromise, result is not promise, no need to call promise.");
393 return false;
394 }
395 return true;
396 }
397
CallPromise(napi_value result,AppExecFwk::AbilityTransactionCallbackInfo<> * callbackInfo)398 bool JsDriverExtension::CallPromise(napi_value result, AppExecFwk::AbilityTransactionCallbackInfo<> *callbackInfo)
399 {
400 napi_value then = nullptr;
401 napi_env env = jsRuntime_.GetNapiEnv();
402 napi_get_named_property(env, result, "then", &then);
403
404 bool isCallable = false;
405 napi_is_callable(env, then, &isCallable);
406 if (!isCallable) {
407 HILOG_ERROR("CallPromise, property then is not callable.");
408 return false;
409 }
410 HandleScope handleScope(jsRuntime_);
411 napi_value promiseCallback = nullptr;
412 napi_create_function(env, "promiseCallback", strlen("promiseCallback"), PromiseCallback,
413 callbackInfo, &promiseCallback);
414 napi_value argv[1] = { promiseCallback };
415 napi_call_function(env, result, then, 1, argv, nullptr);
416 return true;
417 }
418
Dump(const std::vector<std::string> & params,std::vector<std::string> & info)419 void JsDriverExtension::Dump(const std::vector<std::string> ¶ms, std::vector<std::string> &info)
420 {
421 Extension::Dump(params, info);
422 HILOG_INFO("%{public}s called.", __func__);
423 HandleScope handleScope(jsRuntime_);
424 napi_env env = jsRuntime_.GetNapiEnv();
425 // create js array object of params
426 napi_value arrayValue = nullptr;
427 napi_create_array_with_length(env, params.size(), &arrayValue);
428 uint32_t index = 0;
429 for (const auto ¶m : params) {
430 napi_set_element(env, arrayValue, index++, CreateJsValue(env, param));
431 }
432
433 napi_value argv[] = { arrayValue };
434 napi_value dumpInfo = CallObjectMethod(env, "onDump", argv, ARGC_ONE);
435 bool isArray = false;
436 napi_is_array(env, dumpInfo, &isArray);
437 if (isArray) {
438 HILOG_ERROR("dumpInfo is not array.");
439 return;
440 }
441 uint32_t arrayLen = 0;
442 napi_get_array_length(env, dumpInfo, &arrayLen);
443 if (arrayLen <= 0) {
444 HILOG_ERROR("dumpInfo array length is error.");
445 return;
446 }
447 for (uint32_t i = 0; i < arrayLen; i++) {
448 napi_value element;
449 std::string dumpInfoStr;
450 napi_get_element(env, dumpInfo, i, &element);
451 if (!ConvertFromJsValue(env, element, dumpInfoStr)) {
452 HILOG_ERROR("Parse dumpInfoStr failed");
453 return;
454 }
455 info.push_back(dumpInfoStr);
456 }
457 HILOG_DEBUG("Dump info size: %{public}zu", info.size());
458 }
459 }
460 }
461