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_input_monitor_manager.h"
17
18 #include "define_multimodal.h"
19 #include "napi_constants.h"
20 #include "util_napi_error.h"
21
22 #undef MMI_LOG_TAG
23 #define MMI_LOG_TAG "JsInputMonitorManager"
24
25 namespace OHOS {
26 namespace MMI {
27 namespace {
28 constexpr int32_t MONITOR_REGISTER_EXCEED_MAX { 4100001 };
29 } // namespace
30 static const std::vector<int32_t> supportedKeyCodes = {
31 KeyEvent::KEYCODE_POWER,
32 KeyEvent::KEYCODE_META_LEFT,
33 KeyEvent::KEYCODE_VOLUME_UP,
34 KeyEvent::KEYCODE_VOLUME_DOWN,
35 KeyEvent::KEYCODE_META_RIGHT
36 };
37
GetInstance()38 JsInputMonitorManager& JsInputMonitorManager::GetInstance()
39 {
40 static JsInputMonitorManager instance;
41 return instance;
42 }
43
AddMonitor(napi_env jsEnv,const std::string & typeName,std::vector<Rect> hotRectArea,int32_t rectTotal,napi_value callback,const int32_t fingers)44 void JsInputMonitorManager::AddMonitor(napi_env jsEnv, const std::string &typeName,
45 std::vector<Rect> hotRectArea, int32_t rectTotal, napi_value callback, const int32_t fingers)
46 {
47 CALL_DEBUG_ENTER;
48 std::lock_guard<std::mutex> guard(mutex_);
49 for (const auto &item : monitors_) {
50 if ((item != nullptr) && (item->IsMatch(jsEnv, callback) != RET_ERR)) {
51 MMI_HILOGW("Add js monitor failed");
52 return;
53 }
54 }
55 auto monitor = std::make_shared<JsInputMonitor>(jsEnv, typeName, hotRectArea,
56 rectTotal, callback, nextId_++, fingers);
57 int32_t ret = monitor->Start(typeName);
58 if (ret < 0) {
59 MMI_HILOGE("Js monitor startup failed");
60 ThrowError(jsEnv, ret);
61 return;
62 }
63 monitors_.push_back(monitor);
64 }
65
AddMonitor(napi_env jsEnv,const std::string & typeName,napi_value callback,const int32_t fingers)66 void JsInputMonitorManager::AddMonitor(napi_env jsEnv, const std::string &typeName,
67 napi_value callback, const int32_t fingers)
68 {
69 CALL_DEBUG_ENTER;
70 std::lock_guard<std::mutex> guard(mutex_);
71 for (const auto &item : monitors_) {
72 if ((item != nullptr) && (item->IsMatch(jsEnv, callback) != RET_ERR)) {
73 MMI_HILOGW("Add js monitor failed");
74 return;
75 }
76 }
77 auto monitor = std::make_shared<JsInputMonitor>(jsEnv, typeName, callback, nextId_++, fingers);
78 int32_t ret = monitor->Start(typeName);
79 if (ret < 0) {
80 MMI_HILOGE("Js monitor startup failed");
81 ThrowError(jsEnv, ret);
82 return;
83 }
84 monitors_.push_back(monitor);
85 }
86
AddPreMonitor(napi_env jsEnv,const std::string & typeName,napi_value callback,std::vector<int32_t> keys)87 void JsInputMonitorManager::AddPreMonitor(napi_env jsEnv, const std::string &typeName,
88 napi_value callback, std::vector<int32_t> keys)
89 {
90 CALL_DEBUG_ENTER;
91 std::lock_guard<std::mutex> guard(mutex_);
92 for (const auto &item : monitors_) {
93 if ((item != nullptr) && (item->IsMatch(jsEnv, callback) != RET_ERR)) {
94 MMI_HILOGW("Add js monitor failed");
95 return;
96 }
97 }
98 auto monitor = std::make_shared<JsInputMonitor>(jsEnv, typeName, callback, nextId_++, keys);
99 int32_t ret = monitor->Start(typeName);
100 if (ret < 0) {
101 MMI_HILOGE("Js monitor startup failed");
102 ThrowError(jsEnv, ret);
103 return;
104 }
105 monitors_.push_back(monitor);
106 }
107
RemoveMonitor(napi_env jsEnv,const std::string & typeName,napi_value callback,const int32_t fingers)108 void JsInputMonitorManager::RemoveMonitor(napi_env jsEnv, const std::string &typeName, napi_value callback,
109 const int32_t fingers)
110 {
111 CALL_DEBUG_ENTER;
112 std::shared_ptr<JsInputMonitor> monitor = nullptr;
113 do {
114 std::lock_guard<std::mutex> guard(mutex_);
115 for (auto it = monitors_.begin(); it != monitors_.end();) {
116 if ((*it) == nullptr) {
117 monitors_.erase(it++);
118 continue;
119 }
120 if (IsFindJsInputMonitor(*it, jsEnv, typeName, callback, fingers)) {
121 monitor = *it;
122 monitors_.erase(it++);
123 MMI_HILOGD("Found monitor");
124 break;
125 }
126 ++it;
127 }
128 } while (0);
129 if (monitor != nullptr) {
130 monitor->Stop();
131 }
132 }
133
RemoveMonitor(napi_env jsEnv,const std::string & typeName,const int32_t fingers)134 void JsInputMonitorManager::RemoveMonitor(napi_env jsEnv, const std::string &typeName, const int32_t fingers)
135 {
136 CALL_DEBUG_ENTER;
137 std::list<std::shared_ptr<JsInputMonitor>> monitors;
138 do {
139 std::lock_guard<std::mutex> guard(mutex_);
140 for (auto it = monitors_.begin(); it != monitors_.end();) {
141 if ((*it) == nullptr) {
142 monitors_.erase(it++);
143 continue;
144 }
145 if (IsFindJsInputMonitor(*it, jsEnv, typeName, fingers)) {
146 monitors.push_back(*it);
147 monitors_.erase(it++);
148 continue;
149 }
150 ++it;
151 }
152 } while (0);
153
154 for (const auto &item : monitors) {
155 if (item != nullptr) {
156 item->Stop();
157 }
158 }
159 }
160
RemoveMonitor(napi_env jsEnv)161 void JsInputMonitorManager::RemoveMonitor(napi_env jsEnv)
162 {
163 CALL_DEBUG_ENTER;
164 std::list<std::shared_ptr<JsInputMonitor>> monitors;
165 do {
166 std::lock_guard<std::mutex> guard(mutex_);
167 for (auto it = monitors_.begin(); it != monitors_.end();) {
168 if ((*it) == nullptr) {
169 monitors_.erase(it++);
170 continue;
171 }
172 if ((*it)->IsMatch(jsEnv) == RET_OK) {
173 monitors.push_back(*it);
174 monitors_.erase(it++);
175 continue;
176 }
177 ++it;
178 }
179 } while (0);
180
181 for (const auto &item : monitors) {
182 if (item != nullptr) {
183 item->Stop();
184 }
185 }
186 }
187
OnPointerEventByMonitorId(int32_t id,int32_t fingers,std::shared_ptr<PointerEvent> pointEvent)188 void JsInputMonitorManager::OnPointerEventByMonitorId(int32_t id, int32_t fingers,
189 std::shared_ptr<PointerEvent> pointEvent)
190 {
191 CALL_DEBUG_ENTER;
192 std::lock_guard<std::mutex> guard(mutex_);
193 for (const auto &item : monitors_) {
194 if ((item != nullptr) && (item->GetId() == id && item->GetFingers() == fingers)) {
195 item->OnPointerEvent(pointEvent);
196 }
197 }
198 }
199
OnKeyEventByMonitorId(int32_t id,std::shared_ptr<KeyEvent> keyEvent)200 void JsInputMonitorManager::OnKeyEventByMonitorId(int32_t id, std::shared_ptr<KeyEvent> keyEvent)
201 {
202 CALL_DEBUG_ENTER;
203 std::lock_guard<std::mutex> guard(mutex_);
204 for (const auto &item : monitors_) {
205 if (item != nullptr && item->GetId() == id) {
206 item->OnKeyEvent(keyEvent);
207 }
208 }
209 }
210
GetPreMonitor(int32_t id)211 const std::shared_ptr<JsInputMonitor> JsInputMonitorManager::GetPreMonitor(int32_t id)
212 {
213 CALL_DEBUG_ENTER;
214 std::lock_guard<std::mutex> guard(mutex_);
215 for (const auto &item : monitors_) {
216 if (item != nullptr && item->GetId() == id) {
217 return item;
218 }
219 }
220 MMI_HILOGD("No monitor found");
221 return nullptr;
222 }
223
GetMonitor(int32_t id,int32_t fingers)224 const std::shared_ptr<JsInputMonitor> JsInputMonitorManager::GetMonitor(int32_t id, int32_t fingers)
225 {
226 CALL_DEBUG_ENTER;
227 std::lock_guard<std::mutex> guard(mutex_);
228 for (const auto &item : monitors_) {
229 if ((item != nullptr) && (item->GetId() == id && item->GetFingers() == fingers)) {
230 return item;
231 }
232 }
233 MMI_HILOGD("No monitor found");
234 return nullptr;
235 }
236
GetMonitorTypeName(int32_t id,int32_t fingers)237 std::string JsInputMonitorManager::GetMonitorTypeName(int32_t id, int32_t fingers)
238 {
239 CALL_DEBUG_ENTER;
240 std::lock_guard<std::mutex> guard(mutex_);
241 for (const auto &item : monitors_) {
242 if ((item != nullptr) && (item->GetId() == id && item->GetFingers() == fingers)) {
243 return item->GetTypeName();
244 }
245 }
246 MMI_HILOGD("No monitor found");
247 return "";
248 }
249
GetPreMonitorTypeName(int32_t id)250 std::string JsInputMonitorManager::GetPreMonitorTypeName(int32_t id)
251 {
252 CALL_DEBUG_ENTER;
253 std::lock_guard<std::mutex> guard(mutex_);
254 for (const auto &item : monitors_) {
255 if (item != nullptr && item->GetId() == id) {
256 return item->GetTypeName();
257 }
258 }
259 MMI_HILOGD("No monitor found");
260 return "";
261 }
262
AddEnv(napi_env env,napi_callback_info cbInfo)263 bool JsInputMonitorManager::AddEnv(napi_env env, napi_callback_info cbInfo)
264 {
265 CALL_DEBUG_ENTER;
266 if (IsExisting(env)) {
267 MMI_HILOGD("Env is already existent");
268 return true;
269 }
270 napi_value thisVar = nullptr;
271 void *data = nullptr;
272 int32_t *id = new (std::nothrow) int32_t;
273 CHKPF(id);
274 *id = 0;
275 if (napi_get_cb_info(env, cbInfo, nullptr, nullptr, &thisVar, &data) != napi_ok) {
276 MMI_HILOGE("GET_CB_INFO failed");
277 auto infoTemp = std::string("AddEnv GET_CB_INFO failed");
278 napi_throw_error(env, nullptr, infoTemp.c_str());
279 delete id;
280 id = nullptr;
281 return false;
282 }
283 auto status = napi_wrap(env, thisVar, static_cast<void*>(id),
284 [](napi_env env, void *data, void *hint) {
285 MMI_HILOGD("napi_wrap enter");
286 int32_t *id = static_cast<int32_t *>(data);
287 delete id;
288 id = nullptr;
289 JS_INPUT_MONITOR_MGR.RemoveMonitor(env);
290 JS_INPUT_MONITOR_MGR.RemoveEnv(env);
291 MMI_HILOGD("napi_wrap leave");
292 }, nullptr, nullptr);
293 if (status != napi_ok) {
294 MMI_HILOGE("napi_wrap failed");
295 delete id;
296 return false;
297 }
298 napi_ref ref = nullptr;
299 status = napi_create_reference(env, thisVar, 1, &ref);
300 if (status != napi_ok) {
301 MMI_HILOGE("napi_create_reference failed");
302 return false;
303 }
304 std::lock_guard<std::mutex> lock(envMutex_);
305 auto iter = envManager_.insert(std::pair<napi_env, napi_ref>(env, ref));
306 if (!iter.second) {
307 MMI_HILOGE("Insert value failed");
308 return false;
309 }
310 return true;
311 }
312
RemoveEnv(napi_env env)313 void JsInputMonitorManager::RemoveEnv(napi_env env)
314 {
315 CALL_DEBUG_ENTER;
316 std::lock_guard<std::mutex> lock(envMutex_);
317 auto it = envManager_.find(env);
318 if (it == envManager_.end()) {
319 MMI_HILOGD("No env found");
320 return;
321 }
322 RemoveEnv(it);
323 }
324
RemoveEnv(std::map<napi_env,napi_ref>::iterator it)325 void JsInputMonitorManager::RemoveEnv(std::map<napi_env, napi_ref>::iterator it)
326 {
327 CALL_DEBUG_ENTER;
328 uint32_t refCount = 0;
329 CHKRV(napi_reference_unref(it->first, it->second, &refCount), REFERENCE_UNREF);
330 envManager_.erase(it);
331 }
332
RemoveAllEnv()333 void JsInputMonitorManager::RemoveAllEnv()
334 {
335 CALL_DEBUG_ENTER;
336 std::lock_guard<std::mutex> lock(envMutex_);
337 for (auto it = envManager_.begin(); it != envManager_.end();) {
338 RemoveEnv(it++);
339 }
340 }
341
IsExisting(napi_env env)342 bool JsInputMonitorManager::IsExisting(napi_env env)
343 {
344 CALL_DEBUG_ENTER;
345 std::lock_guard<std::mutex> lock(envMutex_);
346 auto it = envManager_.find(env);
347 if (it == envManager_.end()) {
348 MMI_HILOGD("No env found");
349 return false;
350 }
351
352 return true;
353 }
354
ThrowError(napi_env env,int32_t code)355 void JsInputMonitorManager::ThrowError(napi_env env, int32_t code)
356 {
357 int32_t errorCode = -code;
358 if (errorCode == MONITOR_REGISTER_EXCEED_MAX) {
359 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "Maximum number of listeners exceeded for a single process");
360 } else if (errorCode == COMMON_PERMISSION_CHECK_ERROR) {
361 THROWERR_API9(env, COMMON_PERMISSION_CHECK_ERROR, "monitor", "ohos.permission.INPUT_MONITORING");
362 } else {
363 MMI_HILOGE("Add monitor failed");
364 }
365 }
366
GetHotRectAreaList(napi_env env,napi_value rectNapiValue,uint32_t rectListLength)367 std::vector<Rect> JsInputMonitorManager::GetHotRectAreaList(napi_env env,
368 napi_value rectNapiValue, uint32_t rectListLength)
369 {
370 std::vector<Rect> hotRectAreaList;
371 for (uint32_t i = 0; i < rectListLength; i++) {
372 napi_value napiElement;
373 CHKRR(napi_get_element(env, rectNapiValue, i, &napiElement), GET_ELEMENT, hotRectAreaList);
374 Rect rectItem;
375 napi_value napiX = nullptr;
376 CHKRR(napi_get_named_property(env, napiElement, "left", &napiX), GET_NAMED_PROPERTY, hotRectAreaList);
377 if (napiX == nullptr) {
378 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "left not found");
379 return hotRectAreaList;
380 }
381 int32_t rectX = -1;
382 CHKRR(napi_get_value_int32(env, napiX, &rectX), GET_VALUE_INT32, hotRectAreaList);
383 rectItem.x = rectX;
384 napi_value napiY = nullptr;
385 CHKRR(napi_get_named_property(env, napiElement, "top", &napiY), GET_NAMED_PROPERTY, hotRectAreaList);
386 if (napiY == nullptr) {
387 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "top not found");
388 return hotRectAreaList;
389 }
390 int32_t rectY = -1;
391 CHKRR(napi_get_value_int32(env, napiY, &rectY), GET_VALUE_INT32, hotRectAreaList);
392 rectItem.y = rectY;
393 napi_value napiWidth = nullptr;
394 CHKRR(napi_get_named_property(env, napiElement, "width", &napiWidth), GET_NAMED_PROPERTY, hotRectAreaList);
395 if (napiWidth == nullptr) {
396 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "width not found");
397 return hotRectAreaList;
398 }
399 int32_t rectWidth = -1;
400 CHKRR(napi_get_value_int32(env, napiWidth, &rectWidth), GET_VALUE_INT32, hotRectAreaList);
401 rectItem.width = rectWidth;
402 napi_value napiHeight = nullptr;
403 CHKRR(napi_get_named_property(env, napiElement, "height", &napiHeight), GET_NAMED_PROPERTY, hotRectAreaList);
404 if (napiHeight == nullptr) {
405 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "height not found");
406 return hotRectAreaList;
407 }
408 int32_t rectHeight = -1;
409 CHKRR(napi_get_value_int32(env, napiHeight, &rectHeight), GET_VALUE_INT32, hotRectAreaList);
410 rectItem.height = rectHeight;
411 if (rectX < 0 || rectY < 0 || rectHeight < 0 || rectWidth < 0) {
412 THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "Rect parameter can't be negative");
413 return hotRectAreaList;
414 }
415 hotRectAreaList.push_back(rectItem);
416 }
417 return hotRectAreaList;
418 }
419
IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,napi_env jsEnv,const std::string & typeName,napi_value callback,const int32_t fingers)420 bool JsInputMonitorManager::IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,
421 napi_env jsEnv, const std::string &typeName, napi_value callback, const int32_t fingers)
422 {
423 CHKPF(monitor);
424 if ((monitor->GetTypeName() == typeName) && (typeName == "keyPressed" || monitor->GetFingers() == fingers)) {
425 if (monitor->IsMatch(jsEnv, callback) == RET_OK) {
426 return true;
427 }
428 }
429 return false;
430 }
431
IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,napi_env jsEnv,const std::string & typeName,const int32_t fingers)432 bool JsInputMonitorManager::IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,
433 napi_env jsEnv, const std::string &typeName, const int32_t fingers)
434 {
435 CHKPF(monitor);
436 if ((monitor->GetTypeName() == typeName) && (typeName == "keyPressed" || monitor->GetFingers() == fingers)) {
437 if (monitor->IsMatch(jsEnv) == RET_OK) {
438 return true;
439 }
440 }
441 return false;
442 }
443
GetKeysArray(napi_env env,napi_value keysNapiValue,uint32_t keysLength,std::vector<int32_t> & keys)444 bool JsInputMonitorManager::GetKeysArray(napi_env env, napi_value keysNapiValue, uint32_t keysLength,
445 std::vector<int32_t>& keys)
446 {
447 for (uint32_t i = 0; i < keysLength; i++) {
448 napi_value napiElement;
449 CHKRR(napi_get_element(env, keysNapiValue, i, &napiElement), GET_ELEMENT, false);
450 int32_t keycode;
451 CHKRR(napi_get_value_int32(env, napiElement, &keycode), GET_VALUE_INT32, false);
452 auto it = std::find(supportedKeyCodes.begin(), supportedKeyCodes.end(), keycode);
453 if (it == supportedKeyCodes.end()) {
454 MMI_HILOGE("PreKeys is not expect");
455 return false;
456 }
457 keys.push_back(keycode);
458 }
459 return true;
460 }
461
JsQueryTouchEvents(napi_env env,int32_t count)462 napi_value JsInputMonitorManager::JsQueryTouchEvents(napi_env env, int32_t count)
463 {
464 CALL_DEBUG_ENTER;
465 sptr<JsInputMonitor::CallbackInfo> cb = new (std::nothrow) JsInputMonitor::CallbackInfo();
466 CHKPP(cb);
467 cb->env = env;
468 napi_value promise = nullptr;
469 CHKRP(napi_create_promise(env, &cb->deferred, &promise), CREATE_PROMISE);
470 EmitJsQueryTouchEvents(cb, count);
471 return promise;
472 }
473
EmitJsQueryTouchEvents(sptr<JsInputMonitor::CallbackInfo> cb,int32_t count)474 void JsInputMonitorManager::EmitJsQueryTouchEvents(sptr<JsInputMonitor::CallbackInfo> cb, int32_t count)
475 {
476 CALL_DEBUG_ENTER;
477 CHKPV(cb);
478 CHKPV(cb->env);
479 std::vector<std::shared_ptr<PointerEvent>> touchEventList {};
480 cb->data.count = count;
481 cb->data.touchEventList = touchEventList;
482 cb->errCode = RET_OK;
483 uv_loop_s *loop = nullptr;
484 CHKRV(napi_get_uv_event_loop(cb->env, &loop), GET_UV_EVENT_LOOP);
485 uv_work_t *work = new (std::nothrow) uv_work_t;
486 CHKPV(work);
487 cb->IncStrongRef(nullptr);
488 work->data = cb.GetRefPtr();
489 int32_t ret = -1;
490 ret = uv_queue_work_with_qos(
491 loop,
492 work,
493 [](uv_work_t *work) {
494 MMI_HILOGD("uv_queue_work callback function is called");
495 JsInputMonitor::CallJsQueryTouchEventsTask(work);
496 },
497 JsInputMonitor::CallJsQueryTouchEventsPromise,
498 uv_qos_user_initiated);
499 if (ret != 0) {
500 MMI_HILOGE("uv_queue_work_with_qos failed");
501 cb->DecStrongRef(nullptr);
502 JsInputMonitor::DeletePtr<uv_work_t *>(work);
503 }
504 }
505 } // namespace MMI
506 } // namespace OHOS
507