• 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_context_js.h"
17 
18 #include "dtbschedmgr_log.h"
19 #include "js_data_struct_converter.h"
20 #include "js_error_utils.h"
21 #include "js_extension_context.h"
22 #include "js_runtime.h"
23 #include "js_runtime_utils.h"
24 #include "js_utils.h"
25 #include "napi/native_api.h"
26 #include "napi_common_start_options.h"
27 #include "napi_common_util.h"
28 #include "napi_common_want.h"
29 #include "napi_remote_object.h"
30 #include "start_options.h"
31 
32 namespace OHOS {
33 namespace DistributedSchedule {
34 const std::string TAG = "DistributedExtensionContextJS";
35 using namespace AbilityRuntime;
36 
37 constexpr int32_t INDEX_ZERO = 0;
38 constexpr int32_t INDEX_ONE = 1;
39 constexpr int32_t ERROR_CODE_ONE = 1;
40 constexpr int32_t ERROR_CODE_TWO = 2;
41 constexpr size_t ARGC_ONE = 1;
42 constexpr size_t ARGC_TWO = 2;
43 
44 class DistributedExtensionContextJS final {
45 public:
DistributedExtensionContextJS(const std::shared_ptr<DistributedExtensionContext> & ct)46     explicit DistributedExtensionContextJS(const std::shared_ptr<DistributedExtensionContext>& ct) : context(ct) {}
47     ~DistributedExtensionContextJS() = default;
48 
49     static void Finalizer(napi_env env, void* data, void* hint);
50 
ConnectAbility(napi_env env,napi_callback_info info)51     static napi_value ConnectAbility(napi_env env, napi_callback_info info)
52     {
53         GET_CB_INFO_AND_CALL(env, info, DistributedExtensionContextJS, OnConnectAbility);
54     }
55 
DisconnectAbility(napi_env env,napi_callback_info info)56     static napi_value DisconnectAbility(napi_env env, napi_callback_info info)
57     {
58         GET_CB_INFO_AND_CALL(env, info, DistributedExtensionContextJS, OnDisconnectAbility);
59     }
60 
61 private:
62     std::weak_ptr<DistributedExtensionContext> context;
63 
OnConnectAbility(napi_env env,size_t argc,napi_value * argv)64     napi_value OnConnectAbility(napi_env env, size_t argc, napi_value *argv)
65     {
66         HILOGI("OnConnectAbility start.");
67         if (argc != ARGC_TWO) {
68             HILOGE("test failed: not enough params!");
69             return CreateJsUndefined(env);
70         }
71         AAFwk::Want want;
72 
73         if (!OHOS::AppExecFwk::UnwrapWant(env, argv[INDEX_ZERO], want)) {
74             HILOGE("parse want failed");
75             return CreateJsUndefined(env);
76         }
77         HILOGI("%{public}s bundleName: %{public}s abilityName: %{public}s", __func__, want.GetBundle().c_str(),
78             want.GetElement().GetAbilityName().c_str());
79         sptr<DistributedExtensionContextJSConnection> connection = new DistributedExtensionContextJSConnection(env);
80         if (connection == nullptr) {
81             return nullptr;
82         }
83         connection->SetJsConnectionObject(argv[1]);
84         int64_t connectId = serialNumber_;
85         ConnectionKey key;
86         key.id = serialNumber_;
87         key.want = want;
88         {
89             std::lock_guard<std::mutex> lock(g_connectMapMtx);
90             connects_.emplace(key, connection);
91         }
92         if (serialNumber_ < INT64_MAX) {
93             serialNumber_++;
94         } else {
95             serialNumber_ = 0;
96         }
97 
98         return StartConnectAsyncTask(env, connectId, want, connection);
99     }
100 
StartConnectAsyncTask(napi_env env,int64_t connectId,const AAFwk::Want & want,const sptr<DistributedExtensionContextJSConnection> & connection)101     napi_value StartConnectAsyncTask(napi_env env, int64_t connectId, const AAFwk::Want &want,
102         const sptr<DistributedExtensionContextJSConnection> &connection)
103     {
104         napi_value result = nullptr;
105         napi_value lastParam = nullptr;
106         napi_value connectResult = nullptr;
107         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
108         auto asyncTask = [weak = context, want, connection, connectId, env, task = napiAsyncTask.get()]() {
109             HILOGI("OnConnectAbility start.");
110             auto context = weak.lock();
111             if (context == nullptr) {
112                 HILOGW("context is released.");
113                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
114                 delete task;
115                 return;
116             }
117             HILOGI("context->ConnectAbility connection: %{public}d.", static_cast<int32_t>(connectId));
118             if (!context->ConnectAbility(want, connection)) {
119                 connection->CallJsFailed(ERROR_CODE_ONE);
120             }
121             task->Resolve(env, CreateJsUndefined(env));
122             delete task;
123         };
124         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
125             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
126         } else {
127             napiAsyncTask.release();
128         }
129         napi_create_int64(env, connectId, &connectResult);
130         return connectResult;
131     }
132 
OnDisconnectAbility(napi_env env,size_t argc,napi_value * argv)133     napi_value OnDisconnectAbility(napi_env env, size_t argc, napi_value *argv)
134     {
135         HILOGI("OnDisconnectAbility is called.");
136         if (!(argc == ARGC_ONE || argc == ARGC_TWO)) {
137             HILOGE("test failed: not enough params!");
138             return CreateJsUndefined(env);
139         }
140         AAFwk::Want want;
141         int64_t connectId = -1;
142         sptr<DistributedExtensionContextJSConnection> connection = nullptr;
143         napi_get_value_int64(env, argv[INDEX_ZERO], &connectId);
144         HILOGI("OnDisconnectAbility connection: %{public}d.", static_cast<int32_t>(connectId));
145         {
146             std::lock_guard<std::mutex> lock(g_connectMapMtx);
147             auto item = std::find_if(connects_.begin(), connects_.end(),
148                 [connectId](const std::map<ConnectionKey,
149                     sptr<DistributedExtensionContextJSConnection>>::value_type &obj) {
150                     return connectId == obj.first.id;
151                 });
152             if (item != connects_.end()) {
153                 want = item->first.want;
154                 connection = item->second;
155             }
156         }
157         napi_value lastParam = argc == ARGC_ONE ? nullptr : argv[INDEX_ONE];
158         return StartDisconnectAsyncTask(env, want, connection, lastParam);
159     }
160 
StartDisconnectAsyncTask(napi_env env,const AAFwk::Want & want,const sptr<DistributedExtensionContextJSConnection> & connection,napi_value lastParam)161     napi_value StartDisconnectAsyncTask(napi_env env, const AAFwk::Want &want,
162         const sptr<DistributedExtensionContextJSConnection> &connection, napi_value lastParam)
163     {
164         napi_value result = nullptr;
165         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
166         auto asyncTask = [weak = context, want, connection, env, task = napiAsyncTask.get()]() {
167             HILOGI("OnDisconnectAbility start.");
168             auto context = weak.lock();
169             if (context == nullptr) {
170                 HILOGW("context is released.");
171                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
172                 delete task;
173                 return;
174             }
175             if (connection == nullptr) {
176                 HILOGW("connection is nullptr.");
177                 task->Reject(env, CreateJsError(env, ERROR_CODE_TWO, "not found connection"));
178                 delete task;
179                 return;
180             }
181             HILOGI("context->DisconnectAbility.");
182             auto errcode = context->DisconnectAbility(want, connection);
183             errcode == 0 ? task->Resolve(env, CreateJsUndefined(env))
184                          : task->Reject(env, CreateJsError(env, errcode, "Disconnect Ability failed."));
185             delete task;
186         };
187         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
188             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
189         } else {
190             napiAsyncTask.release();
191         }
192         return result;
193     }
194 };
195 
CreateDistributedExtensionContextJS(napi_env env,std::shared_ptr<DistributedExtensionContext> context)196 napi_value CreateDistributedExtensionContextJS(napi_env env, std::shared_ptr<DistributedExtensionContext> context)
197 {
198     if (context == nullptr) {
199         HILOGE("Failed to CreateDistributedExtensionContextJS, context is nullptr.");
200         return nullptr;
201     }
202     std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo = context->GetAbilityInfo();
203     napi_value object = CreateJsExtensionContext(env, context, abilityInfo);
204     if (object == nullptr) {
205         HILOGE("Failed to CreateJsServiceExtensionContext, context is nullptr.");
206         return nullptr;
207     }
208     std::unique_ptr<DistributedExtensionContextJS> jsContext =
209         std::make_unique<DistributedExtensionContextJS>(context);
210     napi_wrap(env, object, jsContext.release(), DistributedExtensionContextJS::Finalizer, nullptr, nullptr);
211 
212     const char *moduleName = "DistributedExtensionContextJS";
213     BindNativeFunction(env, object, "connectAbility", moduleName, DistributedExtensionContextJS::ConnectAbility);
214     BindNativeFunction(env, object, "disconnectAbility", moduleName,
215         DistributedExtensionContextJS::DisconnectAbility);
216     return object;
217 }
218 
Finalizer(napi_env env,void * data,void * hint)219 void DistributedExtensionContextJS::Finalizer(napi_env env, void* data, void* hint)
220 {
221     HILOGI("Finalizer Called.");
222     std::unique_ptr<DistributedExtensionContextJS>(static_cast<DistributedExtensionContextJS*>(data));
223 }
224 
CreateJsMetadata(napi_env env,const AppExecFwk::Metadata & info)225 napi_value CreateJsMetadata(napi_env env, const AppExecFwk::Metadata &info)
226 {
227     HILOGI("CreateJsMetadata start.");
228 
229     napi_value objValue = nullptr;
230     napi_create_object(env, &objValue);
231 
232     napi_set_named_property(env, objValue, "name", CreateJsValue(env, info.name));
233     napi_set_named_property(env, objValue, "value", CreateJsValue(env, info.value));
234     napi_set_named_property(env, objValue, "resource", CreateJsValue(env, info.resource));
235     return objValue;
236 }
237 
CreateJsMetadataArray(napi_env env,const std::vector<AppExecFwk::Metadata> & info)238 napi_value CreateJsMetadataArray(napi_env env, const std::vector<AppExecFwk::Metadata> &info)
239 {
240     HILOGI("CreateJsMetadataArray start.");
241     napi_value arrayValue = nullptr;
242     napi_create_array_with_length(env, info.size(), &arrayValue);
243     uint32_t index = 0;
244     for (const auto &item : info) {
245         napi_set_element(env, arrayValue, index++, CreateJsMetadata(env, item));
246     }
247     return arrayValue;
248 }
249 
CreateJsExtensionAbilityInfo(napi_env env,const AppExecFwk::ExtensionAbilityInfo & info)250 napi_value CreateJsExtensionAbilityInfo(napi_env env, const AppExecFwk::ExtensionAbilityInfo &info)
251 {
252     HILOGI("CreateJsExtensionAbilityInfo start.");
253     napi_value objValue = nullptr;
254     napi_create_object(env, &objValue);
255 
256     napi_set_named_property(env, objValue, "bundleName", CreateJsValue(env, info.bundleName));
257     napi_set_named_property(env, objValue, "moduleName", CreateJsValue(env, info.moduleName));
258     napi_set_named_property(env, objValue, "name", CreateJsValue(env, info.name));
259     napi_set_named_property(env, objValue, "labelId", CreateJsValue(env, info.labelId));
260     napi_set_named_property(env, objValue, "descriptionId", CreateJsValue(env, info.descriptionId));
261     napi_set_named_property(env, objValue, "iconId", CreateJsValue(env, info.iconId));
262     napi_set_named_property(env, objValue, "isVisible", CreateJsValue(env, info.visible));
263     napi_set_named_property(env, objValue, "extensionAbilityType", CreateJsValue(env, info.type));
264 
265     napi_value permissionArray = nullptr;
266     napi_create_array_with_length(env, info.permissions.size(), &permissionArray);
267 
268     if (permissionArray != nullptr) {
269         int32_t index = 0;
270         for (auto permission : info.permissions) {
271             napi_set_element(env, permissionArray, index++, CreateJsValue(env, permission));
272         }
273     }
274     napi_set_named_property(env, objValue, "permissions", permissionArray);
275     napi_set_named_property(env, objValue, "applicationInfo", CreateJsApplicationInfo(env, info.applicationInfo));
276     napi_set_named_property(env, objValue, "metadata", CreateJsMetadataArray(env, info.metadata));
277     napi_set_named_property(env, objValue, "enabled", CreateJsValue(env, info.enabled));
278     napi_set_named_property(env, objValue, "readPermission", CreateJsValue(env, info.readPermission));
279     napi_set_named_property(env, objValue, "writePermission", CreateJsValue(env, info.writePermission));
280     return objValue;
281 }
282 
DistributedExtensionContextJSConnection(napi_env env)283 DistributedExtensionContextJSConnection::DistributedExtensionContextJSConnection(napi_env env) : env_(env),
284     handler_(std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::GetMainEventRunner()))
285 {
286 }
287 
~DistributedExtensionContextJSConnection()288 DistributedExtensionContextJSConnection::~DistributedExtensionContextJSConnection()
289 {
290     ReleaseConnection();
291 }
292 
OnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int32_t resultCode)293 void DistributedExtensionContextJSConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
294     const sptr<IRemoteObject> &remoteObject, int32_t resultCode)
295 {
296     HILOGI("OnAbilityConnectDone start, resultCode: %{public}d.", resultCode);
297     if (jsConnectionObject_ == nullptr) {
298         HILOGE("jsConnectionObject_ is nullptr!");
299         ReleaseConnection();
300         return;
301     }
302     if (handler_ == nullptr) {
303         HILOGI("handler_ is nullptr.");
304         return;
305     }
306     wptr<DistributedExtensionContextJSConnection> connection = this;
307     auto task = [connection, element, remoteObject, resultCode]() {
308         sptr<DistributedExtensionContextJSConnection> connectionSptr = connection.promote();
309         if (connectionSptr == nullptr) {
310             HILOGE("connectionSptr is nullptr.");
311             return;
312         }
313         connectionSptr->HandleOnAbilityConnectDone(element, remoteObject, resultCode);
314     };
315     handler_->PostTask(task, "OnAbilityConnectDone", 0, AppExecFwk::EventQueue::Priority::VIP);
316 }
317 
HandleOnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int32_t resultCode)318 void DistributedExtensionContextJSConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element,
319     const sptr<IRemoteObject> &remoteObject, int32_t resultCode)
320 {
321     HILOGI("HandleOnAbilityConnectDone start, resultCode:%{public}d.", resultCode);
322     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
323 
324     HILOGI("OnAbilityConnectDone start NAPI_ohos_rpc_CreateJsRemoteObject.");
325     napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(env_, remoteObject);
326     napi_value argv[] = { napiElementName, napiRemoteObject };
327 
328     if (jsConnectionObject_ == nullptr) {
329         HILOGE("jsConnectionObject_ is nullptr!");
330         return;
331     }
332 
333     napi_value obj = nullptr;
334     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
335         HILOGE("failed to get jsConnectionObject_!");
336         return;
337     }
338     if (obj == nullptr) {
339         HILOGE("failed to get object!");
340         return;
341     }
342     napi_value methodOnConnect = nullptr;
343     napi_get_named_property(env_, obj, "onConnect", &methodOnConnect);
344     if (methodOnConnect == nullptr) {
345         HILOGE("failed to get onConnect from object!");
346         return;
347     }
348     HILOGI("DistributedExtensionContextJSConnection::CallFunction onConnect, success.");
349     napi_value callResult = nullptr;
350     napi_call_function(env_, obj, methodOnConnect, ARGC_TWO, argv, &callResult);
351     HILOGI("OnAbilityConnectDone end.");
352 }
353 
OnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int32_t resultCode)354 void DistributedExtensionContextJSConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
355     int32_t resultCode)
356 {
357     HILOGI("OnAbilityDisconnectDone start, resultCode: %{public}d.", resultCode);
358     if (handler_ == nullptr) {
359         HILOGI("handler_ is nullptr.");
360         return;
361     }
362     wptr<DistributedExtensionContextJSConnection> connection = this;
363     auto task = [connection, element, resultCode]() {
364         sptr<DistributedExtensionContextJSConnection> connectionSptr = connection.promote();
365         if (!connectionSptr) {
366             HILOGE("connectionSptr is nullptr.");
367             return;
368         }
369         connectionSptr->HandleOnAbilityDisconnectDone(element, resultCode);
370     };
371     handler_->PostTask(task, "OnAbilityDisconnectDone", 0, AppExecFwk::EventQueue::Priority::VIP);
372 }
373 
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int32_t resultCode)374 void DistributedExtensionContextJSConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
375     int32_t resultCode)
376 {
377     HILOGI("HandleOnAbilityDisconnectDone start, resultCode:%{public}d.", resultCode);
378     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
379     napi_value argv[] = { napiElementName };
380     if (jsConnectionObject_ == nullptr) {
381         HILOGE("jsConnectionObject_ is nullptr!");
382         return;
383     }
384     napi_value obj = nullptr;
385     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
386         HILOGE("failed to get jsConnectionObject_!");
387         return;
388     }
389     if (obj == nullptr) {
390         HILOGE("failed to get object!");
391         return;
392     }
393     napi_value method = nullptr;
394     napi_get_named_property(env_, obj, "onDisconnect", &method);
395     if (method == nullptr) {
396         HILOGE("failed to get onDisconnect from object!");
397         return;
398     }
399     std::string bundleName = element.GetBundleName();
400     std::string abilityName = element.GetAbilityName();
401     {
402         std::lock_guard<std::mutex> lock(g_connectMapMtx);
403         HILOGI("OnAbilityDisconnectDone connects_.size: %{public}zu.", connects_.size());
404         auto item = std::find_if(connects_.begin(), connects_.end(),
405             [bundleName, abilityName](
406                 const std::map<ConnectionKey, sptr<DistributedExtensionContextJSConnection>>::value_type &obj) {
407                 return (bundleName == obj.first.want.GetBundle()) &&
408                        (abilityName == obj.first.want.GetElement().GetAbilityName());
409             });
410         if (item != connects_.end()) {
411             if (item->second != nullptr) {
412                 item->second->ReleaseConnection();
413             }
414             connects_.erase(item);
415             HILOGI("OnAbilityDisconnectDone erase connects_.size: %{public}zu.", connects_.size());
416         }
417     }
418     HILOGI("OnAbilityDisconnectDone CallFunction success.");
419     napi_value callResult = nullptr;
420     napi_call_function(env_, obj, method, ARGC_ONE, argv, &callResult);
421 }
422 
SetJsConnectionObject(napi_value jsConnectionObject)423 void DistributedExtensionContextJSConnection::SetJsConnectionObject(napi_value jsConnectionObject)
424 {
425     napi_create_reference(env_, jsConnectionObject, 1, &jsConnectionObject_);
426 }
427 
CallJsFailed(int32_t errorCode)428 void DistributedExtensionContextJSConnection::CallJsFailed(int32_t errorCode)
429 {
430     HILOGI("CallJsFailed start");
431     if (jsConnectionObject_ == nullptr) {
432         HILOGE("jsConnectionObject_ is nullptr!");
433         return;
434     }
435     napi_value obj = nullptr;
436     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
437         HILOGE("failed to get jsConnectionObject_!");
438         return;
439     }
440     if (obj == nullptr) {
441         HILOGE("failed to get object.");
442         return;
443     }
444 
445     napi_value method = nullptr;
446     napi_get_named_property(env_, obj, "onFailed", &method);
447     if (method == nullptr) {
448         HILOGE("failed to get onFailed from object!");
449         return;
450     }
451     napi_value result = nullptr;
452     napi_create_int32(env_, errorCode, &result);
453     napi_value argv[] = { result };
454     HILOGI("CallJsFailed CallFunction success.");
455     napi_value callResult = nullptr;
456     napi_call_function(env_, obj, method, ARGC_ONE, argv, &callResult);
457     HILOGI("CallJsFailed end.");
458 }
459 
ReleaseConnection()460 void DistributedExtensionContextJSConnection::ReleaseConnection()
461 {
462     HILOGI("ReleaseConnection");
463     if (jsConnectionObject_ != nullptr) {
464         napi_delete_reference(env_, jsConnectionObject_);
465         env_ = nullptr;
466         jsConnectionObject_ = nullptr;
467     }
468 }
469 }
470 }
471