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