1 /*
2  * Copyright (c) 2021-2024 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 "form_runtime/js_form_extension_context.h"
17 
18 #include <cinttypes>
19 #include <cstdint>
20 
21 #include "hilog_tag_wrapper.h"
22 #include "form_mgr_errors.h"
23 #include "ipc_skeleton.h"
24 #include "js_extension_context.h"
25 #include "js_error_utils.h"
26 #include "js_runtime.h"
27 #include "js_runtime_utils.h"
28 #include "napi/native_api.h"
29 #include "napi_common_ability.h"
30 #include "napi_common_start_options.h"
31 #include "napi_common_want.h"
32 #include "napi_remote_object.h"
33 #include "napi_form_util.h"
34 #include "tokenid_kit.h"
35 #include <charconv>
36 
37 namespace OHOS {
38 namespace AbilityRuntime {
39 namespace {
40 constexpr int32_t INDEX_ZERO = 0;
41 constexpr int32_t INDEX_ONE = 1;
42 constexpr size_t ARGC_ONE = 1;
43 constexpr size_t ARGC_TWO = 2;
44 const int UPDATE_FORM_PARAMS_SIZE = 2;
45 
46 std::map<ConnectionKey, sptr<JSFormExtensionConnection>, key_compare> g_connects;
47 int64_t g_serialNumber = 0;
48 
RemoveConnection(int64_t connectId)49 void RemoveConnection(int64_t connectId)
50 {
51     auto item = std::find_if(g_connects.begin(), g_connects.end(),
52     [&connectId](const auto &obj) {
53         return connectId == obj.first.id;
54     });
55     if (item != g_connects.end()) {
56         TAG_LOGD(AAFwkTag::FORM_EXT, "ability exist");
57         if (item->second) {
58             item->second->RemoveConnectionObject();
59         }
60         g_connects.erase(item);
61     } else {
62         TAG_LOGD(AAFwkTag::FORM_EXT, "ability not exist");
63     }
64 }
65 class JsFormExtensionContext final {
66 public:
JsFormExtensionContext(const std::shared_ptr<FormExtensionContext> & context)67     explicit JsFormExtensionContext(const std::shared_ptr<FormExtensionContext>& context) : context_(context) {}
68     ~JsFormExtensionContext() = default;
69 
Finalizer(napi_env env,void * data,void * hint)70     static void Finalizer(napi_env env, void* data, void* hint)
71     {
72         TAG_LOGI(AAFwkTag::FORM_EXT, "called");
73         std::unique_ptr<JsFormExtensionContext>(static_cast<JsFormExtensionContext*>(data));
74     }
75 
UpdateForm(napi_env env,napi_callback_info info)76     static napi_value UpdateForm(napi_env env, napi_callback_info info)
77     {
78         GET_NAPI_INFO_AND_CALL(env, info, JsFormExtensionContext, OnUpdateForm);
79     }
80 
StartAbility(napi_env env,napi_callback_info info)81     static napi_value StartAbility(napi_env env, napi_callback_info info)
82     {
83         GET_NAPI_INFO_AND_CALL(env, info, JsFormExtensionContext, OnStartAbility);
84     }
85 
ConnectAbility(napi_env env,napi_callback_info info)86     static napi_value ConnectAbility(napi_env env, napi_callback_info info)
87     {
88         GET_NAPI_INFO_AND_CALL(env, info, JsFormExtensionContext, OnConnectAbility);
89     }
90 
DisconnectAbility(napi_env env,napi_callback_info info)91     static napi_value DisconnectAbility(napi_env env, napi_callback_info info)
92     {
93         GET_NAPI_INFO_AND_CALL(env, info, JsFormExtensionContext, OnDisconnectAbility);
94     }
95 
96 private:
97     std::weak_ptr<FormExtensionContext> context_;
98 
CheckCallerIsSystemApp() const99     bool CheckCallerIsSystemApp() const
100     {
101         auto selfToken = IPCSkeleton::GetSelfTokenID();
102         return Security::AccessToken::TokenIdKit::IsSystemAppByFullTokenID(selfToken);
103     }
104 
OnUpdateForm(napi_env env,NapiCallbackInfo & info)105     napi_value OnUpdateForm(napi_env env, NapiCallbackInfo& info)
106     {
107         TAG_LOGI(AAFwkTag::FORM_EXT, "called");
108         if (info.argc < UPDATE_FORM_PARAMS_SIZE) {
109             TAG_LOGE(AAFwkTag::FORM_EXT, "invalid argc");
110             return CreateJsUndefined(env);
111         }
112 
113         std::string strFormId;
114         ConvertFromJsValue(env, info.argv[0], strFormId);
115         int64_t formId = -1;
116         auto res = std::from_chars(strFormId.c_str(), strFormId.c_str() + strFormId.size(), formId);
117         if (res.ec != std::errc()) {
118             TAG_LOGE(AAFwkTag::FORM_EXT, "from_chars error strFormId:%{public}s", strFormId.c_str());
119             formId = -1;
120         }
121 
122         AppExecFwk::FormProviderData formProviderData;
123         std::string formDataStr = "{}";
124         if (CheckTypeForNapiValue(env, info.argv[1], napi_object)) {
125             napi_value nativeDataValue = nullptr;
126             napi_get_named_property(env, info.argv[1], "data", &nativeDataValue);
127             if (nativeDataValue == nullptr || !ConvertFromJsValue(env, nativeDataValue, formDataStr)) {
128                 TAG_LOGE(AAFwkTag::FORM_EXT, "NativeDataValue null or ConvertFromJsValue failed");
129             }
130         } else {
131             TAG_LOGE(AAFwkTag::FORM_EXT, "Not object");
132         }
133 
134         formProviderData = AppExecFwk::FormProviderData(formDataStr);
135         NapiAsyncTask::CompleteCallback complete =
136             [weak = context_, formId, formProviderData](napi_env env, NapiAsyncTask& task, int32_t status) {
137                 auto context = weak.lock();
138                 if (!context) {
139                     TAG_LOGW(AAFwkTag::FORM_EXT, "Context released");
140                     task.Reject(env, CreateJsError(env, 1, "Context is released"));
141                     return;
142                 }
143                 auto errcode = context->UpdateForm(formId, formProviderData);
144                 if (errcode == ERR_OK) {
145                     task.Resolve(env, CreateJsUndefined(env));
146                 } else {
147                     task.Reject(env, CreateJsError(env, errcode, "update form failed."));
148                 }
149             };
150 
151         napi_value lastParam =
152             (info.argc == UPDATE_FORM_PARAMS_SIZE) ? nullptr : info.argv[info.argc - 1];
153         napi_value result = nullptr;
154         NapiAsyncTask::ScheduleHighQos("JsFormExtensionContext::OnUpdateForm",
155             env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
156         return result;
157     }
158 
OnStartAbility(napi_env env,NapiCallbackInfo & info)159     napi_value OnStartAbility(napi_env env, NapiCallbackInfo& info)
160     {
161         TAG_LOGI(AAFwkTag::FORM_EXT, "called");
162         // only support one or two params
163         if (info.argc != ARGC_ONE && info.argc != ARGC_TWO) {
164             TAG_LOGE(AAFwkTag::FORM_EXT, "Not enough params");
165             NapiFormUtil::ThrowParamNumError(env, std::to_string(info.argc), "1 or 2");
166             return CreateJsUndefined(env);
167         }
168 
169         decltype(info.argc) unwrapArgc = 0;
170         AAFwk::Want want;
171         bool unwrapResult = OHOS::AppExecFwk::UnwrapWant(env, info.argv[INDEX_ZERO], want);
172         if (!unwrapResult) {
173             TAG_LOGE(AAFwkTag::FORM_EXT, "unwrap want failed");
174             NapiFormUtil::ThrowParamTypeError(env, "want", "Want");
175             return CreateJsUndefined(env);
176         }
177         TAG_LOGI(AAFwkTag::FORM_EXT, "Start bundle: %{public}s ability: %{public}s",
178             want.GetBundle().c_str(),
179             want.GetElement().GetAbilityName().c_str());
180         unwrapArgc++;
181 
182         NapiAsyncTask::CompleteCallback complete =
183             [weak = context_, want](napi_env env, NapiAsyncTask& task, int32_t status) {
184                 TAG_LOGI(AAFwkTag::FORM_EXT, "startAbility begin");
185                 auto context = weak.lock();
186                 if (!context) {
187                     TAG_LOGW(AAFwkTag::FORM_EXT, "context is released");
188                     task.Reject(env, NapiFormUtil::CreateErrorByInternalErrorCode(
189                         env, ERR_APPEXECFWK_FORM_COMMON_CODE));
190                     return;
191                 }
192 
193                 // entry to the core functionality.
194                 ErrCode innerErrorCode = context->StartAbility(want);
195                 if (innerErrorCode == ERR_OK) {
196                     task.Resolve(env, CreateJsUndefined(env));
197                 } else {
198                     TAG_LOGE(AAFwkTag::FORM_EXT, "Start failed: %{public}d", innerErrorCode);
199                     task.Reject(env, NapiFormUtil::CreateErrorByInternalErrorCode(env, innerErrorCode));
200                 }
201             };
202 
203         napi_value lastParam = (info.argc == unwrapArgc) ? nullptr : info.argv[unwrapArgc];
204         napi_value result = nullptr;
205         NapiAsyncTask::ScheduleHighQos("JsFormExtensionContext::OnStartAbility",
206             env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
207         return result;
208     }
209 
OnConnectAbility(napi_env env,NapiCallbackInfo & info)210     napi_value OnConnectAbility(napi_env env, NapiCallbackInfo& info)
211     {
212         TAG_LOGD(AAFwkTag::FORM_EXT, "called");
213         if (!CheckCallerIsSystemApp()) {
214             TAG_LOGE(AAFwkTag::FORM_EXT, "not system app");
215             ThrowError(env, AbilityErrorCode::ERROR_CODE_NOT_SYSTEM_APP);
216             return CreateJsUndefined(env);
217         }
218         // Check params count
219         if (info.argc < ARGC_TWO) {
220             TAG_LOGE(AAFwkTag::FORM_EXT, "invalid argc");
221             ThrowTooFewParametersError(env);
222             return CreateJsUndefined(env);
223         }
224         // Unwrap want and connection
225         AAFwk::Want want;
226         sptr<JSFormExtensionConnection> connection = new JSFormExtensionConnection(env);
227         if (!AppExecFwk::UnwrapWant(env, info.argv[0], want) ||
228             !CheckConnectionParam(env, info.argv[1], connection, want)) {
229             ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
230             return CreateJsUndefined(env);
231         }
232         int64_t connectId = connection->GetConnectionId();
233         NapiAsyncTask::CompleteCallback complete =
234             [weak = context_, want, connection, connectId](napi_env env, NapiAsyncTask& task, int32_t status) {
235                 auto context = weak.lock();
236                 if (!context) {
237                     TAG_LOGE(AAFwkTag::FORM_EXT, "Context is free");
238                     task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
239                     RemoveConnection(connectId);
240                     return;
241                 }
242                 TAG_LOGD(AAFwkTag::FORM_EXT, "ConnectAbility connection:%{public}d", static_cast<int32_t>(connectId));
243                 auto innerErrorCode = context->ConnectAbility(want, connection);
244                 int32_t errcode = static_cast<int32_t>(AbilityRuntime::GetJsErrorCodeByNativeError(innerErrorCode));
245                 if (errcode) {
246                     connection->CallJsFailed(errcode);
247                     RemoveConnection(connectId);
248                 }
249                 task.Resolve(env, CreateJsUndefined(env));
250             };
251         napi_value result = nullptr;
252         NapiAsyncTask::ScheduleHighQos("JSFormExtensionConnection::OnConnectAbility",
253             env, CreateAsyncTaskWithLastParam(env, nullptr, nullptr, std::move(complete), &result));
254         return CreateJsValue(env, connectId);
255     }
256 
OnDisconnectAbility(napi_env env,NapiCallbackInfo & info)257     napi_value OnDisconnectAbility(napi_env env, NapiCallbackInfo& info)
258     {
259         TAG_LOGI(AAFwkTag::FORM_EXT, "DisconnectAbility");
260         if (!CheckCallerIsSystemApp()) {
261             TAG_LOGE(AAFwkTag::FORM_EXT, "not system app");
262             ThrowError(env, AbilityErrorCode::ERROR_CODE_NOT_SYSTEM_APP);
263             return CreateJsUndefined(env);
264         }
265         if (info.argc < ARGC_ONE) {
266             TAG_LOGE(AAFwkTag::FORM_EXT, "invalid argc");
267             ThrowTooFewParametersError(env);
268             return CreateJsUndefined(env);
269         }
270         int64_t connectId = -1;
271         if (!AppExecFwk::UnwrapInt64FromJS2(env, info.argv[INDEX_ZERO], connectId)) {
272             ThrowError(env, AbilityErrorCode::ERROR_CODE_INVALID_PARAM);
273             return CreateJsUndefined(env);
274         }
275 
276         AAFwk::Want want;
277         sptr<JSFormExtensionConnection> connection = nullptr;
278         FindConnection(want, connection, connectId);
279         // begin disconnect
280         NapiAsyncTask::CompleteCallback complete =
281             [weak = context_, want, connection](
282                 napi_env env, NapiAsyncTask& task, int32_t status) {
283                 auto context = weak.lock();
284                 if (!context) {
285                     TAG_LOGW(AAFwkTag::FORM_EXT, "Release context");
286                     task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INVALID_CONTEXT));
287                     return;
288                 }
289                 if (connection == nullptr) {
290                     TAG_LOGW(AAFwkTag::FORM_EXT, "Connection null");
291                     task.Reject(env, CreateJsError(env, AbilityErrorCode::ERROR_CODE_INNER));
292                     return;
293                 }
294                 auto innerErrorCode = context->DisconnectAbility(want, connection);
295                 if (innerErrorCode == 0) {
296                     task.Resolve(env, CreateJsUndefined(env));
297                 } else {
298                     task.Reject(env, CreateJsErrorByNativeErr(env, innerErrorCode));
299                 }
300             };
301 
302         napi_value lastParam = (info.argc == ARGC_ONE) ? nullptr : info.argv[INDEX_ONE];
303         napi_value result = nullptr;
304         NapiAsyncTask::Schedule("JSFormExtensionConnection::OnDisconnectAbility",
305             env, CreateAsyncTaskWithLastParam(env, lastParam, nullptr, std::move(complete), &result));
306         return result;
307     }
308 
CheckConnectionParam(napi_env env,napi_value value,sptr<JSFormExtensionConnection> & connection,AAFwk::Want & want) const309     bool CheckConnectionParam(napi_env env, napi_value value,
310         sptr<JSFormExtensionConnection>& connection, AAFwk::Want& want) const
311     {
312         if (!CheckTypeForNapiValue(env, value, napi_object)) {
313             TAG_LOGE(AAFwkTag::FORM_EXT, "get connection object failed");
314             return false;
315         }
316         connection->SetJsConnectionObject(value);
317         ConnectionKey key;
318         key.id = g_serialNumber;
319         key.want = want;
320         connection->SetConnectionId(key.id);
321         g_connects.emplace(key, connection);
322         if (g_serialNumber < INT32_MAX) {
323             g_serialNumber++;
324         } else {
325             g_serialNumber = 0;
326         }
327         TAG_LOGD(AAFwkTag::FORM_EXT, "not find connection");
328         return true;
329     }
330 
FindConnection(AAFwk::Want & want,sptr<JSFormExtensionConnection> & connection,int64_t & connectId) const331     void FindConnection(AAFwk::Want& want, sptr<JSFormExtensionConnection>& connection, int64_t& connectId) const
332     {
333         TAG_LOGD(AAFwkTag::FORM_EXT, "connection:%{public}d", static_cast<int32_t>(connectId));
334         auto item = std::find_if(g_connects.begin(),
335             g_connects.end(),
336             [&connectId](const auto &obj) {
337                 return connectId == obj.first.id;
338             });
339         if (item != g_connects.end()) {
340             // match id
341             want = item->first.want;
342             connection = item->second;
343             TAG_LOGD(AAFwkTag::FORM_EXT, "ability not exist");
344         }
345         return;
346     }
347 };
348 } // namespace
349 
CreateJsFormExtensionContext(napi_env env,std::shared_ptr<FormExtensionContext> context)350 napi_value CreateJsFormExtensionContext(napi_env env, std::shared_ptr<FormExtensionContext> context)
351 {
352     TAG_LOGD(AAFwkTag::FORM_EXT, "called");
353     std::shared_ptr<OHOS::AppExecFwk::AbilityInfo> abilityInfo = nullptr;
354     if (context) {
355         abilityInfo = context->GetAbilityInfo();
356     }
357     napi_value object = CreateJsExtensionContext(env, context, abilityInfo);
358 
359     std::unique_ptr<JsFormExtensionContext> jsContext = std::make_unique<JsFormExtensionContext>(context);
360     napi_wrap(env, object, jsContext.release(), JsFormExtensionContext::Finalizer, nullptr, nullptr);
361 
362     const char *moduleName = "JsFormExtensionContext";
363     BindNativeFunction(env, object, "updateForm", moduleName, JsFormExtensionContext::UpdateForm);
364     BindNativeFunction(env, object, "startAbility", moduleName, JsFormExtensionContext::StartAbility);
365     BindNativeFunction(
366         env, object, "connectServiceExtensionAbility", moduleName, JsFormExtensionContext::ConnectAbility);
367     BindNativeFunction(env, object, "disconnectServiceExtensionAbility",
368         moduleName, JsFormExtensionContext::DisconnectAbility);
369 
370     return object;
371 }
372 
JSFormExtensionConnection(napi_env env)373 JSFormExtensionConnection::JSFormExtensionConnection(napi_env env) : env_(env) {}
374 
~JSFormExtensionConnection()375 JSFormExtensionConnection::~JSFormExtensionConnection()
376 {
377     if (jsConnectionObject_ == nullptr) {
378         return;
379     }
380 
381     uv_loop_t *loop = nullptr;
382     napi_get_uv_event_loop(env_, &loop);
383     if (loop == nullptr) {
384         return;
385     }
386 
387     uv_work_t *work = new (std::nothrow) uv_work_t;
388     if (work == nullptr) {
389         return;
390     }
391     work->data = reinterpret_cast<void *>(jsConnectionObject_.release());
392     int ret = uv_queue_work(loop, work, [](uv_work_t *work) {},
393     [](uv_work_t *work, int status) {
394         if (work == nullptr) {
395             return;
396         }
397         if (work->data == nullptr) {
398             delete work;
399             work = nullptr;
400             return;
401         }
402         delete reinterpret_cast<NativeReference *>(work->data);
403         work->data = nullptr;
404         delete work;
405         work = nullptr;
406     });
407     if (ret != 0) {
408         delete reinterpret_cast<NativeReference *>(work->data);
409         work->data = nullptr;
410         delete work;
411         work = nullptr;
412     }
413 }
414 
SetConnectionId(int64_t id)415 void JSFormExtensionConnection::SetConnectionId(int64_t id)
416 {
417     connectionId_ = id;
418 }
419 
GetConnectionId()420 int64_t JSFormExtensionConnection::GetConnectionId()
421 {
422     return connectionId_;
423 }
424 
OnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)425 void JSFormExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
426     const sptr<IRemoteObject> &remoteObject, int resultCode)
427 {
428     TAG_LOGD(AAFwkTag::FORM_EXT, "called, resultCode:%{public}d", resultCode);
429     wptr<JSFormExtensionConnection> connection = this;
430     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
431         ([connection, element, remoteObject, resultCode](napi_env env, NapiAsyncTask &task, int32_t status) {
432             sptr<JSFormExtensionConnection> connectionSptr = connection.promote();
433             if (!connectionSptr) {
434                 TAG_LOGE(AAFwkTag::FORM_EXT, "connectionSptr null");
435                 return;
436             }
437             connectionSptr->HandleOnAbilityConnectDone(element, remoteObject, resultCode);
438         });
439 
440     napi_ref callback = nullptr;
441     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
442     NapiAsyncTask::Schedule("JSFormExtensionConnection::OnAbilityConnectDone",
443         env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
444 }
445 
HandleOnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)446 void JSFormExtensionConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element,
447     const sptr<IRemoteObject> &remoteObject, int resultCode)
448 {
449     TAG_LOGI(AAFwkTag::FORM_EXT, "called, resultCode:%{public}d", resultCode);
450     // wrap ElementName
451     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
452 
453     // wrap RemoteObject
454     napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(env_, remoteObject);
455     napi_value argv[] = {napiElementName, napiRemoteObject};
456     if (jsConnectionObject_ == nullptr) {
457         TAG_LOGE(AAFwkTag::FORM_EXT, "jsConnectionObject null");
458         return;
459     }
460     napi_value obj = jsConnectionObject_->GetNapiValue();
461     if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
462         TAG_LOGE(AAFwkTag::FORM_EXT, "get object error");
463         return;
464     }
465     napi_value methodOnConnect = nullptr;
466     napi_get_named_property(env_, obj, "onConnect", &methodOnConnect);
467     if (methodOnConnect == nullptr) {
468         TAG_LOGE(AAFwkTag::FORM_EXT, "get onConnect error");
469         return;
470     }
471     napi_call_function(env_, obj, methodOnConnect, ARGC_TWO, argv, nullptr);
472 }
473 
OnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)474 void JSFormExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
475 {
476     TAG_LOGD(AAFwkTag::FORM_EXT, "called, resultCode:%{public}d", resultCode);
477     wptr<JSFormExtensionConnection> connection = this;
478     std::unique_ptr<NapiAsyncTask::CompleteCallback> complete = std::make_unique<NapiAsyncTask::CompleteCallback>
479         ([connection, element, resultCode](napi_env env, NapiAsyncTask &task, int32_t status) {
480             sptr<JSFormExtensionConnection> connectionSptr = connection.promote();
481             if (!connectionSptr) {
482                 TAG_LOGI(AAFwkTag::FORM_EXT, "connectionSptr null");
483                 return;
484             }
485             connectionSptr->HandleOnAbilityDisconnectDone(element, resultCode);
486         });
487     napi_ref callback = nullptr;
488     std::unique_ptr<NapiAsyncTask::ExecuteCallback> execute = nullptr;
489     NapiAsyncTask::Schedule("JSFormExtensionConnection::OnAbilityDisconnectDone",
490         env_, std::make_unique<NapiAsyncTask>(callback, std::move(execute), std::move(complete)));
491 }
492 
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)493 void JSFormExtensionConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
494     int resultCode)
495 {
496     TAG_LOGI(AAFwkTag::FORM_EXT, "called, resultCode:%{public}d", resultCode);
497     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
498     napi_value argv[] = {napiElementName};
499     if (jsConnectionObject_ == nullptr) {
500         TAG_LOGE(AAFwkTag::FORM_EXT, "jsConnectionObject null");
501         return;
502     }
503     napi_value obj = jsConnectionObject_->GetNapiValue();
504     if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
505         TAG_LOGE(AAFwkTag::FORM_EXT, "get object fail");
506         return;
507     }
508 
509     napi_value method = nullptr;
510     napi_get_named_property(env_, obj, "onDisconnect", &method);
511     if (method == nullptr) {
512         TAG_LOGE(AAFwkTag::FORM_EXT, "get onDisconnect error");
513         return;
514     }
515 
516     // release connect
517     TAG_LOGD(AAFwkTag::FORM_EXT, "size:%{public}zu", g_connects.size());
518     std::string bundleName = element.GetBundleName();
519     std::string abilityName = element.GetAbilityName();
520     auto item = std::find_if(g_connects.begin(),
521         g_connects.end(),
522         [bundleName, abilityName, connectionId = connectionId_](
523             const auto &obj) {
524             return (bundleName == obj.first.want.GetBundle()) &&
525                    (abilityName == obj.first.want.GetElement().GetAbilityName()) &&
526                    connectionId == obj.first.id;
527         });
528     if (item != g_connects.end()) {
529         // match bundleName && abilityName
530         g_connects.erase(item);
531         TAG_LOGD(AAFwkTag::FORM_EXT, "erase size:%{public}zu", g_connects.size());
532     }
533     napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr);
534 }
535 
SetJsConnectionObject(napi_value jsConnectionObject)536 void JSFormExtensionConnection::SetJsConnectionObject(napi_value jsConnectionObject)
537 {
538     napi_ref ref = nullptr;
539     napi_create_reference(env_, jsConnectionObject, 1, &ref);
540     jsConnectionObject_ = std::unique_ptr<NativeReference>(reinterpret_cast<NativeReference*>(ref));
541 }
542 
RemoveConnectionObject()543 void JSFormExtensionConnection::RemoveConnectionObject()
544 {
545     jsConnectionObject_.reset();
546 }
547 
CallJsFailed(int32_t errorCode)548 void JSFormExtensionConnection::CallJsFailed(int32_t errorCode)
549 {
550     TAG_LOGD(AAFwkTag::FORM_EXT, "called");
551     if (jsConnectionObject_ == nullptr) {
552         TAG_LOGE(AAFwkTag::FORM_EXT, "jsConnectionObject null");
553         return;
554     }
555     napi_value obj = jsConnectionObject_->GetNapiValue();
556     if (!CheckTypeForNapiValue(env_, obj, napi_object)) {
557         TAG_LOGE(AAFwkTag::FORM_EXT, "get object error");
558         return;
559     }
560 
561     napi_value method = nullptr;
562     napi_get_named_property(env_, obj, "onFailed", &method);
563     if (method == nullptr) {
564         TAG_LOGE(AAFwkTag::FORM_EXT, "get onFailed error");
565         return;
566     }
567     napi_value argv[] = {CreateJsValue(env_, errorCode)};
568     napi_call_function(env_, obj, method, ARGC_ONE, argv, nullptr);
569     TAG_LOGD(AAFwkTag::FORM_EXT, "CallJsFailed exit");
570 }
571 } // namespace AbilityRuntime
572 } // namespace OHOS
573