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