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