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