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