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