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