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