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