1 /*
2 * Copyright (c) 2021-2024 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_register_util.h"
17
18 #include <cinttypes>
19 #include <uv.h>
20
21 #include "error_multimodal.h"
22 #include "input_manager.h"
23 #include "napi_constants.h"
24 #include "util_napi.h"
25 #include "util_napi_error.h"
26
27 #undef MMI_LOG_TAG
28 #define MMI_LOG_TAG "JSRegisterUtil"
29
30 namespace OHOS {
31 namespace MMI {
32
TypeOf(napi_env env, napi_value value, napi_valuetype type)33 bool TypeOf(napi_env env, napi_value value, napi_valuetype type)
34 {
35 napi_valuetype valueType = napi_undefined;
36 CHKRF(napi_typeof(env, value, &valueType), TYPEOF);
37 if (valueType != type) {
38 return false;
39 }
40 return true;
41 }
42
SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, int32_t value)43 void SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, int32_t value)
44 {
45 MMI_HILOGD("%{public}s=%{public}d", name.c_str(), value);
46 napi_value napiValue;
47 CHKRV(napi_create_int32(env, value, &napiValue), CREATE_INT32);
48 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), napiValue));
49 }
50
SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, std::string value)51 void SetNamedProperty(const napi_env &env, napi_value &object, const std::string &name, std::string value)
52 {
53 MMI_HILOGD("%{public}s=%{public}s", name.c_str(), value.c_str());
54 napi_value napiValue;
55 CHKRV(napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &napiValue), CREATE_STRING_UTF8);
56 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, object, name.c_str(), napiValue));
57 }
58
GetNamedPropertyBool(const napi_env &env, const napi_value &object, const std::string &name, bool &ret)59 bool GetNamedPropertyBool(const napi_env &env, const napi_value &object, const std::string &name, bool &ret)
60 {
61 napi_value napiValue = {};
62 bool exist = false;
63 napi_status status = napi_has_named_property(env, object, name.c_str(), &exist);
64 if (status != napi_ok || !exist) {
65 MMI_HILOGD("Can not find %{public}s property", name.c_str());
66 return false;
67 }
68 napi_get_named_property(env, object, name.c_str(), &napiValue);
69 napi_valuetype tmpType = napi_undefined;
70
71 CHKRF(napi_typeof(env, napiValue, &tmpType), TYPEOF);
72 if (tmpType != napi_boolean) {
73 MMI_HILOGE("The value is not bool");
74 THROWERR_API9(env, COMMON_PARAMETER_ERROR, name.c_str(), "bool");
75 return false;
76 }
77 CHKRF(napi_get_value_bool(env, napiValue, &ret), GET_VALUE_BOOL);
78 MMI_HILOGD("%{public}s=%{public}d", name.c_str(), ret);
79 return true;
80 }
81
GetNamedPropertyInt32(const napi_env &env, const napi_value &object, const std::string &name)82 std::optional<int32_t> GetNamedPropertyInt32(const napi_env &env, const napi_value &object, const std::string &name)
83 {
84 napi_value napiValue = {};
85 bool exist = false;
86 napi_status status = napi_has_named_property(env, object, name.c_str(), &exist);
87 if (status != napi_ok || !exist) {
88 MMI_HILOGD("Can not find %{public}s property", name.c_str());
89 return std::nullopt;
90 }
91 napi_get_named_property(env, object, name.c_str(), &napiValue);
92 napi_valuetype tmpType = napi_undefined;
93 if (napi_typeof(env, napiValue, &tmpType) != napi_ok) {
94 MMI_HILOGE("Call napi_typeof failed");
95 return std::nullopt;
96 }
97 if (tmpType != napi_number) {
98 MMI_HILOGE("The value is not number");
99 THROWERR_API9(env, COMMON_PARAMETER_ERROR, name.c_str(), "number");
100 return std::nullopt;
101 }
102 int32_t ret = 0;
103 if (napi_get_value_int32(env, napiValue, &ret) != napi_ok) {
104 MMI_HILOGE("Call napi_get_value_int32 failed");
105 return std::nullopt;
106 }
107 MMI_HILOGD("%{public}s=%{public}d", name.c_str(), ret);
108 return std::make_optional(ret);
109 }
110
GetPreKeys(const napi_env &env, const napi_value &value, std::set<int32_t> ¶ms)111 napi_value GetPreKeys(const napi_env &env, const napi_value &value, std::set<int32_t> ¶ms)
112 {
113 CALL_DEBUG_ENTER;
114 uint32_t arrayLength = 0;
115 CHKRP(napi_get_array_length(env, value, &arrayLength), GET_ARRAY_LENGTH);
116 for (uint32_t i = 0; i < arrayLength; i++) {
117 napi_value napiElement;
118 CHKRP(napi_get_element(env, value, i, &napiElement), GET_ELEMENT);
119 napi_valuetype valuetype;
120 CHKRP(napi_typeof(env, napiElement, &valuetype), TYPEOF);
121 if (valuetype != napi_number) {
122 MMI_HILOGE("PreKeys Wrong argument type, Number expected");
123 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "element of preKeys must be number");
124 return nullptr;
125 }
126 int32_t value = 0;
127 CHKRP(napi_get_value_int32(env, napiElement, &value), GET_VALUE_INT32);
128 if (value < 0) {
129 MMI_HILOGE("preKey:%{public}d is less 0, can not process", value);
130 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "element of preKeys must be greater than or equal to 0");
131 return nullptr;
132 }
133 MMI_HILOGD("Get int array number:%{public}d", value);
134 if (!params.insert(value).second) {
135 MMI_HILOGE("Params insert value failed");
136 return nullptr;
137 }
138 }
139 napi_value ret;
140 CHKRP(napi_create_int32(env, RET_OK, &ret), CREATE_INT32);
141 return ret;
142 }
143
GetPreSubscribeId(Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)144 int32_t GetPreSubscribeId(Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)
145 {
146 CHKPR(event, ERROR_NULL_POINTER);
147 std::lock_guard guard(sCallBacksMutex);
148 auto it = callbacks.find(event->eventType);
149 if (it == callbacks.end() || it->second.empty()) {
150 MMI_HILOGE("The callbacks is empty");
151 return JS_CALLBACK_EVENT_FAILED;
152 }
153 CHKPR(it->second.front(), ERROR_NULL_POINTER);
154 return it->second.front()->subscribeId;
155 }
156
DelEventCallbackRef(const napi_env &env, std::list<sptr<KeyEventMonitorInfo>> &info, napi_value handler, int32_t &subscribeId)157 int32_t DelEventCallbackRef(const napi_env &env, std::list<sptr<KeyEventMonitorInfo>> &info,
158 napi_value handler, int32_t &subscribeId)
159 {
160 CALL_DEBUG_ENTER;
161 for (auto iter = info.begin(); iter != info.end();) {
162 if (*iter == nullptr) {
163 info.erase(iter++);
164 continue;
165 }
166 if (handler != nullptr) {
167 napi_value iterHandler = nullptr;
168 CHKRR(napi_get_reference_value(env, (*iter)->callback, &iterHandler),
169 GET_REFERENCE_VALUE, JS_CALLBACK_EVENT_FAILED);
170 bool isEquals = false;
171 CHKRR(napi_strict_equals(env, handler, iterHandler, &isEquals), STRICT_EQUALS, JS_CALLBACK_EVENT_FAILED);
172 if (isEquals) {
173 sptr<KeyEventMonitorInfo> monitorInfo = *iter;
174 info.erase(iter++);
175 if (info.empty()) {
176 subscribeId = monitorInfo->subscribeId;
177 }
178 MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
179 return JS_CALLBACK_EVENT_SUCCESS;
180 }
181 ++iter;
182 continue;
183 }
184 sptr<KeyEventMonitorInfo> monitorInfo = *iter;
185 info.erase(iter++);
186 if (info.empty()) {
187 subscribeId = monitorInfo->subscribeId;
188 }
189 MMI_HILOGD("Callback has deleted, size:%{public}zu", info.size());
190 }
191 MMI_HILOGD("Callback size:%{public}zu", info.size());
192 return JS_CALLBACK_EVENT_SUCCESS;
193 }
194
AddEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)195 int32_t AddEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event)
196 {
197 CALL_DEBUG_ENTER;
198 std::lock_guard guard(sCallBacksMutex);
199 CHKPR(event, ERROR_NULL_POINTER);
200 if (callbacks.find(event->eventType) == callbacks.end()) {
201 MMI_HILOGD("No callback in %{public}s", event->eventType.c_str());
202 callbacks[event->eventType] = {};
203 }
204 napi_value handler1 = nullptr;
205 napi_status status = napi_get_reference_value(env, event->callback, &handler1);
206 if (status != napi_ok) {
207 MMI_HILOGE("Handler1 get reference value failed");
208 return JS_CALLBACK_EVENT_FAILED;
209 }
210 auto it = callbacks.find(event->eventType);
211 for (const auto &iter: it->second) {
212 napi_value handler2 = nullptr;
213 status = napi_get_reference_value(env, iter->callback, &handler2);
214 if (status != napi_ok) {
215 MMI_HILOGE("Handler2 get reference value failed");
216 return JS_CALLBACK_EVENT_FAILED;
217 }
218 bool isEqual = false;
219 status = napi_strict_equals(env, handler1, handler2, &isEqual);
220 if (status != napi_ok) {
221 MMI_HILOGE("Compare two handler failed");
222 return JS_CALLBACK_EVENT_FAILED;
223 }
224 if (isEqual) {
225 MMI_HILOGE("Callback already exist");
226 return JS_CALLBACK_EVENT_FAILED;
227 }
228 }
229 it->second.push_back(event);
230 return JS_CALLBACK_EVENT_SUCCESS;
231 }
232
DelEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event, int32_t &subscribeId)233 int32_t DelEventCallback(const napi_env &env, Callbacks &callbacks, sptr<KeyEventMonitorInfo> event,
234 int32_t &subscribeId)
235 {
236 CALL_DEBUG_ENTER;
237 std::lock_guard guard(sCallBacksMutex);
238 CHKPR(event, ERROR_NULL_POINTER);
239 if (callbacks.count(event->eventType) <= 0) {
240 MMI_HILOGE("Callback doesn't exists");
241 return JS_CALLBACK_EVENT_FAILED;
242 }
243 auto &info = callbacks[event->eventType];
244 MMI_HILOGD("EventType:%{private}s, keyEventMonitorInfos:%{public}zu", event->eventType.c_str(), info.size());
245 napi_value eventHandler = nullptr;
246 if (event->callback != nullptr) {
247 CHKRR(napi_get_reference_value(env, event->callback, &eventHandler), GET_REFERENCE_VALUE,
248 JS_CALLBACK_EVENT_FAILED);
249 }
250 return DelEventCallbackRef(env, info, eventHandler, subscribeId);
251 }
252
AsyncWorkFn(const napi_env &env, std::shared_ptr<KeyOption> keyOption, napi_value &result, std::string keyType)253 static void AsyncWorkFn(const napi_env &env, std::shared_ptr<KeyOption> keyOption, napi_value &result,
254 std::string keyType)
255 {
256 CHKPV(keyOption);
257 MMI_HILOGD("Status > 0 enter");
258 CHKRV(napi_create_object(env, &result), CREATE_OBJECT);
259 napi_value arr;
260 CHKRV(napi_create_array(env, &arr), CREATE_ARRAY);
261 std::set <int32_t> preKeys = keyOption->GetPreKeys();
262 int32_t i = 0;
263 napi_value value;
264 for (const auto &preKey: preKeys) {
265 CHKRV(napi_create_int32(env, preKey, &value), CREATE_INT32);
266 CHKRV(napi_set_element(env, arr, i, value), SET_ELEMENT);
267 ++i;
268 }
269 std::string preKeysStr = "preKeys";
270 NAPI_CALL_RETURN_VOID(env, napi_set_named_property(env, result, preKeysStr.c_str(), arr));
271 MMI::SetNamedProperty(env, result, "finalKey", keyOption->GetFinalKey());
272 if (keyType == SUBSCRIBE_TYPE) {
273 MMI::SetNamedProperty(env, result, "isFinalKeyDown", keyOption->IsFinalKeyDown());
274 MMI::SetNamedProperty(env, result, "finalKeyDownDuration", keyOption->GetFinalKeyDownDuration());
275 }
276 MMI::SetNamedProperty(env, result, "isRepeat", static_cast<int32_t>(keyOption->IsRepeat()));
277 }
278
UvQueueWorkAsyncCallback(uv_work_t *work, int32_t status)279 void UvQueueWorkAsyncCallback(uv_work_t *work, int32_t status)
280 {
281 CALL_DEBUG_ENTER;
282 CHKPV(work);
283 if (work->data == nullptr) {
284 DeletePtr<uv_work_t *>(work);
285 MMI_HILOGE("Check data is nullptr");
286 return;
287 }
288 (void)status;
289 sptr<KeyEventMonitorInfo> dataWorker(static_cast<KeyEventMonitorInfo *>(work->data));
290 DeletePtr<uv_work_t *>(work);
291 dataWorker->DecStrongRef(nullptr);
292 CHKPV(dataWorker->env);
293 napi_handle_scope scope = nullptr;
294 napi_open_handle_scope(dataWorker->env, &scope);
295 if (scope == nullptr) {
296 MMI_HILOGE("Scope is nullptr");
297 return;
298 }
299 napi_value callback = nullptr;
300 MMI_HILOGD("deliver uv work from %{public}d", GetPid());
301 if (dataWorker->callback == nullptr) {
302 MMI_HILOGE("dataWorker->callback is nullptr");
303 napi_close_handle_scope(dataWorker->env, scope);
304 return;
305 }
306 if ((napi_get_reference_value(dataWorker->env, dataWorker->callback, &callback)) != napi_ok) {
307 MMI_HILOGE("%{public}s failed", std::string(GET_REFERENCE_VALUE).c_str());
308 napi_close_handle_scope(dataWorker->env, scope);
309 return;
310 }
311 napi_value result = nullptr;
312 AsyncWorkFn(dataWorker->env, dataWorker->keyOption, result, dataWorker->name);
313 napi_value callResult = nullptr;
314 if ((napi_call_function(dataWorker->env, nullptr, callback, 1, &result, &callResult)) != napi_ok) {
315 MMI_HILOGE("%{public}s failed", std::string(CALL_FUNCTION).c_str());
316 napi_close_handle_scope(dataWorker->env, scope);
317 return;
318 }
319 napi_close_handle_scope(dataWorker->env, scope);
320 }
321
EmitAsyncCallbackWork(sptr<KeyEventMonitorInfo> reportEvent)322 void EmitAsyncCallbackWork(sptr<KeyEventMonitorInfo> reportEvent)
323 {
324 CALL_DEBUG_ENTER;
325 CHKPV(reportEvent);
326 uv_loop_s *loop = nullptr;
327 CHKRV(napi_get_uv_event_loop(reportEvent->env, &loop), GET_UV_EVENT_LOOP);
328 uv_work_t *work = new (std::nothrow) uv_work_t;
329 if (work == nullptr) {
330 MMI_HILOGE("work is nullptr");
331 return;
332 }
333 reportEvent->IncStrongRef(nullptr);
334 work->data = reportEvent.GetRefPtr();
335 int32_t ret = uv_queue_work_with_qos(
336 loop, work,
337 [](uv_work_t *work) {
338 MMI_HILOGD("uv_queue_work callback function is called");
339 },
340 UvQueueWorkAsyncCallback, uv_qos_user_initiated);
341 if (ret != 0) {
342 DeletePtr<uv_work_t *>(work);
343 }
344 }
345
ConvertHotkeyToNapiValue(napi_env env, std::unique_ptr<KeyOption> &keyOption)346 napi_value ConvertHotkeyToNapiValue(napi_env env, std::unique_ptr<KeyOption> &keyOption)
347 {
348 napi_value obj = nullptr;
349 CHKRP(napi_create_object(env, &obj), CREATE_OBJECT);
350 napi_value preKeysArray = nullptr;
351 CHKRP(napi_create_array(env, &preKeysArray), CREATE_ARRAY);
352 int32_t index = 0;
353 std::set<int32_t> preKeys = keyOption->GetPreKeys();
354 for (auto key : preKeys) {
355 napi_value keyVal = nullptr;
356 CHKRP(napi_create_int32(env, key, &keyVal), CREATE_INT32);
357 CHKRP(napi_set_element(env, preKeysArray, index++, keyVal), SET_ELEMENT);
358 }
359 CHKRP(napi_set_named_property(env, obj, "preKeys", preKeysArray), SET_NAMED_PROPERTY);
360 napi_value finalKeyVal = nullptr;
361 CHKRP(napi_create_int32(env, keyOption->GetFinalKey(), &finalKeyVal), CREATE_INT32);
362 CHKRP(napi_set_named_property(env, obj, "finalKey", finalKeyVal), SET_NAMED_PROPERTY);
363 return obj;
364 }
365
ConvertHotkeysToNapiArray(sptr<CallbackInfo> cb)366 napi_value ConvertHotkeysToNapiArray(sptr<CallbackInfo> cb)
367 {
368 napi_value keyOptionArray = nullptr;
369 CHKRP(napi_create_array(cb->env, &keyOptionArray), CREATE_ARRAY);
370 if (cb->errCode != RET_OK) {
371 MMI_HILOGE("Get Hotkeys failed, errCode:%{public}d", cb->errCode);
372 return keyOptionArray;
373 }
374 for (size_t i = 0; i < cb->keyOptions.size(); ++i) {
375 napi_value obj = ConvertHotkeyToNapiValue(cb->env, cb->keyOptions[i]);
376 if (obj == nullptr) {
377 MMI_HILOGE("ConvertHotkeyToNapiValue fail");
378 return keyOptionArray;
379 }
380 CHKRP(napi_set_element(cb->env, keyOptionArray, i, obj), SET_ELEMENT);
381 }
382 return keyOptionArray;
383 }
384
GreateBusinessError(napi_env env, int32_t errCode, std::string errMessage)385 napi_value GreateBusinessError(napi_env env, int32_t errCode, std::string errMessage)
386 {
387 CALL_DEBUG_ENTER;
388 napi_value result = nullptr;
389 napi_value resultCode = nullptr;
390 napi_value resultMessage = nullptr;
391 CHKRP(napi_create_int32(env, errCode, &resultCode), CREATE_INT32);
392 CHKRP(napi_create_string_utf8(env, errMessage.data(), NAPI_AUTO_LENGTH, &resultMessage), CREATE_STRING_UTF8);
393 CHKRP(napi_create_error(env, nullptr, resultMessage, &result), CREATE_ERROR);
394 CHKRP(napi_set_named_property(env, result, ERR_CODE.c_str(), resultCode), SET_NAMED_PROPERTY);
395 return result;
396 }
397
CallHotkeyPromiseWork(uv_work_t *work, int32_t status)398 void CallHotkeyPromiseWork(uv_work_t *work, int32_t status)
399 {
400 CALL_DEBUG_ENTER;
401 CHKPV(work);
402 if (work->data == nullptr) {
403 DeletePtr<uv_work_t *>(work);
404 MMI_HILOGE("Check data is nullptr");
405 return;
406 }
407 sptr<CallbackInfo> cb(static_cast<CallbackInfo *>(work->data));
408 DeletePtr<uv_work_t *>(work);
409 cb->DecStrongRef(nullptr);
410 CHKPV(cb->env);
411 napi_handle_scope scope = nullptr;
412 napi_open_handle_scope(cb->env, &scope);
413 CHKPV(scope);
414 napi_value callResult = ConvertHotkeysToNapiArray(cb);
415 if (callResult == nullptr) {
416 MMI_HILOGE("Check callResult is nullptr");
417 napi_close_handle_scope(cb->env, scope);
418 return;
419 }
420 CHKRV_SCOPE(cb->env, napi_resolve_deferred(cb->env, cb->deferred, callResult), RESOLVE_DEFERRED, scope);
421 napi_close_handle_scope(cb->env, scope);
422 }
423
EmitSystemHotkey(sptr<CallbackInfo> cb)424 void EmitSystemHotkey(sptr<CallbackInfo> cb)
425 {
426 CALL_DEBUG_ENTER;
427 CHKPV(cb);
428 CHKPV(cb->env);
429 uv_loop_s *loop = nullptr;
430 CHKRV(napi_get_uv_event_loop(cb->env, &loop), GET_UV_EVENT_LOOP);
431 uv_work_t *work = new (std::nothrow) uv_work_t;
432 CHKPV(work);
433 cb->IncStrongRef(nullptr);
434 work->data = cb.GetRefPtr();
435 int32_t ret = 0;
436 ret = uv_queue_work_with_qos(
437 loop, work,
438 [](uv_work_t *work) {
439 MMI_HILOGD("uv_queue_work CallHotkeyPromiseWork callback function is called");
440 }, CallHotkeyPromiseWork, uv_qos_user_initiated);
441 if (ret != 0) {
442 MMI_HILOGE("uv_queue_work_with_qos failed");
443 cb->DecStrongRef(nullptr);
444 DeletePtr<uv_work_t *>(work);
445 }
446 }
447
GetSystemHotkey(napi_env env, napi_value handle)448 napi_value GetSystemHotkey(napi_env env, napi_value handle)
449 {
450 CALL_DEBUG_ENTER;
451 sptr<CallbackInfo> cb = new (std::nothrow) CallbackInfo();
452 CHKPP(cb);
453 cb->env = env;
454 napi_value promise = nullptr;
455 CHKRP(napi_create_promise(env, &cb->deferred, &promise), CREATE_PROMISE);
456 std::vector<std::unique_ptr<KeyOption>> keyOptions;
457 int32_t count = 0;
458 cb->errCode = InputManager::GetInstance()->GetAllSystemHotkeys(keyOptions, count);
459 cb->keyOptions = std::move(keyOptions);
460 EmitSystemHotkey(cb);
461 return promise;
462 }
463 } // namespace MMI
464 } // namespace OHOS