• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "distributed_extension_js.h"
17 
18 #include "distributed_sched_utils.h"
19 #include "distributed_extension_context_js.h"
20 #include "napi_common_want.h"
21 
22 namespace OHOS {
23 namespace DistributedSchedule {
24 using namespace std;
25 
26 constexpr size_t ARGC_ONE = 1;
27 static std::mutex g_distributedExtensionValidLock;
28 static int32_t g_distributedExtensionCount = 0;
29 
GetSrcPath(const AppExecFwk::AbilityInfo & info)30 static string GetSrcPath(const AppExecFwk::AbilityInfo &info)
31 {
32     using AbilityRuntime::Extension;
33     stringstream ss;
34 
35     if (!info.srcEntrance.empty()) {
36         ss << info.moduleName << '/' << string(info.srcEntrance, 0, info.srcEntrance.rfind(".")) << ".abc";
37         return ss.str();
38     }
39     return "";
40 }
41 
AttachDistributedExtensionContext(napi_env env,void * value,void *)42 napi_value AttachDistributedExtensionContext(napi_env env, void *value, void *)
43 {
44     HILOG_INFO("AttachDistributedExtensionContext");
45     if (value == nullptr || env == nullptr) {
46         HILOG_WARN("invalid parameter.");
47         return nullptr;
48     }
49     auto ptr = reinterpret_cast<std::weak_ptr<DistributedExtensionContext> *>(value)->lock();
50     if (ptr == nullptr) {
51         HILOG_ERROR("invalid context.");
52         return nullptr;
53     }
54     auto object = CreateDistributedExtensionContextJS(env, ptr);
55     if (object == nullptr) {
56         HILOG_ERROR("Failed to get js distributed extension context");
57         return nullptr;
58     }
59     auto contextRef = AbilityRuntime::JsRuntime::LoadSystemModuleByEngine(env,
60         "application.DistributedExtensionContext", &object, 1);
61     if (contextRef == nullptr) {
62         HILOG_ERROR("Failed to load BackupExtensionContext.");
63         return nullptr;
64     }
65     napi_value contextObj = contextRef->GetNapiValue();
66     napi_coerce_to_native_binding_object(env, contextObj, AbilityRuntime::DetachCallbackFunc,
67                                          AttachDistributedExtensionContext, value, nullptr);
68 
69     auto workContext = new (std::nothrow) std::weak_ptr<DistributedExtensionContext>(ptr);
70     if (workContext == nullptr) {
71         HILOG_ERROR("Failed to get backup extension context");
72         return nullptr;
73     }
74     napi_status status = napi_wrap(
75         env, contextObj, workContext,
76         [](napi_env, void *data, void *) {
77             HILOG_DEBUG("Finalizer for weak_ptr base context is called");
78             delete static_cast<std::weak_ptr<DistributedExtensionContext> *>(data);
79         },
80         nullptr, nullptr);
81     if (status != napi_ok) {
82         HILOG_DEBUG("Failed to wrap js instance");
83         delete workContext;
84         workContext = nullptr;
85     }
86     return contextObj;
87 }
88 
Init(const shared_ptr<AppExecFwk::AbilityLocalRecord> & record,const shared_ptr<AppExecFwk::OHOSApplication> & application,shared_ptr<AppExecFwk::AbilityHandler> & handler,const sptr<IRemoteObject> & token)89 void DistributedExtensionJs::Init(const shared_ptr<AppExecFwk::AbilityLocalRecord> &record,
90     const shared_ptr<AppExecFwk::OHOSApplication> &application,
91     shared_ptr<AppExecFwk::AbilityHandler> &handler, const sptr<IRemoteObject> &token)
92 {
93     HILOG_INFO("Init the DistributedExtensionAbility(JS)");
94     try {
95         DistributedExtension::Init(record, application, handler, token);
96         if (abilityInfo_ == nullptr) {
97             HILOG_ERROR("Invalid abilityInfo_");
98             return;
99         }
100         const AppExecFwk::AbilityInfo &info = *abilityInfo_;
101         string bundleName = info.bundleName;
102         string moduleName(info.moduleName + "::" + info.name);
103         string modulePath = GetSrcPath(info);
104         int moduleType = static_cast<int>(info.type);
105         HILOG_INFO("Try to load %{public}s's %{public}s(type %{public}d) from %{public}s", bundleName.c_str(),
106             moduleName.c_str(), moduleType, GetAnonymStr(modulePath).c_str());
107 
108         AbilityRuntime::HandleScope handleScope(jsRuntime_);
109         jsObj_ = jsRuntime_.LoadModule(moduleName, modulePath, info.hapPath,
110                                        abilityInfo_->compileMode == AbilityRuntime::CompileMode::ES_MODULE);
111         if (jsObj_ == nullptr) {
112             HILOG_WARN("Oops! There's no custom DistributedExtensionAbility");
113             return;
114         }
115         HILOG_INFO("Wow! Here's a custsom DistributedExtensionAbility");
116         ExportJsContext();
117     } catch (const exception &e) {
118         HILOG_ERROR("%{public}s", e.what());
119     }
120 }
121 
DistributedExtensionJs(AbilityRuntime::JsRuntime & jsRuntime)122 DistributedExtensionJs::DistributedExtensionJs(AbilityRuntime::JsRuntime &jsRuntime) : jsRuntime_(jsRuntime)
123 {
124     std::lock_guard<std::mutex> lock(g_distributedExtensionValidLock);
125     g_distributedExtensionCount += 1;
126     HILOG_INFO("DistributedExtensionJs::DistributedExtensionJs, count=%{public}d.", g_distributedExtensionCount);
127 }
128 
~DistributedExtensionJs()129 DistributedExtensionJs::~DistributedExtensionJs()
130 {
131     jsRuntime_.FreeNativeReference(std::move(jsObj_));
132     std::lock_guard<std::mutex> lock(g_distributedExtensionValidLock);
133     g_distributedExtensionCount -= 1;
134     HILOG_INFO("DistributedExtensionJs::~DistributedExtensionJs, count=%{public}d.", g_distributedExtensionCount);
135 }
136 
ExportJsContext(void)137 void DistributedExtensionJs::ExportJsContext(void)
138 {
139     auto env = jsRuntime_.GetNapiEnv();
140     if (jsObj_ == nullptr) {
141         HILOG_ERROR("Failed to get js object.");
142         return;
143     }
144     napi_value obj = jsObj_->GetNapiValue();
145     if (obj == nullptr) {
146         HILOG_ERROR("Failed to get DistributedExtensionAbility object");
147         return;
148     }
149 
150     auto context = GetContext();
151     if (context == nullptr) {
152         HILOG_ERROR("Failed to get context");
153         return;
154     }
155 
156     HILOG_INFO("CreateDistributedExtensionContextJS");
157     napi_value contextObj = CreateDistributedExtensionContextJS(env, context);
158     auto contextRef = jsRuntime_.LoadSystemModule("application.DistributedExtensionContext", &contextObj, ARGC_ONE);
159     if (!contextRef) {
160         HILOG_ERROR("context is nullptr");
161         return;
162     }
163     contextObj = contextRef->GetNapiValue();
164     HILOG_INFO("Bind context");
165     context->Bind(jsRuntime_, contextRef.release());
166     napi_set_named_property(env, obj, "context", contextObj);
167 
168     auto workContext = new (std::nothrow) std::weak_ptr<DistributedExtensionContext>(context);
169     if (workContext == nullptr) {
170         HILOG_ERROR("Failed to create DistributedExtensionContext.");
171         return;
172     }
173     napi_coerce_to_native_binding_object(env, contextObj, AbilityRuntime::DetachCallbackFunc,
174                                          AttachDistributedExtensionContext, workContext, nullptr);
175     HILOG_INFO("Set backup extension ability context pointer is nullptr: %{public}d", context.get() == nullptr);
176     napi_status status = napi_wrap(
177         env, contextObj, workContext,
178         [](napi_env, void *data, void *) {
179             HILOG_INFO("Finalizer for weak_ptr base context is called");
180             delete static_cast<std::weak_ptr<DistributedExtensionContext> *>(data);
181         },
182         nullptr, nullptr);
183     if (status != napi_ok) {
184         HILOG_INFO("Failed to wrap js instance");
185         delete workContext;
186         workContext = nullptr;
187     }
188 }
189 
Create(const unique_ptr<AbilityRuntime::Runtime> & runtime)190 DistributedExtensionJs *DistributedExtensionJs::Create(const unique_ptr<AbilityRuntime::Runtime> &runtime)
191 {
192     HILOG_INFO("Create as an DistributedExtensionAbility(JS)");
193     return new DistributedExtensionJs(static_cast<AbilityRuntime::JsRuntime &>(*runtime));
194 }
195 
TriggerOnCreate(AAFwk::Want & want)196 int32_t DistributedExtensionJs::TriggerOnCreate(AAFwk::Want& want)
197 {
198     HILOG_INFO("DistributedExtensionAbility(JS) TriggerOnCreate ex");
199     want_ = want;
200     if (jsObj_ == nullptr) {
201         HILOG_ERROR("The app does not provide the TriggerOnCreate interface.");
202     }
203 
204     return CallJsOnCreate();
205 }
206 
CallJsOnCreate()207 int32_t DistributedExtensionJs::CallJsOnCreate()
208 {
209     HILOG_INFO("Start call app js method onCreate");
210     auto errCode = CallJsMethod("onCreate", jsRuntime_, jsObj_.get(), ParseCreateInfo(), nullptr);
211     if (errCode != ERR_OK) {
212         HILOG_ERROR("CallJsMethod error, code:%{public}d.", errCode);
213     }
214     return errCode;
215 }
216 
ParseCreateInfo()217 std::function<bool(napi_env env, std::vector<napi_value> &argv)> DistributedExtensionJs::ParseCreateInfo()
218 {
219     auto onCreateFun = [want(want_)](napi_env env, vector<napi_value> &argv) -> bool {
220         napi_value napiWant = OHOS::AppExecFwk::WrapWant(env, want);
221         argv.push_back(napiWant);
222         return true;
223     };
224     return onCreateFun;
225 }
226 
TriggerOnDestroy()227 int32_t DistributedExtensionJs::TriggerOnDestroy()
228 {
229     HILOG_INFO("DistributedExtensionAbility(JS) TriggerOnDestroy ex");
230     if (jsObj_ == nullptr) {
231         HILOG_ERROR("The app does not provide the TriggerOnDestroy interface.");
232     }
233 
234     return CallJsOnDestroy();
235 }
236 
CallJsOnDestroy()237 int32_t DistributedExtensionJs::CallJsOnDestroy()
238 {
239     HILOG_INFO("Start call app js method onDestroy");
240     auto errCode = CallJsMethod("onDestroy", jsRuntime_, jsObj_.get(), nullptr, nullptr);
241     if (errCode != ERR_OK) {
242         HILOG_ERROR("CallJsMethod error, code:%{public}d.", errCode);
243     }
244     return errCode;
245 }
246 
TriggerOnCollaborate(AAFwk::WantParams & wantParam)247 int32_t DistributedExtensionJs::TriggerOnCollaborate(AAFwk::WantParams &wantParam)
248 {
249     HILOG_INFO("jDistributedExtensionAbility(JS) TriggerOnCollaborate ex");
250     wantParam_ = wantParam;
251     if (jsObj_ == nullptr) {
252         HILOG_ERROR("The app does not provide the TriggerOnCollaborate interface.");
253     }
254 
255     return CallJsOnCollaborate();
256 }
257 
CallJsOnCollaborate()258 int32_t DistributedExtensionJs::CallJsOnCollaborate()
259 {
260     HILOG_INFO("Start call app js method OnCollaborate");
261     auto retParser = [jsRuntime{ &jsRuntime_ }](napi_env env,
262         napi_value result) -> bool {
263         uint32_t value;
264         if (napi_get_value_uint32(env, result, &value) != napi_ok) {
265             HILOG_ERROR("Can not get uint32 value");
266             return false;
267         }
268 
269         HILOG_INFO("CallJsOnCollaborate result = %{public}d.", value);
270         return true;
271     };
272     auto errCode = CallJsMethod("onCollaborate", jsRuntime_, jsObj_.get(), ParseCollabInfo(), retParser);
273     if (errCode != ERR_OK) {
274         HILOG_ERROR("CallJsMethod error, code:%{public}d.", errCode);
275     }
276     return errCode;
277 }
278 
ParseCollabInfo()279 std::function<bool(napi_env env, std::vector<napi_value> &argv)> DistributedExtensionJs::ParseCollabInfo()
280 {
281     auto onCollabFun = [wantParam(wantParam_)](napi_env env, vector<napi_value> &argv) -> bool {
282         napi_value napiWantParam = AppExecFwk::WrapWantParams(env, wantParam);
283         argv.push_back(napiWantParam);
284         return true;
285     };
286     return onCollabFun;
287 }
288 
InvokeJsMethod(CallJsParam * param,AbilityRuntime::HandleEscape & handleEscape,napi_env env,napi_handle_scope & scope,vector<napi_value> & argv)289 static int InvokeJsMethod(CallJsParam *param, AbilityRuntime::HandleEscape& handleEscape, napi_env env,
290     napi_handle_scope& scope, vector<napi_value>& argv)
291 {
292     HILOG_INFO("InvokeJsMethod start");
293     if (param == nullptr || param->jsObj == nullptr) {
294         HILOG_ERROR("param or jsObj is nullptr");
295         return EINVAL;
296     }
297     napi_value value = param->jsObj->GetNapiValue();
298     if (value == nullptr) {
299         HILOG_ERROR("failed to get napi value object.");
300         return EINVAL;
301     }
302     napi_status status;
303     napi_value method;
304     status = napi_get_named_property(env, value, param->funcName.c_str(), &method);
305     if (status != napi_ok) {
306         HILOG_ERROR("napi_get_named_property failed.");
307         return EINVAL;
308     }
309     napi_value result;
310     HILOG_INFO("Extension start do call current js method, methodName:%{public}s", param->funcName.c_str());
311     if (napi_call_function(env, value, method, argv.size(), argv.data(), &result) != napi_ok) {
312         HILOG_ERROR("napi call function failed");
313     }
314     if (param->retParser != nullptr) {
315         if (!param->retParser(env, handleEscape.Escape(result))) {
316             HILOG_ERROR("Parser js result fail.");
317             return EINVAL;
318         }
319     }
320     HILOG_INFO("InvokeJsMethod end");
321     return ERR_OK;
322 }
323 
DoCallJsMethod(CallJsParam * param)324 static int DoCallJsMethod(CallJsParam *param)
325 {
326     HILOG_INFO("DoCallJsMethod enter");
327     if (param == nullptr) {
328         HILOG_ERROR("param is nullptr");
329         return EINVAL;
330     }
331     AbilityRuntime::JsRuntime *jsRuntime = param->jsRuntime;
332     HILOG_INFO("Start execute DoCallJsMethod");
333     if (jsRuntime == nullptr) {
334         HILOG_ERROR("failed to get jsRuntime");
335         return EINVAL;
336     }
337     AbilityRuntime::HandleEscape handleEscape(*jsRuntime);
338     auto env = jsRuntime->GetNapiEnv();
339     napi_handle_scope scope = nullptr;
340     napi_open_handle_scope(env, &scope);
341     if (scope == nullptr) {
342         HILOG_ERROR("scope is nullptr");
343         return EINVAL;
344     }
345     vector<napi_value> argv = {};
346     if (param->argParser != nullptr) {
347         if (!param->argParser(env, argv)) {
348             HILOG_ERROR("failed to get params.");
349             napi_close_handle_scope(env, scope);
350             return EINVAL;
351         }
352     }
353     auto ret = InvokeJsMethod(param, handleEscape, env, scope, argv);
354     napi_close_handle_scope(env, scope);
355     HILOG_INFO("End execute DoCallJsMethod");
356     return ret;
357 }
358 
CallJsMethod(const std::string & funcName,AbilityRuntime::JsRuntime & jsRuntime,NativeReference * jsObj,InputArgsParser argParser,ResultValueParser retParser)359 int32_t DistributedExtensionJs::CallJsMethod(const std::string &funcName, AbilityRuntime::JsRuntime &jsRuntime,
360     NativeReference *jsObj, InputArgsParser argParser, ResultValueParser retParser)
361 {
362     HILOG_INFO("DistributedExtensionJs::CallJsMethod enter");
363     uv_loop_s *loop = nullptr;
364     napi_status status = napi_get_uv_event_loop(jsRuntime.GetNapiEnv(), &loop);
365     if (status != napi_ok) {
366         HILOG_ERROR("failed to get uv event loop.");
367         return EINVAL;
368     }
369     auto param = std::make_shared<CallJsParam>(funcName, &jsRuntime, jsObj, argParser, retParser);
370     if (param == nullptr) {
371         HILOG_ERROR("failed to new param.");
372         return EINVAL;
373     }
374 
375     auto work = std::make_shared<uv_work_t>();
376     if (work == nullptr) {
377         HILOG_ERROR("failed to new uv_work_t.");
378         return EINVAL;
379     }
380 
381     work->data = reinterpret_cast<void *>(param.get());
382     int ret = uv_queue_work(
383         loop, work.get(), [](uv_work_t *work) {
384             HILOG_INFO("Enter, %{public}zu", (size_t)work);
385         },
386         [](uv_work_t *work, int status) {
387             HILOG_INFO("AsyncWork Enter, %{public}zu", (size_t)work);
388             CallJsParam *param = reinterpret_cast<CallJsParam *>(work->data);
389             do {
390                 if (param == nullptr) {
391                     HILOG_ERROR("failed to get CallJsParam.");
392                     break;
393                 }
394                 HILOG_INFO("Start call current js method");
395                 if (DoCallJsMethod(param) != ERR_OK) {
396                     HILOG_ERROR("failed to call DoCallJsMethod.");
397                 }
398             } while (false);
399             HILOG_INFO("will notify current thread info");
400             std::unique_lock<std::mutex> lock(param->distributedOperateMutex);
401             param->isReady.store(true);
402             param->distributedOperateCondition.notify_all();
403         });
404     if (ret != 0) {
405         HILOG_ERROR("failed to exec uv_queue_work.");
406         return EINVAL;
407     }
408     std::unique_lock<std::mutex> lock(param->distributedOperateMutex);
409     param->distributedOperateCondition.wait(lock, [param]() { return param->isReady.load(); });
410     HILOG_INFO("End do call current js method");
411     return ERR_OK;
412 }
413 }
414 }
415