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,
317                                                        connectId](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(napi_env env, std::shared_ptr<InputMethodExtensionContext> context)
451 {
452     IMSA_HILOGI("CreateJsInputMethodExtensionContext start.");
453     if (context != nullptr) {
454         auto abilityInfo = context->GetAbilityInfo();
455     }
456 
457     napi_value objValue = CreateJsExtensionContext(env, context);
458     std::unique_ptr<JsInputMethodExtensionContext> jsContext = std::make_unique<JsInputMethodExtensionContext>(context);
459     napi_wrap(env, objValue, jsContext.release(), JsInputMethodExtensionContext::Finalizer, nullptr, nullptr);
460 
461     const char *moduleName = "JsInputMethodExtensionContext";
462     BindNativeFunction(env, objValue, "startAbility", moduleName, JsInputMethodExtensionContext::StartAbility);
463     BindNativeFunction(env, objValue, "terminateSelf", moduleName, JsInputMethodExtensionContext::TerminateAbility);
464     BindNativeFunction(env, objValue, "destroy", moduleName, JsInputMethodExtensionContext::TerminateAbility);
465     BindNativeFunction(env, objValue, "connectAbility", moduleName, JsInputMethodExtensionContext::ConnectAbility);
466     BindNativeFunction(env, objValue, "disconnectAbility", moduleName,
467         JsInputMethodExtensionContext::DisconnectAbility);
468     BindNativeFunction(env, objValue, "startAbilityWithAccount", moduleName,
469         JsInputMethodExtensionContext::StartAbilityWithAccount);
470     BindNativeFunction(env, objValue, "connectAbilityWithAccount", moduleName,
471         JsInputMethodExtensionContext::ConnectAbilityWithAccount);
472     return objValue;
473 }
474 
JSInputMethodExtensionConnection(napi_env env)475 JSInputMethodExtensionConnection::JSInputMethodExtensionConnection(napi_env env) : env_(env),
476     handler_(std::make_shared<AppExecFwk::EventHandler>(AppExecFwk::EventRunner::GetMainEventRunner()))
477 {
478 }
479 
480 JSInputMethodExtensionConnection::~JSInputMethodExtensionConnection() = default;
481 
OnAbilityConnectDone(const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int resultCode)482 void JSInputMethodExtensionConnection::OnAbilityConnectDone(const AppExecFwk::ElementName &element,
483     const sptr<IRemoteObject> &remoteObject, int resultCode)
484 {
485     IMSA_HILOGI("OnAbilityConnectDone start, resultCode: %{public}d.", resultCode);
486     if (handler_ == nullptr) {
487         IMSA_HILOGI("handler_ is nullptr.");
488         return;
489     }
490     wptr<JSInputMethodExtensionConnection> connection = this;
491     auto task = [connection, element, remoteObject, resultCode]() {
492         sptr<JSInputMethodExtensionConnection> connectionSptr = connection.promote();
493         if (connectionSptr == nullptr) {
494             IMSA_HILOGE("connectionSptr is nullptr.");
495             return;
496         }
497         connectionSptr->HandleOnAbilityConnectDone(element, remoteObject, resultCode);
498     };
499     handler_->PostTask(task, "OnAbilityConnectDone");
500 }
501 
HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element, const sptr<IRemoteObject> &remoteObject, int resultCode)502 void JSInputMethodExtensionConnection::HandleOnAbilityConnectDone(const AppExecFwk::ElementName &element,
503     const sptr<IRemoteObject> &remoteObject, int resultCode)
504 {
505     IMSA_HILOGI("HandleOnAbilityConnectDone start, resultCode:%{public}d.", resultCode);
506     // wrap ElementName
507     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
508 
509     // wrap RemoteObject
510     IMSA_HILOGI("OnAbilityConnectDone start NAPI_ohos_rpc_CreateJsRemoteObject.");
511     napi_value napiRemoteObject = NAPI_ohos_rpc_CreateJsRemoteObject(env_, remoteObject);
512     napi_value argv[] = { napiElementName, napiRemoteObject };
513 
514     if (jsConnectionObject_ == nullptr) {
515         IMSA_HILOGE("jsConnectionObject_ is nullptr!");
516         return;
517     }
518 
519     napi_value obj = nullptr;
520     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
521         IMSA_HILOGE("failed to get jsConnectionObject_!");
522         return;
523     }
524     if (obj == nullptr) {
525         IMSA_HILOGE("failed to get object!");
526         return;
527     }
528     napi_value methodOnConnect = nullptr;
529     napi_get_named_property(env_, obj, "onConnect", &methodOnConnect);
530     if (methodOnConnect == nullptr) {
531         IMSA_HILOGE("failed to get onConnect from object!");
532         return;
533     }
534     IMSA_HILOGI("JSInputMethodExtensionConnection::CallFunction onConnect, success.");
535     napi_value callResult = nullptr;
536     napi_call_function(env_, obj, methodOnConnect, ARGC_TWO, argv, &callResult);
537     IMSA_HILOGI("OnAbilityConnectDone end.");
538 }
539 
OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)540 void JSInputMethodExtensionConnection::OnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)
541 {
542     IMSA_HILOGI("OnAbilityDisconnectDone start, resultCode: %{public}d.", resultCode);
543     if (handler_ == nullptr) {
544         IMSA_HILOGI("handler_ is nullptr.");
545         return;
546     }
547     wptr<JSInputMethodExtensionConnection> connection = this;
548     auto task = [connection, element, resultCode]() {
549         sptr<JSInputMethodExtensionConnection> connectionSptr = connection.promote();
550         if (!connectionSptr) {
551             IMSA_HILOGE("connectionSptr is nullptr.");
552             return;
553         }
554         connectionSptr->HandleOnAbilityDisconnectDone(element, resultCode);
555     };
556     handler_->PostTask(task, "OnAbilityDisconnectDone");
557 }
558 
HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element, int resultCode)559 void JSInputMethodExtensionConnection::HandleOnAbilityDisconnectDone(const AppExecFwk::ElementName &element,
560     int resultCode)
561 {
562     IMSA_HILOGI("HandleOnAbilityDisconnectDone start, resultCode:%{public}d.", resultCode);
563     napi_value napiElementName = OHOS::AppExecFwk::WrapElementName(env_, element);
564     napi_value argv[] = { napiElementName };
565     if (jsConnectionObject_ == nullptr) {
566         IMSA_HILOGE("jsConnectionObject_ is nullptr!");
567         return;
568     }
569     napi_value obj = nullptr;
570     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
571         IMSA_HILOGE("failed to get jsConnectionObject_!");
572         return;
573     }
574     if (obj == nullptr) {
575         IMSA_HILOGE("failed to get object!");
576         return;
577     }
578     napi_value method = nullptr;
579     napi_get_named_property(env_, obj, "onDisconnect", &method);
580     if (method == nullptr) {
581         IMSA_HILOGE("failed to get onDisconnect from object!");
582         return;
583     }
584     // release connect
585     std::string bundleName = element.GetBundleName();
586     std::string abilityName = element.GetAbilityName();
587     {
588         std::lock_guard<std::mutex> lock(g_connectMapMtx);
589         IMSA_HILOGI("OnAbilityDisconnectDone connects_.size: %{public}zu.", connects_.size());
590         auto item = std::find_if(connects_.begin(), connects_.end(),
591             [bundleName, abilityName](
592                 const std::map<ConnectionKey, sptr<JSInputMethodExtensionConnection>>::value_type &obj) {
593                 return (bundleName == obj.first.want.GetBundle()) &&
594                        (abilityName == obj.first.want.GetElement().GetAbilityName());
595             });
596         if (item != connects_.end()) {
597             // match bundleName && abilityName
598             if (item->second != nullptr) {
599                 item->second->ReleaseConnection();
600             }
601             connects_.erase(item);
602             IMSA_HILOGI("OnAbilityDisconnectDone erase connects_.size: %{public}zu.", connects_.size());
603         }
604     }
605     IMSA_HILOGI("OnAbilityDisconnectDone CallFunction success.");
606     napi_value callResult = nullptr;
607     napi_call_function(env_, obj, method, ARGC_ONE, argv, &callResult);
608 }
609 
SetJsConnectionObject(napi_value jsConnectionObject)610 void JSInputMethodExtensionConnection::SetJsConnectionObject(napi_value jsConnectionObject)
611 {
612     napi_create_reference(env_, jsConnectionObject, 1, &jsConnectionObject_);
613 }
614 
CallJsFailed(int32_t errorCode)615 void JSInputMethodExtensionConnection::CallJsFailed(int32_t errorCode)
616 {
617     IMSA_HILOGI("CallJsFailed start");
618     if (jsConnectionObject_ == nullptr) {
619         IMSA_HILOGE("jsConnectionObject_ is nullptr!");
620         return;
621     }
622     napi_value obj = nullptr;
623     if (napi_get_reference_value(env_, jsConnectionObject_, &obj) != napi_ok) {
624         IMSA_HILOGE("failed to get jsConnectionObject_!");
625         return;
626     }
627     if (obj == nullptr) {
628         IMSA_HILOGE("failed to get object.");
629         return;
630     }
631 
632     napi_value method = nullptr;
633     napi_get_named_property(env_, obj, "onFailed", &method);
634     if (method == nullptr) {
635         IMSA_HILOGE("failed to get onFailed from object!");
636         return;
637     }
638     napi_value result = nullptr;
639     napi_create_int32(env_, errorCode, &result);
640     napi_value argv[] = { result };
641     IMSA_HILOGI("CallJsFailed CallFunction success.");
642     napi_value callResult = nullptr;
643     napi_call_function(env_, obj, method, ARGC_ONE, argv, &callResult);
644     IMSA_HILOGI("CallJsFailed end.");
645 }
646 
ReleaseConnection()647 void JSInputMethodExtensionConnection::ReleaseConnection()
648 {
649     IMSA_HILOGD("ReleaseConnection");
650     if (jsConnectionObject_ != nullptr) {
651         napi_delete_reference(env_, jsConnectionObject_);
652         env_ = nullptr;
653         jsConnectionObject_ = nullptr;
654     }
655 }
656 } // namespace AbilityRuntime
657 } // namespace OHOS