• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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_inputmethod_extension_context.h"
17 
18 #include <cstdint>
19 
20 #include "global.h"
21 #include "js_data_struct_converter.h"
22 #include "js_error_utils.h"
23 #include "js_extension_context.h"
24 #include "js_runtime.h"
25 #include "js_runtime_utils.h"
26 #include "js_util.h"
27 #include "js_utils.h"
28 #include "napi/native_api.h"
29 #include "napi_common_start_options.h"
30 #include "napi_common_util.h"
31 #include "napi_common_want.h"
32 #include "napi_remote_object.h"
33 #include "start_options.h"
34 
35 namespace OHOS {
36 namespace AbilityRuntime {
37 using namespace OHOS::MiscServices;
38 namespace {
39 constexpr int32_t INDEX_ZERO = 0;
40 constexpr int32_t INDEX_ONE = 1;
41 constexpr int32_t INDEX_TWO = 2;
42 constexpr int32_t ERROR_CODE_ONE = 1;
43 constexpr int32_t ERROR_CODE_TWO = 2;
44 constexpr size_t ARGC_ZERO = 0;
45 constexpr size_t ARGC_ONE = 1;
46 constexpr size_t ARGC_TWO = 2;
47 constexpr size_t ARGC_THREE = 3;
48 constexpr size_t ARGC_FOUR = 4;
49 
50 class JsInputMethodExtensionContext final {
51 public:
JsInputMethodExtensionContext(const std::shared_ptr<InputMethodExtensionContext> & context)52     explicit JsInputMethodExtensionContext(const std::shared_ptr<InputMethodExtensionContext> &context)
53         : context_(context)
54     {
55     }
56     ~JsInputMethodExtensionContext() = default;
57     JsInputMethodExtensionContext() = default;
58 
Finalizer(napi_env env,void * data,void * hint)59     static void Finalizer(napi_env env, void *data, void *hint)
60     {
61         IMSA_HILOGI("JsInputMethodExtensionContext::Finalizer is called.");
62         std::unique_ptr<JsInputMethodExtensionContext>(static_cast<JsInputMethodExtensionContext *>(data));
63     }
64 
StartAbility(napi_env env,napi_callback_info info)65     static napi_value StartAbility(napi_env env, napi_callback_info info)
66     {
67         GET_CB_INFO_AND_CALL(env, info, JsInputMethodExtensionContext, OnStartAbility);
68     }
69 
StartAbilityWithAccount(napi_env env,napi_callback_info info)70     static napi_value StartAbilityWithAccount(napi_env env, napi_callback_info info)
71     {
72         GET_CB_INFO_AND_CALL(env, info, JsInputMethodExtensionContext, OnStartAbilityWithAccount);
73     }
74 
ConnectAbilityWithAccount(napi_env env,napi_callback_info info)75     static napi_value ConnectAbilityWithAccount(napi_env env, napi_callback_info info)
76     {
77         GET_CB_INFO_AND_CALL(env, info, JsInputMethodExtensionContext, OnConnectAbilityWithAccount);
78     }
79 
TerminateAbility(napi_env env,napi_callback_info info)80     static napi_value TerminateAbility(napi_env env, napi_callback_info info)
81     {
82         GET_CB_INFO_AND_CALL(env, info, JsInputMethodExtensionContext, OnTerminateAbility);
83     }
84 
ConnectAbility(napi_env env,napi_callback_info info)85     static napi_value ConnectAbility(napi_env env, napi_callback_info info)
86     {
87         GET_CB_INFO_AND_CALL(env, info, JsInputMethodExtensionContext, OnConnectAbility);
88     }
89 
DisconnectAbility(napi_env env,napi_callback_info info)90     static napi_value DisconnectAbility(napi_env env, napi_callback_info info)
91     {
92         GET_CB_INFO_AND_CALL(env, info, JsInputMethodExtensionContext, OnDisconnectAbility);
93     }
94 
95 private:
96     std::weak_ptr<InputMethodExtensionContext> context_;
97 
OnStartAbility(napi_env env,size_t argc,napi_value * argv)98     napi_value OnStartAbility(napi_env env, size_t argc, napi_value *argv)
99     {
100         IMSA_HILOGI("InputMethodExtensionContext OnStartAbility.");
101         // only support one or two or three params
102         PARAM_CHECK_RETURN(env, argc == ARGC_ONE || argc == ARGC_TWO || argc == ARGC_THREE,
103             "number of param should in [1,3]", TYPE_NONE, CreateJsUndefined(env));
104         PARAM_CHECK_RETURN(env, JsUtil::GetType(env, argv[0]) == napi_object, "param want type must be Want", TYPE_NONE,
105             JsUtil::Const::Null(env));
106         decltype(argc) unwrapArgc = 0;
107         AAFwk::Want want;
108         OHOS::AppExecFwk::UnwrapWant(env, argv[INDEX_ZERO], want);
109         IMSA_HILOGI("bundleName:%{public}s abilityName:%{public}s", want.GetBundle().c_str(),
110             want.GetElement().GetAbilityName().c_str());
111         unwrapArgc++;
112         AAFwk::StartOptions startOptions;
113         if (argc > ARGC_ONE) {
114             napi_valuetype valueType = napi_undefined;
115             napi_typeof(env, argv[INDEX_ONE], &valueType);
116             if (valueType == napi_object) {
117                 IMSA_HILOGI("OnStartAbility start options is used.");
118                 AppExecFwk::UnwrapStartOptions(env, argv[INDEX_ONE], startOptions);
119                 unwrapArgc++;
120             }
121         }
122         napi_value lastParam = argc > unwrapArgc ? argv[unwrapArgc] : nullptr;
123         napi_value result = nullptr;
124         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
125         auto asyncTask = [weak = context_, want, startOptions, unwrapArgc, env, task = napiAsyncTask.get()]() {
126             IMSA_HILOGI("startAbility start.");
127             auto context = weak.lock();
128             if (context == nullptr) {
129                 IMSA_HILOGW("context is released.");
130                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
131                 delete task;
132                 return;
133             }
134             ErrCode errcode = ERR_OK;
135             (unwrapArgc == 1) ? errcode = context->StartAbility(want)
136                               : errcode = context->StartAbility(want, startOptions);
137             if (errcode == 0) {
138                 task->Resolve(env, CreateJsUndefined(env));
139             } else {
140                 task->Reject(env, CreateJsErrorByNativeErr(env, errcode));
141             }
142             delete task;
143         };
144         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
145             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
146         } else {
147             napiAsyncTask.release();
148         }
149         return result;
150     }
151 
OnStartAbilityWithAccount(napi_env env,size_t argc,napi_value * argv)152     napi_value OnStartAbilityWithAccount(napi_env env, size_t argc, napi_value *argv)
153     {
154         // only support two or three or four params
155         CHECK_RETURN(argc == ARGC_TWO || argc == ARGC_THREE || argc == ARGC_FOUR,
156             "not enough params!", CreateJsUndefined(env));
157         decltype(argc) unwrapArgc = 0;
158         AAFwk::Want want;
159         OHOS::AppExecFwk::UnwrapWant(env, argv[INDEX_ZERO], want);
160         unwrapArgc++;
161         int32_t accountId = 0;
162         if (!OHOS::AppExecFwk::UnwrapInt32FromJS2(env, argv[INDEX_ONE], accountId)) {
163             IMSA_HILOGI("%{public}s called, the second parameter is invalid.", __func__);
164             return CreateJsUndefined(env);
165         }
166         IMSA_HILOGI("bundleName: %{public}s abilityName: %{public}s, accountId: %{public}d", want.GetBundle().c_str(),
167             want.GetElement().GetAbilityName().c_str(), accountId);
168         unwrapArgc++;
169         AAFwk::StartOptions startOptions;
170         napi_valuetype valueType = napi_undefined;
171         napi_typeof(env, argv[INDEX_ONE], &valueType);
172         if (argc > ARGC_TWO && valueType == napi_object) {
173             AppExecFwk::UnwrapStartOptions(env, argv[INDEX_TWO], startOptions);
174             unwrapArgc++;
175         }
176         napi_value lastParam = argc == unwrapArgc ? nullptr : argv[unwrapArgc];
177         napi_value result = nullptr;
178         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
179         auto asyncTask = [weak = context_, want, accountId, startOptions, unwrapArgc, env,
180             task = napiAsyncTask.get()]() {
181             IMSA_HILOGI("startAbility start");
182             auto context = weak.lock();
183             if (context == nullptr) {
184                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
185                 delete task;
186                 return;
187             }
188             ErrCode errcode = (unwrapArgc == ARGC_TWO)
189                                   ? context->StartAbilityWithAccount(want, accountId)
190                                   : context->StartAbilityWithAccount(want, accountId, startOptions);
191             if (errcode == 0) {
192                 task->Resolve(env, CreateJsUndefined(env));
193             }
194             task->Reject(env, CreateJsError(env, errcode, "Start Ability failed."));
195             delete task;
196         };
197         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
198             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
199         } else {
200             napiAsyncTask.release();
201         }
202         return result;
203     }
204 
OnTerminateAbility(napi_env env,size_t argc,napi_value * argv)205     napi_value OnTerminateAbility(napi_env env, size_t argc, napi_value *argv)
206     {
207         IMSA_HILOGI("OnTerminateAbility is called.");
208         // only support one or zero params
209         if (argc != ARGC_ZERO && argc != ARGC_ONE) {
210             IMSA_HILOGE("not enough params!");
211             return CreateJsUndefined(env);
212         }
213         napi_value lastParam = argc == ARGC_ZERO ? nullptr : argv[INDEX_ZERO];
214         napi_value result = nullptr;
215         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
216         auto asyncTask = [weak = context_, env, task = napiAsyncTask.get()]() {
217             IMSA_HILOGI("TerminateAbility start.");
218             auto context = weak.lock();
219             if (context == nullptr) {
220                 IMSA_HILOGW("context is released.");
221                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
222                 delete task;
223                 return;
224             }
225 
226             auto errcode = context->TerminateAbility();
227             if (errcode == 0) {
228                 task->Resolve(env, CreateJsUndefined(env));
229             } else {
230                 task->Reject(env, CreateJsError(env, errcode, "Terminate Ability failed."));
231             }
232             delete task;
233         };
234         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
235             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
236         } else {
237             napiAsyncTask.release();
238         }
239         return result;
240     }
241 
OnConnectAbility(napi_env env,size_t argc,napi_value * argv)242     napi_value OnConnectAbility(napi_env env, size_t argc, napi_value *argv)
243     {
244         IMSA_HILOGI("OnConnectAbility start.");
245         // only support two params
246         CHECK_RETURN(argc == ARGC_TWO, "not enough params!", CreateJsUndefined(env));
247         AAFwk::Want want;
248         OHOS::AppExecFwk::UnwrapWant(env, argv[INDEX_ZERO], want);
249         IMSA_HILOGI("%{public}s bundleName: %{public}s abilityName: %{public}s", __func__, want.GetBundle().c_str(),
250             want.GetElement().GetAbilityName().c_str());
251         sptr<JSInputMethodExtensionConnection> connection = new (std::nothrow) JSInputMethodExtensionConnection(env);
252         CHECK_RETURN(connection != nullptr, "connection is nullptr", CreateJsUndefined(env));
253         connection->SetJsConnectionObject(argv[1]);
254         int64_t connectId = serialNumber_;
255         ConnectionKey key = { want, serialNumber_ };
256         {
257             std::lock_guard<std::mutex> lock(g_connectMapMtx);
258             connects_.emplace(key, connection);
259         }
260         if (serialNumber_ < INT64_MAX) {
261             serialNumber_++;
262         } else {
263             serialNumber_ = 0;
264         }
265         napi_value result = nullptr;
266         napi_value lastParam = nullptr;
267         napi_value connectResult = nullptr;
268         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
269         auto asyncTask = [weak = context_, want, connection, connectId, env, task = napiAsyncTask.get()]() {
270             IMSA_HILOGI("OnConnectAbility start.");
271             auto context = weak.lock();
272             if (context == nullptr) {
273                 IMSA_HILOGW("context is released.");
274                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
275                 delete task;
276                 return;
277             }
278             IMSA_HILOGI("context->ConnectAbility connection: %{public}d.", static_cast<int32_t>(connectId));
279             if (!context->ConnectAbility(want, connection)) {
280                 connection->CallJsFailed(ERROR_CODE_ONE);
281             }
282             task->Resolve(env, CreateJsUndefined(env));
283             delete task;
284         };
285         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
286             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
287         } else {
288             napiAsyncTask.release();
289         }
290         napi_create_int64(env, connectId, &connectResult);
291         return connectResult;
292     }
293 
CheckSerialNumber(int64_t & serialNumber)294     void CheckSerialNumber(int64_t &serialNumber)
295     {
296         if (serialNumber < INT64_MAX) {
297             serialNumber++;
298         } else {
299             serialNumber = 0;
300         }
301     }
OnConnectAbilityWithAccount(napi_env env,size_t argc,napi_value * argv)302     napi_value OnConnectAbilityWithAccount(napi_env env, size_t argc, napi_value *argv)
303     {
304         CHECK_RETURN(argc == ARGC_THREE, "not enough params!", CreateJsUndefined(env));
305         AAFwk::Want want;
306         OHOS::AppExecFwk::UnwrapWant(env, argv[INDEX_ZERO], want);
307         IMSA_HILOGI("%{public}s bundleName: %{public}s, abilityName: %{public}s", __func__, want.GetBundle().c_str(),
308             want.GetElement().GetAbilityName().c_str());
309         int32_t accountId = 0;
310         if (!OHOS::AppExecFwk::UnwrapInt32FromJS2(env, argv[INDEX_ONE], accountId)) {
311             IMSA_HILOGI("%{public}s called, the second parameter is invalid.", __func__);
312             return CreateJsUndefined(env);
313         }
314         sptr<JSInputMethodExtensionConnection> connection = new (std::nothrow) JSInputMethodExtensionConnection(env);
315         CHECK_RETURN(connection != nullptr, "connection is nullptr", CreateJsUndefined(env));
316         connection->SetJsConnectionObject(argv[1]);
317         int64_t connectId = serialNumber_;
318         ConnectionKey key = { want, serialNumber_ };
319         {
320             std::lock_guard<std::mutex> lock(g_connectMapMtx);
321             connects_.emplace(key, connection);
322         }
323         CheckSerialNumber(serialNumber_);
324         napi_value result = nullptr;
325         napi_value lastParam = nullptr;
326         napi_value connectResult = nullptr;
327 
328         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
329         auto asyncTask = [weak = context_, want, accountId, connection, connectId, env, task = napiAsyncTask.get()]() {
330             auto context = weak.lock();
331             if (context == nullptr) {
332                 IMSA_HILOGW("context is released.");
333                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
334                 delete task;
335                 return;
336             }
337             IMSA_HILOGI("context->ConnectAbilityWithAccount connection:%{public}d.", static_cast<int32_t>(connectId));
338             if (!context->ConnectAbilityWithAccount(want, accountId, connection)) {
339                 connection->CallJsFailed(ERROR_CODE_ONE);
340             }
341             task->Resolve(env, CreateJsUndefined(env));
342             delete task;
343         };
344         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
345             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
346         } else {
347             napiAsyncTask.release();
348         }
349         napi_create_int64(env, connectId, &connectResult);
350         return connectResult;
351     }
352 
OnDisconnectAbility(napi_env env,size_t argc,napi_value * argv)353     napi_value OnDisconnectAbility(napi_env env, size_t argc, napi_value *argv)
354     {
355         IMSA_HILOGI("OnDisconnectAbility is called.");
356         // only support one or two params
357         CHECK_RETURN(argc == ARGC_ONE || argc == ARGC_TWO, "not enough params!", CreateJsUndefined(env));
358         AAFwk::Want want;
359         int64_t connectId = -1;
360         sptr<JSInputMethodExtensionConnection> connection = nullptr;
361         napi_get_value_int64(env, argv[INDEX_ZERO], &connectId);
362         IMSA_HILOGI("OnDisconnectAbility connection: %{public}d.", static_cast<int32_t>(connectId));
363         {
364             std::lock_guard<std::mutex> lock(g_connectMapMtx);
365             auto item = std::find_if(connects_.begin(), connects_.end(),
366                 [&connectId](const std::map<ConnectionKey, sptr<JSInputMethodExtensionConnection>>::value_type &obj) {
367                     return connectId == obj.first.id;
368                 });
369             if (item != connects_.end()) {
370                 // match id
371                 want = item->first.want;
372                 connection = item->second;
373             }
374         }
375         // begin disconnect
376         napi_value lastParam = argc == ARGC_ONE ? nullptr : argv[INDEX_ONE];
377         napi_value result = nullptr;
378         std::unique_ptr<NapiAsyncTask> napiAsyncTask = CreateEmptyAsyncTask(env, lastParam, &result);
379         auto asyncTask = [weak = context_, want, connection, env, task = napiAsyncTask.get()]() {
380             IMSA_HILOGI("OnDisconnectAbility start.");
381             auto context = weak.lock();
382             if (context == nullptr) {
383                 IMSA_HILOGW("context is released.");
384                 task->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "Context is released"));
385                 delete task;
386                 return;
387             }
388             if (connection == nullptr) {
389                 IMSA_HILOGW("connection is nullptr.");
390                 task->Reject(env, CreateJsError(env, ERROR_CODE_TWO, "not found connection"));
391                 delete task;
392                 return;
393             }
394             IMSA_HILOGI("context->DisconnectAbility.");
395             auto errcode = context->DisconnectAbility(want, connection);
396             errcode == 0 ? task->Resolve(env, CreateJsUndefined(env))
397                          : task->Reject(env, CreateJsError(env, errcode, "Disconnect Ability failed."));
398             delete task;
399         };
400         if (napi_send_event(env, asyncTask, napi_eprio_high) != napi_status::napi_ok) {
401             napiAsyncTask->Reject(env, CreateJsError(env, ERROR_CODE_ONE, "send event failed"));
402         } else {
403             napiAsyncTask.release();
404         }
405         return result;
406     }
407 };
408 } // namespace
409 
CreateJsMetadata(napi_env env,const AppExecFwk::Metadata & info)410 napi_value CreateJsMetadata(napi_env env, const AppExecFwk::Metadata &info)
411 {
412     IMSA_HILOGI("CreateJsMetadata start.");
413 
414     napi_value objValue = nullptr;
415     napi_create_object(env, &objValue);
416 
417     napi_set_named_property(env, objValue, "name", CreateJsValue(env, info.name));
418     napi_set_named_property(env, objValue, "value", CreateJsValue(env, info.value));
419     napi_set_named_property(env, objValue, "resource", CreateJsValue(env, info.resource));
420     return objValue;
421 }
422 
CreateJsMetadataArray(napi_env env,const std::vector<AppExecFwk::Metadata> & info)423 napi_value CreateJsMetadataArray(napi_env env, const std::vector<AppExecFwk::Metadata> &info)
424 {
425     IMSA_HILOGI("CreateJsMetadataArray start.");
426     napi_value arrayValue = nullptr;
427     napi_create_array_with_length(env, info.size(), &arrayValue);
428     uint32_t index = 0;
429     for (const auto &item : info) {
430         napi_set_element(env, arrayValue, index++, CreateJsMetadata(env, item));
431     }
432     return arrayValue;
433 }
434 
CreateJsExtensionAbilityInfo(napi_env env,const AppExecFwk::ExtensionAbilityInfo & info)435 napi_value CreateJsExtensionAbilityInfo(napi_env env, const AppExecFwk::ExtensionAbilityInfo &info)
436 {
437     IMSA_HILOGI("CreateJsExtensionAbilityInfo start.");
438     napi_value objValue = nullptr;
439     napi_create_object(env, &objValue);
440 
441     napi_set_named_property(env, objValue, "bundleName", CreateJsValue(env, info.bundleName));
442     napi_set_named_property(env, objValue, "moduleName", CreateJsValue(env, info.moduleName));
443     napi_set_named_property(env, objValue, "name", CreateJsValue(env, info.name));
444     napi_set_named_property(env, objValue, "labelId", CreateJsValue(env, info.labelId));
445     napi_set_named_property(env, objValue, "descriptionId", CreateJsValue(env, info.descriptionId));
446     napi_set_named_property(env, objValue, "iconId", CreateJsValue(env, info.iconId));
447     napi_set_named_property(env, objValue, "isVisible", CreateJsValue(env, info.visible));
448     napi_set_named_property(env, objValue, "extensionAbilityType", CreateJsValue(env, info.type));
449 
450     napi_value permissionArray = nullptr;
451     napi_create_array_with_length(env, info.permissions.size(), &permissionArray);
452 
453     if (permissionArray != nullptr) {
454         int index = 0;
455         for (auto permission : info.permissions) {
456             napi_set_element(env, permissionArray, index++, CreateJsValue(env, permission));
457         }
458     }
459     napi_set_named_property(env, objValue, "permissions", permissionArray);
460     napi_set_named_property(env, objValue, "applicationInfo", CreateJsApplicationInfo(env, info.applicationInfo));
461     napi_set_named_property(env, objValue, "metadata", CreateJsMetadataArray(env, info.metadata));
462     napi_set_named_property(env, objValue, "enabled", CreateJsValue(env, info.enabled));
463     napi_set_named_property(env, objValue, "readPermission", CreateJsValue(env, info.readPermission));
464     napi_set_named_property(env, objValue, "writePermission", CreateJsValue(env, info.writePermission));
465     return objValue;
466 }
467 
CreateJsInputMethodExtensionContext(napi_env env,std::shared_ptr<InputMethodExtensionContext> context)468 napi_value CreateJsInputMethodExtensionContext(napi_env env, std::shared_ptr<InputMethodExtensionContext> context)
469 {
470     IMSA_HILOGI("CreateJsInputMethodExtensionContext start.");
471     if (context != nullptr) {
472         auto abilityInfo = context->GetAbilityInfo();
473     }
474 
475     napi_value objValue = CreateJsExtensionContext(env, context);
476     std::unique_ptr<JsInputMethodExtensionContext> jsContext = std::make_unique<JsInputMethodExtensionContext>(context);
477     napi_wrap(env, objValue, jsContext.release(), JsInputMethodExtensionContext::Finalizer, nullptr, nullptr);
478 
479     const char *moduleName = "JsInputMethodExtensionContext";
480     BindNativeFunction(env, objValue, "startAbility", moduleName, JsInputMethodExtensionContext::StartAbility);
481     BindNativeFunction(env, objValue, "terminateSelf", moduleName, JsInputMethodExtensionContext::TerminateAbility);
482     BindNativeFunction(env, objValue, "destroy", moduleName, JsInputMethodExtensionContext::TerminateAbility);
483     BindNativeFunction(env, objValue, "connectAbility", moduleName, JsInputMethodExtensionContext::ConnectAbility);
484     BindNativeFunction(env, objValue, "disconnectAbility", moduleName,
485         JsInputMethodExtensionContext::DisconnectAbility);
486     BindNativeFunction(env, objValue, "startAbilityWithAccount", moduleName,
487         JsInputMethodExtensionContext::StartAbilityWithAccount);
488     BindNativeFunction(env, objValue, "connectAbilityWithAccount", moduleName,
489         JsInputMethodExtensionContext::ConnectAbilityWithAccount);
490     return objValue;
491 }
492 
JSInputMethodExtensionConnection(napi_env env)493 JSInputMethodExtensionConnection::JSInputMethodExtensionConnection(napi_env env) : env_(env),
494     handler_(std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::GetMainEventRunner()))
495 {
496 }
497 
498 JSInputMethodExtensionConnection::~JSInputMethodExtensionConnection() = default;
499 
OnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)500 void JSInputMethodExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
501     const sptr<IRemoteObject> &remoteObject, int resultCode)
502 {
503     IMSA_HILOGI("OnAbilityConnectDone start, resultCode: %{public}d.", resultCode);
504     if (handler_ == nullptr) {
505         IMSA_HILOGI("handler_ is nullptr.");
506         return;
507     }
508     wptr<JSInputMethodExtensionConnection> connection = this;
509     auto task = [connection, element, remoteObject, resultCode]() {
510         sptr<JSInputMethodExtensionConnection> connectionSptr = connection.promote();
511         if (connectionSptr == nullptr) {
512             IMSA_HILOGE("connectionSptr is nullptr.");
513             return;
514         }
515         connectionSptr->HandleOnAbilityConnectDone(element, remoteObject, resultCode);
516     };
517     handler_->PostTask(task, "OnAbilityConnectDone", 0, AppExecFwk::EventQueue::Priority::VIP);
518 }
519 
HandleOnAbilityConnectDone(const AppExecFwk::ElementName & element,const sptr<IRemoteObject> & remoteObject,int resultCode)520 void JSInputMethodExtensionConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element,
521     const sptr<IRemoteObject> &remoteObject, int resultCode)
522 {
523     IMSA_HILOGI("HandleOnAbilityConnectDone start, resultCode:%{public}d.", resultCode);
524     // wrap ElementName
525     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
526 
527     // wrap RemoteObject
528     IMSA_HILOGI("OnAbilityConnectDone start NAPI_ohos_rpc_CreateJsRemoteObject.");
529     napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(env_, remoteObject);
530     napi_value argv[] = { napiElementName, napiRemoteObject };
531 
532     if (jsConnectionObject_ == nullptr) {
533         IMSA_HILOGE("jsConnectionObject_ is nullptr!");
534         return;
535     }
536 
537     napi_value obj = nullptr;
538     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
539         IMSA_HILOGE("failed to get jsConnectionObject_!");
540         return;
541     }
542     if (obj == nullptr) {
543         IMSA_HILOGE("failed to get object!");
544         return;
545     }
546     napi_value methodOnConnect = nullptr;
547     napi_get_named_property(env_, obj, "onConnect", &methodOnConnect);
548     if (methodOnConnect == nullptr) {
549         IMSA_HILOGE("failed to get onConnect from object!");
550         return;
551     }
552     IMSA_HILOGI("JSInputMethodExtensionConnection::CallFunction onConnect, success.");
553     napi_value callResult = nullptr;
554     napi_call_function(env_, obj, methodOnConnect, ARGC_TWO, argv, &callResult);
555     IMSA_HILOGI("OnAbilityConnectDone end.");
556 }
557 
OnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)558 void JSInputMethodExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
559 {
560     IMSA_HILOGI("OnAbilityDisconnectDone start, resultCode: %{public}d.", resultCode);
561     if (handler_ == nullptr) {
562         IMSA_HILOGI("handler_ is nullptr.");
563         return;
564     }
565     wptr<JSInputMethodExtensionConnection> connection = this;
566     auto task = [connection, element, resultCode]() {
567         sptr<JSInputMethodExtensionConnection> connectionSptr = connection.promote();
568         if (!connectionSptr) {
569             IMSA_HILOGE("connectionSptr is nullptr.");
570             return;
571         }
572         connectionSptr->HandleOnAbilityDisconnectDone(element, resultCode);
573     };
574     handler_->PostTask(task, "OnAbilityDisconnectDone", 0, AppExecFwk::EventQueue::Priority::VIP);
575 }
576 
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName & element,int resultCode)577 void JSInputMethodExtensionConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
578     int resultCode)
579 {
580     IMSA_HILOGI("HandleOnAbilityDisconnectDone start, resultCode:%{public}d.", resultCode);
581     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
582     napi_value argv[] = { napiElementName };
583     if (jsConnectionObject_ == nullptr) {
584         IMSA_HILOGE("jsConnectionObject_ is nullptr!");
585         return;
586     }
587     napi_value obj = nullptr;
588     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
589         IMSA_HILOGE("failed to get jsConnectionObject_!");
590         return;
591     }
592     if (obj == nullptr) {
593         IMSA_HILOGE("failed to get object!");
594         return;
595     }
596     napi_value method = nullptr;
597     napi_get_named_property(env_, obj, "onDisconnect", &method);
598     if (method == nullptr) {
599         IMSA_HILOGE("failed to get onDisconnect from object!");
600         return;
601     }
602     // release connect
603     std::string bundleName = element.GetBundleName();
604     std::string abilityName = element.GetAbilityName();
605     {
606         std::lock_guard<std::mutex> lock(g_connectMapMtx);
607         IMSA_HILOGI("OnAbilityDisconnectDone connects_.size: %{public}zu.", connects_.size());
608         auto item = std::find_if(connects_.begin(), connects_.end(),
609             [bundleName, abilityName](
610                 const std::map<ConnectionKey, sptr<JSInputMethodExtensionConnection>>::value_type &obj) {
611                 return (bundleName == obj.first.want.GetBundle()) &&
612                        (abilityName == obj.first.want.GetElement().GetAbilityName());
613             });
614         if (item != connects_.end()) {
615             // match bundleName && abilityName
616             if (item->second != nullptr) {
617                 item->second->ReleaseConnection();
618             }
619             connects_.erase(item);
620             IMSA_HILOGI("OnAbilityDisconnectDone erase connects_.size: %{public}zu.", connects_.size());
621         }
622     }
623     IMSA_HILOGI("OnAbilityDisconnectDone CallFunction success.");
624     napi_value callResult = nullptr;
625     napi_call_function(env_, obj, method, ARGC_ONE, argv, &callResult);
626 }
627 
SetJsConnectionObject(napi_value jsConnectionObject)628 void JSInputMethodExtensionConnection::SetJsConnectionObject(napi_value jsConnectionObject)
629 {
630     napi_create_reference(env_, jsConnectionObject, 1, &jsConnectionObject_);
631 }
632 
CallJsFailed(int32_t errorCode)633 void JSInputMethodExtensionConnection::CallJsFailed(int32_t errorCode)
634 {
635     IMSA_HILOGI("CallJsFailed start");
636     if (jsConnectionObject_ == nullptr) {
637         IMSA_HILOGE("jsConnectionObject_ is nullptr!");
638         return;
639     }
640     napi_value obj = nullptr;
641     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
642         IMSA_HILOGE("failed to get jsConnectionObject_!");
643         return;
644     }
645     if (obj == nullptr) {
646         IMSA_HILOGE("failed to get object.");
647         return;
648     }
649 
650     napi_value method = nullptr;
651     napi_get_named_property(env_, obj, "onFailed", &method);
652     if (method == nullptr) {
653         IMSA_HILOGE("failed to get onFailed from object!");
654         return;
655     }
656     napi_value result = nullptr;
657     napi_create_int32(env_, errorCode, &result);
658     napi_value argv[] = { result };
659     IMSA_HILOGI("CallJsFailed CallFunction success.");
660     napi_value callResult = nullptr;
661     napi_call_function(env_, obj, method, ARGC_ONE, argv, &callResult);
662     IMSA_HILOGI("CallJsFailed end.");
663 }
664 
ReleaseConnection()665 void JSInputMethodExtensionConnection::ReleaseConnection()
666 {
667     IMSA_HILOGD("ReleaseConnection");
668     if (jsConnectionObject_ != nullptr) {
669         napi_delete_reference(env_, jsConnectionObject_);
670         env_ = nullptr;
671         jsConnectionObject_ = nullptr;
672     }
673 }
674 } // namespace AbilityRuntime
675 } // namespace OHOS
676