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 <uv.h>
19 
20 #include "define_multimodal.h"
21 #include "napi_constants.h"
22 #include "util_napi_error.h"
23 
24 #undef MMI_LOG_TAG
25 #define MMI_LOG_TAG "JsInputMonitorManager"
26 
27 namespace OHOS {
28 namespace MMI {
29 namespace {
30 constexpr int32_t MONITOR_REGISTER_EXCEED_MAX { 4100001 };
31 } // namespace
32 
GetInstance()33 JsInputMonitorManager& JsInputMonitorManager::GetInstance()
34 {
35     static JsInputMonitorManager instance;
36     return instance;
37 }
38 
AddMonitor(napi_env jsEnv,const std::string & typeName,std::vector<Rect> hotRectArea,int32_t rectTotal,napi_value callback,const int32_t fingers)39 void JsInputMonitorManager::AddMonitor(napi_env jsEnv, const std::string &typeName,
40     std::vector<Rect> hotRectArea, int32_t rectTotal, napi_value callback, const int32_t fingers)
41 {
42     CALL_DEBUG_ENTER;
43     std::lock_guard<std::mutex> guard(mutex_);
44     for (const auto &item : monitors_) {
45         if ((item != nullptr) && (item->IsMatch(jsEnv, callback) != RET_ERR)) {
46             MMI_HILOGW("Add js monitor failed");
47             return;
48         }
49     }
50     auto monitor = std::make_shared<JsInputMonitor>(jsEnv, typeName, hotRectArea,
51         rectTotal, callback, nextId_++, fingers);
52     int32_t ret = monitor->Start(typeName);
53     if (ret < 0) {
54         MMI_HILOGE("js monitor startup failed");
55         ThrowError(jsEnv, ret);
56         return;
57     }
58     monitors_.push_back(monitor);
59 }
60 
AddMonitor(napi_env jsEnv,const std::string & typeName,napi_value callback,const int32_t fingers)61 void JsInputMonitorManager::AddMonitor(napi_env jsEnv, const std::string &typeName,
62     napi_value callback, const int32_t fingers)
63 {
64     CALL_DEBUG_ENTER;
65     std::lock_guard<std::mutex> guard(mutex_);
66     for (const auto &item : monitors_) {
67         if ((item != nullptr) && (item->IsMatch(jsEnv, callback) != RET_ERR)) {
68             MMI_HILOGW("Add js monitor failed");
69             return;
70         }
71     }
72     auto monitor = std::make_shared<JsInputMonitor>(jsEnv, typeName, callback, nextId_++, fingers);
73     int32_t ret = monitor->Start(typeName);
74     if (ret < 0) {
75         MMI_HILOGE("js monitor startup failed");
76         ThrowError(jsEnv, ret);
77         return;
78     }
79     monitors_.push_back(monitor);
80 }
81 
RemoveMonitor(napi_env jsEnv,const std::string & typeName,napi_value callback,const int32_t fingers)82 void JsInputMonitorManager::RemoveMonitor(napi_env jsEnv, const std::string &typeName, napi_value callback,
83     const int32_t fingers)
84 {
85     CALL_DEBUG_ENTER;
86     std::shared_ptr<JsInputMonitor> monitor = nullptr;
87     do {
88         std::lock_guard<std::mutex> guard(mutex_);
89         for (auto it = monitors_.begin(); it != monitors_.end();) {
90             if ((*it) == nullptr) {
91                 monitors_.erase(it++);
92                 continue;
93             }
94             if (IsFindJsInputMonitor(*it, jsEnv, typeName, callback, fingers)) {
95                 monitor = *it;
96                 monitors_.erase(it++);
97                 MMI_HILOGD("Found monitor");
98                 break;
99             }
100             ++it;
101         }
102     } while (0);
103     if (monitor != nullptr) {
104         monitor->Stop();
105     }
106 }
107 
RemoveMonitor(napi_env jsEnv,const std::string & typeName,const int32_t fingers)108 void JsInputMonitorManager::RemoveMonitor(napi_env jsEnv, const std::string &typeName, const int32_t fingers)
109 {
110     CALL_DEBUG_ENTER;
111     std::list<std::shared_ptr<JsInputMonitor>> monitors;
112     do {
113         std::lock_guard<std::mutex> guard(mutex_);
114         for (auto it = monitors_.begin(); it != monitors_.end();) {
115             if ((*it) == nullptr) {
116                 monitors_.erase(it++);
117                 continue;
118             }
119             if (IsFindJsInputMonitor(*it, jsEnv, typeName, fingers)) {
120                 monitors.push_back(*it);
121                 monitors_.erase(it++);
122                 continue;
123             }
124             ++it;
125         }
126     } while (0);
127 
128     for (const auto &item : monitors) {
129         if (item != nullptr) {
130             item->Stop();
131         }
132     }
133 }
134 
RemoveMonitor(napi_env jsEnv)135 void JsInputMonitorManager::RemoveMonitor(napi_env jsEnv)
136 {
137     CALL_DEBUG_ENTER;
138     std::list<std::shared_ptr<JsInputMonitor>> monitors;
139     do {
140         std::lock_guard<std::mutex> guard(mutex_);
141         for (auto it = monitors_.begin(); it != monitors_.end();) {
142             if ((*it) == nullptr) {
143                 monitors_.erase(it++);
144                 continue;
145             }
146             if ((*it)->IsMatch(jsEnv) == RET_OK) {
147                 monitors.push_back(*it);
148                 monitors_.erase(it++);
149                 continue;
150             }
151             ++it;
152         }
153     } while (0);
154 
155     for (const auto &item : monitors) {
156         if (item != nullptr) {
157             item->Stop();
158         }
159     }
160 }
161 
OnPointerEventByMonitorId(int32_t id,int32_t fingers,std::shared_ptr<PointerEvent> pointEvent)162 void JsInputMonitorManager::OnPointerEventByMonitorId(int32_t id, int32_t fingers,
163     std::shared_ptr<PointerEvent> pointEvent)
164 {
165     CALL_DEBUG_ENTER;
166     std::lock_guard<std::mutex> guard(mutex_);
167     for (const auto &item : monitors_) {
168         if ((item != nullptr) && (item->GetId() == id && item->GetFingers() == fingers)) {
169             item->OnPointerEvent(pointEvent);
170         }
171     }
172 }
173 
GetMonitor(int32_t id,int32_t fingers)174 const std::shared_ptr<JsInputMonitor> JsInputMonitorManager::GetMonitor(int32_t id, int32_t fingers)
175 {
176     CALL_DEBUG_ENTER;
177     std::lock_guard<std::mutex> guard(mutex_);
178     for (const auto &item : monitors_) {
179         if ((item != nullptr) && (item->GetId() == id && item->GetFingers() == fingers)) {
180             return item;
181         }
182     }
183     MMI_HILOGD("No monitor found");
184     return nullptr;
185 }
186 
GetMonitorTypeName(int32_t id,int32_t fingers)187 std::string JsInputMonitorManager::GetMonitorTypeName(int32_t id, int32_t fingers)
188 {
189     CALL_DEBUG_ENTER;
190     std::lock_guard<std::mutex> guard(mutex_);
191     for (const auto &item : monitors_) {
192         if ((item != nullptr) && (item->GetId() == id && item->GetFingers() == fingers)) {
193             return item->GetTypeName();
194         }
195     }
196     MMI_HILOGD("No monitor found");
197     return "";
198 }
199 
AddEnv(napi_env env,napi_callback_info cbInfo)200 bool JsInputMonitorManager::AddEnv(napi_env env, napi_callback_info cbInfo)
201 {
202     CALL_DEBUG_ENTER;
203     if (IsExisting(env)) {
204         MMI_HILOGD("Env is already existent");
205         return true;
206     }
207     napi_value thisVar = nullptr;
208     void *data = nullptr;
209     int32_t *id = new (std::nothrow) int32_t;
210     CHKPF(id);
211     *id = 0;
212     if (napi_get_cb_info(env, cbInfo, nullptr, nullptr, &thisVar, &data) != napi_ok) {
213         MMI_HILOGE("GET_CB_INFO failed");
214         auto infoTemp = std::string("AddEnv GET_CB_INFO failed");
215         napi_throw_error(env, nullptr, infoTemp.c_str());
216         delete id;
217         return false;
218     }
219     auto status = napi_wrap(env, thisVar, static_cast<void*>(id),
220                             [](napi_env env, void *data, void *hint) {
221                                 MMI_HILOGD("napi_wrap enter");
222                                 int32_t *id = static_cast<int32_t *>(data);
223                                 delete id;
224                                 id = nullptr;
225                                 JS_INPUT_MONITOR_MGR.RemoveMonitor(env);
226                                 JS_INPUT_MONITOR_MGR.RemoveEnv(env);
227                                 MMI_HILOGD("napi_wrap leave");
228                                 }, nullptr, nullptr);
229     if (status != napi_ok) {
230         MMI_HILOGE("napi_wrap failed");
231         delete id;
232         return false;
233     }
234     napi_ref ref = nullptr;
235     status = napi_create_reference(env, thisVar, 1, &ref);
236     if (status != napi_ok) {
237         MMI_HILOGE("napi_create_reference failed");
238         return false;
239     }
240     std::lock_guard<std::mutex> lock(envMutex_);
241     auto iter = envManager_.insert(std::pair<napi_env, napi_ref>(env, ref));
242     if (!iter.second) {
243         MMI_HILOGE("Insert value failed");
244         return false;
245     }
246     return true;
247 }
248 
RemoveEnv(napi_env env)249 void JsInputMonitorManager::RemoveEnv(napi_env env)
250 {
251     CALL_DEBUG_ENTER;
252     std::lock_guard<std::mutex> lock(envMutex_);
253     auto it = envManager_.find(env);
254     if (it == envManager_.end()) {
255         MMI_HILOGD("No env found");
256         return;
257     }
258     RemoveEnv(it);
259 }
260 
RemoveEnv(std::map<napi_env,napi_ref>::iterator it)261 void JsInputMonitorManager::RemoveEnv(std::map<napi_env, napi_ref>::iterator it)
262 {
263     CALL_DEBUG_ENTER;
264     uint32_t refCount = 0;
265     CHKRV(napi_reference_unref(it->first, it->second, &refCount), REFERENCE_UNREF);
266     envManager_.erase(it);
267 }
268 
RemoveAllEnv()269 void JsInputMonitorManager::RemoveAllEnv()
270 {
271     CALL_DEBUG_ENTER;
272     std::lock_guard<std::mutex> lock(envMutex_);
273     for (auto it = envManager_.begin(); it != envManager_.end();) {
274         RemoveEnv(it++);
275     }
276 }
277 
IsExisting(napi_env env)278 bool JsInputMonitorManager::IsExisting(napi_env env)
279 {
280     CALL_DEBUG_ENTER;
281     std::lock_guard<std::mutex> lock(envMutex_);
282     auto it = envManager_.find(env);
283     if (it == envManager_.end()) {
284         MMI_HILOGD("No env found");
285         return false;
286     }
287 
288     return true;
289 }
290 
ThrowError(napi_env env,int32_t code)291 void JsInputMonitorManager::ThrowError(napi_env env, int32_t code)
292 {
293     int32_t errorCode = -code;
294     if (errorCode == MONITOR_REGISTER_EXCEED_MAX) {
295         THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "Maximum number of listeners exceeded for a single process");
296     } else if (errorCode == COMMON_PERMISSION_CHECK_ERROR) {
297         THROWERR_API9(env, COMMON_PERMISSION_CHECK_ERROR, "monitor", "ohos.permission.INPUT_MONITORING");
298     } else {
299         MMI_HILOGE("Add monitor failed");
300     }
301 }
302 
GetHotRectAreaList(napi_env env,napi_value rectNapiValue,uint32_t rectListLength)303 std::vector<Rect> JsInputMonitorManager::GetHotRectAreaList(napi_env env,
304     napi_value rectNapiValue, uint32_t rectListLength)
305 {
306     std::vector<Rect> hotRectAreaList;
307     for (uint32_t i = 0; i < rectListLength; i++) {
308         napi_value napiElement;
309         CHKRR(napi_get_element(env, rectNapiValue, i, &napiElement), GET_ELEMENT, hotRectAreaList);
310         Rect rectItem;
311         napi_value napiX = nullptr;
312         CHKRR(napi_get_named_property(env, napiElement, "left", &napiX), GET_NAMED_PROPERTY, hotRectAreaList);
313         if (napiX == nullptr) {
314             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "left not found");
315             return hotRectAreaList;
316         }
317         int32_t rectX = -1;
318         CHKRR(napi_get_value_int32(env, napiX, &rectX), GET_VALUE_INT32, hotRectAreaList);
319         rectItem.x = rectX;
320         napi_value napiY = nullptr;
321         CHKRR(napi_get_named_property(env, napiElement, "top", &napiY), GET_NAMED_PROPERTY, hotRectAreaList);
322         if (napiY == nullptr) {
323             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "top not found");
324             return hotRectAreaList;
325         }
326         int32_t rectY = -1;
327         CHKRR(napi_get_value_int32(env, napiY, &rectY), GET_VALUE_INT32, hotRectAreaList);
328         rectItem.y = rectY;
329         napi_value napiWidth = nullptr;
330         CHKRR(napi_get_named_property(env, napiElement, "width", &napiWidth), GET_NAMED_PROPERTY, hotRectAreaList);
331         if (napiWidth == nullptr) {
332             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "width not found");
333             return hotRectAreaList;
334         }
335         int32_t rectWidth = -1;
336         CHKRR(napi_get_value_int32(env, napiWidth, &rectWidth), GET_VALUE_INT32, hotRectAreaList);
337         rectItem.width = rectWidth;
338         napi_value napiHeight = nullptr;
339         CHKRR(napi_get_named_property(env, napiElement, "height", &napiHeight), GET_NAMED_PROPERTY, hotRectAreaList);
340         if (napiHeight == nullptr) {
341             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "height not found");
342             return hotRectAreaList;
343         }
344         int32_t rectHeight = -1;
345         CHKRR(napi_get_value_int32(env, napiHeight, &rectHeight), GET_VALUE_INT32, hotRectAreaList);
346         rectItem.height = rectHeight;
347         if (rectX < 0 || rectY < 0 || rectHeight < 0 || rectWidth < 0) {
348             THROWERR_CUSTOM(env, COMMON_PARAMETER_ERROR, "Rect parameter can't be negative");
349             return hotRectAreaList;
350         }
351         hotRectAreaList.push_back(rectItem);
352     }
353     return hotRectAreaList;
354 }
355 
IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,napi_env jsEnv,const std::string & typeName,napi_value callback,const int32_t fingers)356 bool JsInputMonitorManager::IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,
357     napi_env jsEnv, const std::string &typeName, napi_value callback, const int32_t fingers)
358 {
359     if ((monitor->GetTypeName() == typeName) && (monitor->GetFingers() == fingers)) {
360         if (monitor->IsMatch(jsEnv, callback) == RET_OK) {
361             return true;
362         }
363     }
364     return false;
365 }
366 
IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,napi_env jsEnv,const std::string & typeName,const int32_t fingers)367 bool JsInputMonitorManager::IsFindJsInputMonitor(const std::shared_ptr<JsInputMonitor> monitor,
368     napi_env jsEnv, const std::string &typeName, const int32_t fingers)
369 {
370     if ((monitor->GetTypeName() == typeName) && (monitor->GetFingers() == fingers)) {
371         if (monitor->IsMatch(jsEnv) == RET_OK) {
372             return true;
373         }
374     }
375     return false;
376 }
377 } // namespace MMI
378 } // namespace OHOS
379