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> ¶ms, 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 ¶m : 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