1 /*
2 * Copyright (c) 2021-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_register_module.h"
17
18 #include <algorithm>
19 #include <cinttypes>
20
21 #include "input_manager.h"
22 #include "js_register_util.h"
23 #include "napi_constants.h"
24 #include "util_napi_error.h"
25 #include "util_napi.h"
26
27 namespace OHOS {
28 namespace MMI {
29 namespace {
30 constexpr OHOS::HiviewDFX::HiLogLabel LABEL = { LOG_CORE, MMI_LOG_DOMAIN, "JSRegisterModule" };
31 constexpr size_t EVENT_NAME_LEN = 64;
32 constexpr size_t PRE_KEYS_SIZE = 4;
33 } // namespace
34
35 static Callbacks callbacks = {};
36
GetEventInfoAPI9(napi_env env,napi_callback_info info,KeyEventMonitorInfo * event,std::shared_ptr<KeyOption> keyOption)37 napi_value GetEventInfoAPI9(napi_env env, napi_callback_info info, KeyEventMonitorInfo* event,
38 std::shared_ptr<KeyOption> keyOption)
39 {
40 CALL_DEBUG_ENTER;
41 CHKPP(event);
42 CHKPP(keyOption);
43 size_t argc = 3;
44 napi_value argv[3] = { 0 };
45 CHKRP(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr), GET_CB_INFO);
46 napi_valuetype valueType = napi_undefined;
47 if (!UtilNapi::TypeOf(env, argv[0], napi_string)) {
48 THROWERR_API9(env, COMMON_PARAMETER_ERROR, "type", "string");
49 MMI_HILOGE("The first parameter is not string");
50 return nullptr;
51 }
52 if (!UtilNapi::TypeOf(env, argv[1], napi_object)) {
53 THROWERR_API9(env, COMMON_PARAMETER_ERROR, "keyOptions", "object");
54 MMI_HILOGE("The second parameter is not napi_object");
55 return nullptr;
56 }
57 char eventType[EVENT_NAME_LEN] = { 0 };
58 size_t typeLen = 0;
59 CHKRP(env, napi_get_value_string_utf8(env, argv[0], eventType, EVENT_NAME_LEN - 1, &typeLen), GET_STRING_UTF8);
60 std::string type = eventType;
61 if (type != SUBSCRIBE_TYPE) {
62 MMI_HILOGE("Type is not key");
63 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "type must be key");
64 return nullptr;
65 }
66 napi_value receiveValue = nullptr;
67 CHKRP(env, napi_get_named_property(env, argv[1], "preKeys", &receiveValue), GET_NAMED_PROPERTY);
68 if (receiveValue == nullptr) {
69 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "preKeys not found");
70 return nullptr;
71 }
72 std::set<int32_t> preKeys;
73 if (GetPreKeys(env, receiveValue, preKeys) == nullptr) {
74 MMI_HILOGE("Get preKeys failed");
75 return nullptr;
76 }
77 if (preKeys.size() > PRE_KEYS_SIZE) {
78 MMI_HILOGE("preKeys size invalid");
79 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "preKeys size invalid");
80 return nullptr;
81 }
82 MMI_HILOGD("PreKeys size:%{public}zu", preKeys.size());
83 keyOption->SetPreKeys(preKeys);
84 std::string subKeyNames = "";
85 for (const auto &item : preKeys) {
86 subKeyNames += std::to_string(item);
87 subKeyNames += ",";
88 MMI_HILOGD("preKeys:%{public}d", item);
89 }
90 std::optional<int32_t> tempFinalKey = GetNamedPropertyInt32(env, argv[1], "finalKey");
91 if (!tempFinalKey) {
92 MMI_HILOGE("GetNamedPropertyInt32 failed");
93 return nullptr;
94 }
95 int32_t finalKey = tempFinalKey.value();
96 if (finalKey < 0) {
97 MMI_HILOGE("finalKey:%{public}d is less 0, can not process", finalKey);
98 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "finalKey must be greater than or equal to 0");
99 return nullptr;
100 }
101 subKeyNames += std::to_string(finalKey);
102 subKeyNames += ",";
103 keyOption->SetFinalKey(finalKey);
104 MMI_HILOGD("FinalKey:%{public}d", finalKey);
105 bool isFinalKeyDown;
106 if (!GetNamedPropertyBool(env, argv[1], "isFinalKeyDown", isFinalKeyDown)) {
107 MMI_HILOGE("GetNamedPropertyBool failed");
108 return nullptr;
109 }
110 subKeyNames += std::to_string(isFinalKeyDown);
111 subKeyNames += ",";
112 keyOption->SetFinalKeyDown(isFinalKeyDown);
113 MMI_HILOGD("IsFinalKeyDown:%{public}d,map_key:%{public}s",
114 (isFinalKeyDown == true?1:0), subKeyNames.c_str());
115 std::optional<int32_t> tempKeyDownDuration = GetNamedPropertyInt32(env, argv[1], "finalKeyDownDuration");
116 if (!tempKeyDownDuration) {
117 MMI_HILOGE("GetNamedPropertyInt32 failed");
118 return nullptr;
119 }
120 int32_t finalKeyDownDuration = tempKeyDownDuration.value();
121 if (finalKeyDownDuration < 0) {
122 MMI_HILOGE("finalKeyDownDuration:%{public}d is less 0, can not process", finalKeyDownDuration);
123 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "finalKeyDownDuration must be greater than or equal to 0");
124 return nullptr;
125 }
126 subKeyNames += std::to_string(finalKeyDownDuration);
127 keyOption->SetFinalKeyDownDuration(finalKeyDownDuration);
128 event->eventType = subKeyNames;
129 MMI_HILOGD("FinalKeyDownDuration:%{public}d", finalKeyDownDuration);
130 if (argc == 3) {
131 CHKRP(env, napi_typeof(env, argv[2], &valueType), TYPEOF);
132 if (valueType != napi_function) {
133 MMI_HILOGE("the third parameter is not napi_function");
134 THROWERR_API9(env, COMMON_PARAMETER_ERROR, "callback", "function");
135 return nullptr;
136 }
137 CHKRP(env, napi_create_reference(env, argv[2], 1, &event->callback[0]), REFERENCE_REF);
138 } else {
139 event->callback[0] = nullptr;
140 }
141 napi_value ret;
142 CHKRP(env, napi_create_int32(env, RET_OK, &ret), CREATE_INT32);
143 return ret;
144 }
145
IsMatchKeyAction(bool isFinalKeydown,int32_t keyAction)146 static bool IsMatchKeyAction(bool isFinalKeydown, int32_t keyAction)
147 {
148 CALL_DEBUG_ENTER;
149 MMI_HILOGD("isFinalKeydown:%{public}d,keyAction:%{public}d", isFinalKeydown, keyAction);
150 if (isFinalKeydown && keyAction == KeyEvent::KEY_ACTION_DOWN) {
151 return true;
152 }
153
154 if (!isFinalKeydown && keyAction == KeyEvent::KEY_ACTION_UP) {
155 return true;
156 }
157 MMI_HILOGE("isFinalKeydown not matched with keyAction");
158 return false;
159 }
160
MatchCombinationKeys(KeyEventMonitorInfo * monitorInfo,std::shared_ptr<KeyEvent> keyEvent)161 static bool MatchCombinationKeys(KeyEventMonitorInfo* monitorInfo, std::shared_ptr<KeyEvent> keyEvent)
162 {
163 CALL_DEBUG_ENTER;
164 CHKPF(monitorInfo);
165 CHKPF(keyEvent);
166 auto keyOption = monitorInfo->keyOption;
167 CHKPF(keyOption);
168 std::vector<KeyEvent::KeyItem> items = keyEvent->GetKeyItems();
169 int32_t infoFinalKey = keyOption->GetFinalKey();
170 int32_t keyEventFinalKey = keyEvent->GetKeyCode();
171 bool isFinalKeydown = keyOption->IsFinalKeyDown();
172 MMI_HILOGD("infoFinalKey:%{public}d,keyEventFinalKey:%{public}d", infoFinalKey, keyEventFinalKey);
173 if (infoFinalKey != keyEventFinalKey || items.size() > PRE_KEYS_SIZE ||
174 !IsMatchKeyAction(isFinalKeydown, keyEvent->GetKeyAction())) {
175 MMI_HILOGE("Param invalid");
176 return false;
177 }
178 std::set<int32_t> infoPreKeys = keyOption->GetPreKeys();
179 int32_t infoSize = 0;
180 auto it = infoPreKeys.begin();
181 while (it != infoPreKeys.end()) {
182 if (*it >= 0) {
183 infoSize++;
184 }
185 ++it;
186 }
187 int32_t count = 0;
188 for (const auto &item : items) {
189 if (item.GetKeyCode() == keyEventFinalKey) {
190 continue;
191 }
192 auto iter = find(infoPreKeys.begin(), infoPreKeys.end(), item.GetKeyCode());
193 if (iter == infoPreKeys.end()) {
194 MMI_HILOGW("No keyCode in preKeys");
195 return false;
196 }
197 count++;
198 }
199 MMI_HILOGD("kevEventSize:%{public}d,infoSize:%{public}d", count, infoSize);
200 auto keyItem = keyEvent->GetKeyItem();
201 CHKPF(keyItem);
202 auto upTime = keyEvent->GetActionTime();
203 auto downTime = keyItem->GetDownTime();
204 auto curDurationTime = keyOption->GetFinalKeyDownDuration();
205 if (curDurationTime > 0 && (upTime - downTime >= (static_cast<int64_t>(curDurationTime) * 1000))) {
206 MMI_HILOGE("Skip, upTime - downTime >= duration");
207 return false;
208 }
209 return count == infoSize;
210 }
211
SubKeyEventCallback(std::shared_ptr<KeyEvent> keyEvent)212 static void SubKeyEventCallback(std::shared_ptr<KeyEvent> keyEvent)
213 {
214 CALL_DEBUG_ENTER;
215 CHKPV(keyEvent);
216 std::lock_guard guard(sCallBacksMutex_);
217 auto iter = callbacks.begin();
218 while (iter != callbacks.end()) {
219 auto &list = iter->second;
220 ++iter;
221 MMI_HILOGD("list size:%{public}zu", list.size());
222 auto infoIter = list.begin();
223 while (infoIter != list.end()) {
224 auto monitorInfo = *infoIter;
225 if (MatchCombinationKeys(monitorInfo, keyEvent)) {
226 monitorInfo->keyEvent = keyEvent;
227 EmitAsyncCallbackWork(monitorInfo);
228 }
229 ++infoIter;
230 }
231 }
232 }
233
JsOn(napi_env env,napi_callback_info info)234 static napi_value JsOn(napi_env env, napi_callback_info info)
235 {
236 CALL_DEBUG_ENTER;
237 size_t argc = 3;
238 napi_value argv[3] = { 0 };
239 napi_value thisArg = nullptr;
240 CHKRP(env, napi_get_cb_info(env, info, &argc, argv, &thisArg, nullptr), GET_CB_INFO);
241 if (argc < 3) {
242 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "parameter number error");
243 return nullptr;
244 }
245 if (thisArg == nullptr) {
246 MMI_HILOGE("%{public}s, This argument is nullptr.", __func__);
247 return nullptr;
248 }
249 napi_valuetype valueOfThis = napi_undefined;
250 CHKRP(env, napi_typeof(env, thisArg, &valueOfThis), TYPEOF);
251 if (valueOfThis == napi_undefined) {
252 MMI_HILOGE("%{public}s, Wrong value of this.", __func__);
253 return nullptr;
254 }
255 KeyEventMonitorInfo *event = new (std::nothrow) KeyEventMonitorInfo {
256 .env = env,
257 .asyncWork = nullptr,
258 };
259 CHKPP(event);
260 auto keyOption = std::make_shared<KeyOption>();
261 napi_valuetype valueType = napi_undefined;
262 if (napi_typeof(env, argv[0], &valueType) != napi_ok) {
263 delete event;
264 MMI_HILOGE("Napi typeof failed");
265 return nullptr;
266 }
267 if (GetEventInfoAPI9(env, info, event, keyOption) == nullptr) {
268 delete event;
269 MMI_HILOGE("GetEventInfo failed");
270 return nullptr;
271 }
272 event->keyOption = keyOption;
273 int32_t preSubscribeId = GetPreSubscribeId(callbacks, event);
274 if (preSubscribeId < 0) {
275 MMI_HILOGD("eventType:%{public}s,eventName:%{public}s", event->eventType.c_str(), event->name.c_str());
276 int32_t subscribeId = -1;
277 subscribeId = InputManager::GetInstance()->SubscribeKeyEvent(keyOption, SubKeyEventCallback);
278 if (subscribeId < 0) {
279 MMI_HILOGE("SubscribeId invalid:%{public}d", subscribeId);
280 napi_delete_reference(env, event->callback[0]);
281 delete event;
282 return nullptr;
283 }
284 MMI_HILOGD("SubscribeId:%{public}d", subscribeId);
285 event->subscribeId = subscribeId;
286 } else {
287 event->subscribeId = preSubscribeId;
288 }
289 if (AddEventCallback(env, callbacks, event) < 0) {
290 delete event;
291 MMI_HILOGE("AddEventCallback failed");
292 return nullptr;
293 }
294 std::shared_ptr<KeyEventMonitorInfo>* cbInfo = new std::shared_ptr<KeyEventMonitorInfo>(event);
295 napi_wrap(env, thisArg, static_cast<void*>(cbInfo), [](napi_env env, void* data, void* hint) {
296 std::shared_ptr<KeyEventMonitorInfo>* cbInfo = static_cast<std::shared_ptr<KeyEventMonitorInfo>*>(data);
297 if (cbInfo != nullptr && *cbInfo != nullptr) {
298 (*cbInfo)->SetValid(false);
299 delete cbInfo;
300 }
301 }, nullptr, nullptr);
302 return nullptr;
303 }
304
JsOff(napi_env env,napi_callback_info info)305 static napi_value JsOff(napi_env env, napi_callback_info info)
306 {
307 CALL_DEBUG_ENTER;
308 size_t argc = 3;
309 napi_value argv[3] = { 0 };
310 CHKRP(env, napi_get_cb_info(env, info, &argc, argv, nullptr, nullptr), GET_CB_INFO);
311 if (argc < 2) {
312 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "parameter number error");
313 return nullptr;
314 }
315 KeyEventMonitorInfo *event = new (std::nothrow) KeyEventMonitorInfo {
316 .env = env,
317 .asyncWork = nullptr,
318 };
319 CHKPP(event);
320 auto keyOption = std::make_shared<KeyOption>();
321 napi_valuetype valueType = napi_undefined;
322 if (napi_typeof(env, argv[0], &valueType) != napi_ok) {
323 delete event;
324 MMI_HILOGE("Napi typeof failed");
325 return nullptr;
326 }
327 if (GetEventInfoAPI9(env, info, event, keyOption) == nullptr) {
328 delete event;
329 MMI_HILOGE("GetEventInfo failed");
330 return nullptr;
331 }
332 int32_t subscribeId = -1;
333 if (DelEventCallback(env, callbacks, event, subscribeId) < 0) {
334 delete event;
335 MMI_HILOGE("DelEventCallback failed");
336 return nullptr;
337 }
338 MMI_HILOGD("SubscribeId:%{public}d", subscribeId);
339 if (subscribeId >= 0) {
340 InputManager::GetInstance()->UnsubscribeKeyEvent(subscribeId);
341 }
342 if (event->callback[0] != nullptr) {
343 napi_delete_reference(env, event->callback[0]);
344 }
345 delete event;
346 return nullptr;
347 }
348
349 EXTERN_C_START
MmiInit(napi_env env,napi_value exports)350 static napi_value MmiInit(napi_env env, napi_value exports)
351 {
352 CALL_DEBUG_ENTER;
353 napi_property_descriptor desc[] = {
354 DECLARE_NAPI_FUNCTION("on", JsOn),
355 DECLARE_NAPI_FUNCTION("off", JsOff),
356 };
357 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
358 return exports;
359 }
360 EXTERN_C_END
361
362 static napi_module mmiModule = {
363 .nm_version = 1,
364 .nm_flags = 0,
365 .nm_filename = nullptr,
366 .nm_register_func = MmiInit,
367 .nm_modname = "multimodalInput.inputConsumer",
368 .nm_priv = ((void*)0),
369 .reserved = { 0 },
370 };
371
RegisterModule(void)372 extern "C" __attribute__((constructor)) void RegisterModule(void)
373 {
374 napi_module_register(&mmiModule);
375 }
376 } // namespace MMI
377 } // namespace OHOS