• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "motion_napi.h"
17 
18 #include <mutex>
19 #include "devicestatus_define.h"
20 #include "fi_log.h"
21 #ifdef MOTION_ENABLE
22 #include "motion_client.h"
23 #endif
24 #include "motion_napi_error.h"
25 #include "parameters.h"
26 
27 #undef LOG_TAG
28 #define LOG_TAG "DeviceMotionNapi"
29 
30 namespace OHOS {
31 namespace Msdp {
32 namespace {
33 #ifdef MOTION_ENABLE
34 auto &g_motionClient = MotionClient::GetInstance();
35 constexpr int32_t PERMISSION_DENIED = 201;
36 static constexpr uint8_t ARG_1 = 1;
37 #endif
38 static constexpr uint8_t ARG_0 = 0;
39 static constexpr uint8_t ARG_2 = 2;
40 constexpr int32_t INVALID_MOTION_TYPE = -1;
41 constexpr size_t MAX_ARG_STRING_LEN = 512;
42 constexpr int32_t MOTION_TYPE_OPERATING_HAND = 3601;
43 constexpr int32_t MOTION_TYPE_STAND = 3602;
44 constexpr int32_t MOTION_TYPE_REMOTE_PHOTO = 3604;
45 constexpr int32_t BASE_HAND = 0;
46 constexpr int32_t LEFT_HAND = 1;
47 constexpr int32_t RIGHT_HAND = 2;
48 const std::vector<std::string> EXPECTED_SUB_ARG_TYPES = { "string", "function" };
49 const std::vector<std::string> EXPECTED_UNSUB_ONE_ARG_TYPES = { "string" };
50 const std::vector<std::string> EXPECTED_UNSUB_TWO_ARG_TYPES = { "string", "function" };
51 const std::map<const std::string, int32_t> MOTION_TYPE_MAP = {
52     { "operatingHandChanged", MOTION_TYPE_OPERATING_HAND },
53     { "steadyStandingDetect", MOTION_TYPE_STAND },
54     { "remotePhotoStandingDetect", MOTION_TYPE_REMOTE_PHOTO },
55 };
56 MotionNapi *g_motionObj = nullptr;
57 } // namespace
58 
59 std::mutex g_mutex;
60 
61 #ifdef MOTION_ENABLE
OnMotionChanged(const MotionEvent & event)62 void MotionCallback::OnMotionChanged(const MotionEvent &event)
63 {
64     FI_HILOGD("Enter");
65     std::lock_guard<std::mutex> guard(g_mutex);
66     auto* data = new (std::nothrow) MotionEvent();
67     CHKPV(data);
68     data->type = event.type;
69     data->status = event.status;
70     data->dataLen = event.dataLen;
71     data->data = event.data;
72 
73     auto task = [data]() {
74         FI_HILOGI("Execute lamdba");
75         EmitOnEvent(data);
76     };
77     if (napi_status::napi_ok != napi_send_event(env_, task, napi_eprio_immediate)) {
78         FI_HILOGE("Failed to SendEvent");
79     }
80     FI_HILOGD("Exit");
81 }
82 
EmitOnEvent(MotionEvent * data)83 void MotionCallback::EmitOnEvent(MotionEvent* data)
84 {
85     if (data == nullptr) {
86         FI_HILOGE("data is nullptr");
87         return;
88     }
89 
90     if (g_motionObj == nullptr) {
91         FI_HILOGE("Failed to get g_motionObj");
92         delete data;
93     }
94     g_motionObj->OnEventOperatingHand(data->type, 1, *data);
95     delete data;
96 }
97 #endif
98 
MotionNapi(napi_env env,napi_value thisVar)99 MotionNapi::MotionNapi(napi_env env, napi_value thisVar) : MotionEventNapi(env, thisVar)
100 {
101     env_ = env;
102 }
103 
~MotionNapi()104 MotionNapi::~MotionNapi()
105 {}
106 
GetMotionType(const std::string & type)107 int32_t MotionNapi::GetMotionType(const std::string &type)
108 {
109     FI_HILOGD("Enter");
110     auto iter = MOTION_TYPE_MAP.find(type);
111     if (iter == MOTION_TYPE_MAP.end()) {
112         FI_HILOGD("Don't find this type");
113         return INVALID_MOTION_TYPE;
114     }
115     FI_HILOGD("Exit");
116     return iter->second;
117 }
118 
119 #ifdef MOTION_ENABLE
SubscribeCallback(napi_env env,int32_t type)120 bool MotionNapi::SubscribeCallback(napi_env env, int32_t type)
121 {
122     if (g_motionObj == nullptr) {
123         ThrowMotionErr(env, SUBSCRIBE_EXCEPTION, "g_motionObj is nullptr");
124         return false;
125     }
126 
127     auto iter = g_motionObj->callbacks_.find(type);
128     if (iter == g_motionObj->callbacks_.end()) {
129         FI_HILOGD("Don't find callback, to create");
130         sptr<IMotionCallback> callback = new (std::nothrow) MotionCallback(env);
131         int32_t ret = g_motionClient.SubscribeCallback(type, callback);
132         if (ret == RET_OK) {
133             g_motionObj->callbacks_.insert(std::make_pair(type, callback));
134             return true;
135         }
136 
137         if (ret == PERMISSION_DENIED) {
138             FI_HILOGE("failed to subscribe");
139             ThrowMotionErr(env, PERMISSION_EXCEPTION, "Permission denined");
140             return false;
141         } else {
142             FI_HILOGE("failed to subscribe");
143             ThrowMotionErr(env, SUBSCRIBE_EXCEPTION, "Subscribe failed");
144             return false;
145         }
146     }
147     return false;
148 }
149 
UnSubscribeCallback(napi_env env,int32_t type)150 bool MotionNapi::UnSubscribeCallback(napi_env env, int32_t type)
151 {
152     if (g_motionObj == nullptr) {
153         ThrowMotionErr(env, UNSUBSCRIBE_EXCEPTION, "g_motionObj is nullptr");
154         return false;
155     }
156 
157     if (g_motionObj->CheckEvents(type)) {
158         auto iter = g_motionObj->callbacks_.find(type);
159         if (iter == g_motionObj->callbacks_.end()) {
160             FI_HILOGE("faild to find callback");
161             ThrowMotionErr(env, UNSUBSCRIBE_EXCEPTION, "Unsubscribe failed");
162             return false;
163         }
164         int32_t ret = g_motionClient.UnsubscribeCallback(type, iter->second);
165         if (ret == RET_OK) {
166             g_motionObj->callbacks_.erase(iter);
167             return true;
168         }
169 
170         if (ret == PERMISSION_DENIED) {
171             FI_HILOGE("failed to unsubscribe");
172             ThrowMotionErr(env, PERMISSION_EXCEPTION, "Permission denined");
173             return false;
174         } else {
175             FI_HILOGE("failed to unsubscribe");
176             ThrowMotionErr(env, UNSUBSCRIBE_EXCEPTION, "Unsubscribe failed");
177             return false;
178         }
179     }
180     return false;
181 }
182 #endif
183 
ConstructMotion(napi_env env,napi_value jsThis)184 bool MotionNapi::ConstructMotion(napi_env env, napi_value jsThis) __attribute__((no_sanitize("cfi")))
185 {
186     std::lock_guard<std::mutex> guard(g_mutex);
187     if (g_motionObj == nullptr) {
188         g_motionObj = new (std::nothrow) MotionNapi(env, jsThis);
189         if (g_motionObj == nullptr) {
190             FI_HILOGE("faild to get g_motionObj");
191             return false;
192         }
193         napi_status status = napi_wrap(env, jsThis, reinterpret_cast<void *>(g_motionObj),
194             [](napi_env env, void *data, void *hint) {
195                 (void)env;
196                 (void)hint;
197                 if (data != nullptr) {
198                     MotionNapi *motion = reinterpret_cast<MotionNapi *>(data);
199                     delete motion;
200                 }
201             }, nullptr, nullptr);
202         if (status != napi_ok) {
203             delete g_motionObj;
204             g_motionObj = nullptr;
205             FI_HILOGE("napi_wrap failed");
206             return false;
207         }
208     }
209     return true;
210 }
211 
SubscribeMotion(napi_env env,napi_callback_info info)212 napi_value MotionNapi::SubscribeMotion(napi_env env, napi_callback_info info)
213 {
214     FI_HILOGD("Enter");
215     if (!CheckDeviceType()) {
216         ThrowMotionErr(env, DEVICE_EXCEPTION, "Device not support");
217         return nullptr;
218     }
219     size_t argc = ARG_2;
220     napi_value args[ARG_2] = { nullptr };
221     napi_value jsThis = nullptr;
222     napi_value result = nullptr;
223     napi_status status = napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
224     if (status != napi_ok) {
225         ThrowMotionErr(env, SUBSCRIBE_EXCEPTION, "napi_get_cb_info failed");
226         return nullptr;
227     }
228 
229     if (!ValidateArgsType(env, args, argc, EXPECTED_SUB_ARG_TYPES)) {
230         ThrowMotionErr(env, PARAM_EXCEPTION, "validateargstype failed");
231         return nullptr;
232     }
233 
234     std::string typeStr;
235     if (!TransJsToStr(env, args[ARG_0], typeStr)) {
236         ThrowMotionErr(env, SUBSCRIBE_EXCEPTION, "Trans to string failed");
237         return nullptr;
238     }
239 
240     int32_t type = GetMotionType(typeStr);
241     if (type == INVALID_MOTION_TYPE) {
242         ThrowMotionErr(env, PARAM_EXCEPTION, "Type is illegal");
243         return nullptr;
244     }
245 
246     if (!ConstructMotion(env, jsThis)) {
247         ThrowMotionErr(env, SUBSCRIBE_EXCEPTION, "Failed to get g_motionObj");
248         return nullptr;
249     }
250 
251 #ifdef MOTION_ENABLE
252     if (!SubscribeCallback(env, type)) {
253         return nullptr;
254     }
255 
256     if (!g_motionObj->AddCallback(type, args[ARG_1])) {
257         ThrowMotionErr(env, SERVICE_EXCEPTION, "AddCallback failed");
258         return nullptr;
259     }
260     napi_get_undefined(env, &result);
261     return result;
262 #else
263     ThrowMotionErr(env, DEVICE_EXCEPTION, "Device not support");
264     return result;
265 #endif
266 }
267 
UnSubscribeMotion(napi_env env,napi_callback_info info)268 napi_value MotionNapi::UnSubscribeMotion(napi_env env, napi_callback_info info)
269 {
270     FI_HILOGD("Enter");
271     if (!CheckDeviceType()) {
272         ThrowMotionErr(env, DEVICE_EXCEPTION, "Device not support");
273         return nullptr;
274     }
275     if (g_motionObj == nullptr) {
276         ThrowMotionErr(env, UNSUBSCRIBE_EXCEPTION, "g_motionObj is nullptr");
277         return nullptr;
278     }
279 
280     size_t argc = ARG_2;
281     napi_value args[ARG_2] = { nullptr };
282     napi_value jsThis = nullptr;
283     napi_value result = nullptr;
284     napi_status status = napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr);
285     if (status != napi_ok) {
286         ThrowMotionErr(env, UNSUBSCRIBE_EXCEPTION, "napi_get_cb_info is failed");
287         return nullptr;
288     }
289 
290     auto expectedArgs = EXPECTED_UNSUB_TWO_ARG_TYPES;
291     if (argc != ARG_2) {
292         expectedArgs = EXPECTED_UNSUB_ONE_ARG_TYPES;
293     }
294     if (!ValidateArgsType(env, args, argc, expectedArgs)) {
295         ThrowMotionErr(env, PARAM_EXCEPTION, "validateargstype failed");
296         return nullptr;
297     }
298 
299     std::string typeStr;
300     if (!TransJsToStr(env, args[ARG_0], typeStr)) {
301         ThrowMotionErr(env, UNSUBSCRIBE_EXCEPTION, "Trans to string failed");
302         return nullptr;
303     }
304 
305     int32_t type = GetMotionType(typeStr);
306     if (type == INVALID_MOTION_TYPE) {
307         ThrowMotionErr(env, PARAM_EXCEPTION, "Type is illegal");
308         return nullptr;
309     }
310 
311 #ifdef MOTION_ENABLE
312     if (!g_motionObj->RemoveCallback(type)) {
313         ThrowMotionErr(env, SERVICE_EXCEPTION, "RemoveCallback failed");
314         return nullptr;
315     }
316 
317     if (!UnSubscribeCallback(env, type)) {
318         return nullptr;
319     }
320     napi_get_undefined(env, &result);
321     return result;
322 #else
323     ThrowMotionErr(env, DEVICE_EXCEPTION, "Device not support");
324     return result;
325 #endif
326 }
327 
GetRecentOptHandStatus(napi_env env,napi_callback_info info)328 napi_value MotionNapi::GetRecentOptHandStatus(napi_env env, napi_callback_info info)
329 {
330     FI_HILOGD("Enter");
331     if (!CheckDeviceType()) {
332         ThrowMotionErr(env, DEVICE_EXCEPTION, "Device not support");
333         return nullptr;
334     }
335     napi_value result = nullptr;
336     size_t argc = ARG_0;
337     napi_value jsThis;
338 
339     napi_status status = napi_get_cb_info(env, info, &argc, NULL, &jsThis, nullptr);
340     if (status != napi_ok) {
341         ThrowMotionErr(env, SERVICE_EXCEPTION, "napi_get_cb_info failed");
342         return nullptr;
343     }
344 
345 #ifdef MOTION_ENABLE
346     MotionEvent motionEvent = g_motionClient.GetMotionData(MOTION_TYPE_OPERATING_HAND);
347     if (motionEvent.status == -1) {
348         ThrowMotionErr(env, PERMISSION_EXCEPTION, "Invalid Type");
349         return nullptr;
350     }
351 #endif
352 
353     ConstructMotion(env, jsThis);
354 #ifdef MOTION_ENABLE
355     if (g_motionObj == nullptr) {
356         ThrowMotionErr(env, SERVICE_EXCEPTION, "Error invalid type");
357         return nullptr;
358     }
359     napi_status ret = napi_create_int32(env, static_cast<int32_t>(motionEvent.status), &result);
360     if (ret != napi_ok) {
361         ThrowMotionErr(env, SERVICE_EXCEPTION, "napi_create_int32 failed");
362         return nullptr;
363     }
364 #else
365     ThrowMotionErr(env, DEVICE_EXCEPTION, "Device not support");
366 #endif
367     FI_HILOGD("Exit");
368     return result;
369 }
370 
Init(napi_env env,napi_value exports)371 napi_value MotionNapi::Init(napi_env env, napi_value exports)
372 {
373     FI_HILOGD("Enter");
374     napi_property_descriptor desc[] = {
375         DECLARE_NAPI_STATIC_FUNCTION("on", SubscribeMotion),
376         DECLARE_NAPI_STATIC_FUNCTION("off", UnSubscribeMotion),
377         DECLARE_NAPI_STATIC_FUNCTION("getRecentOperatingHandStatus", GetRecentOptHandStatus),
378     };
379     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc)/sizeof(desc[0]), desc));
380 
381     napi_value operatingHandStatus;
382     napi_status status = napi_create_object(env, &operatingHandStatus);
383     if (status != napi_ok) {
384         FI_HILOGE("Failed create object");
385         return exports;
386     }
387 
388     SetInt32Property(env, operatingHandStatus, BASE_HAND, "UNKNOWN_STATUS");
389     SetInt32Property(env, operatingHandStatus, LEFT_HAND, "LEFT_HAND_OPERATED");
390     SetInt32Property(env, operatingHandStatus, RIGHT_HAND, "RIGHT_HAND_OPERATED");
391     SetPropertyName(env, exports, "OperatingHandStatus", operatingHandStatus);
392     FI_HILOGD("Exit");
393     return exports;
394 }
395 
CheckDeviceType()396 bool MotionNapi::CheckDeviceType()
397 {
398     std::string model = OHOS::system::GetParameter("const.build.product", "0");
399     if (model == "VDE" || model == "ALN") {
400         return true;
401     }
402     FI_HILOGE("The device is not support");
403     return false;
404 }
405 
SetInt32Property(napi_env env,napi_value targetObj,int32_t value,const char * propName)406 void MotionNapi::SetInt32Property(napi_env env, napi_value targetObj, int32_t value, const char *propName)
407 {
408     napi_value prop = nullptr;
409     napi_status ret = napi_create_int32(env, value, &prop);
410     if (ret != napi_ok) {
411         FI_HILOGE("napi_create_int32 failed");
412         return;
413     }
414     SetPropertyName(env, targetObj, propName, prop);
415 }
416 
SetPropertyName(napi_env env,napi_value targetObj,const char * propName,napi_value propValue)417 void MotionNapi::SetPropertyName(napi_env env, napi_value targetObj, const char *propName, napi_value propValue)
418 {
419     napi_status status = napi_set_named_property(env, targetObj, propName, propValue);
420     if (status != napi_ok) {
421         FI_HILOGE("Failed to set the name property");
422         return;
423     }
424 }
425 
ValidateArgsType(napi_env env,napi_value * args,size_t argc,const std::vector<std::string> & expectedTypes)426 bool MotionNapi::ValidateArgsType(napi_env env, napi_value *args, size_t argc,
427     const std::vector<std::string> &expectedTypes)
428 {
429     FI_HILOGD("Enter");
430     napi_status status = napi_ok;
431     napi_valuetype valueType = napi_undefined;
432 
433     if (argc != expectedTypes.size()) {
434         FI_HILOGE("Wrong number of arguments");
435         return false;
436     }
437 
438     for (size_t i = 0; i < argc; ++i) {
439         status = napi_typeof(env, args[i], &valueType);
440         if (status != napi_ok) {
441             FI_HILOGE("Error while checking arguments types");
442             return false;
443         }
444         std::string expectedType = expectedTypes[i];
445         if ((expectedType == "string" && valueType != napi_string) ||
446             (expectedType == "function" && valueType != napi_function)) {
447                 FI_HILOGE("Wrong argument type");
448                 return false;
449         }
450     }
451     return true;
452 }
453 
TransJsToStr(napi_env env,napi_value value,std::string & str)454 bool MotionNapi::TransJsToStr(napi_env env, napi_value value, std::string &str)
455 {
456     FI_HILOGD("Enter");
457     size_t strlen = 0;
458     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &strlen);
459     if (status != napi_ok) {
460         FI_HILOGE("Error string length invalid");
461         return false;
462     }
463     if (strlen < 0 || strlen > MAX_ARG_STRING_LEN) {
464         FI_HILOGE("The string length invalid");
465         return false;
466     }
467     std::vector<char> buf(strlen + 1);
468     status = napi_get_value_string_utf8(env, value, buf.data(), strlen+1, &strlen);
469     if (status != napi_ok) {
470         FI_HILOGE("napi_get_value_string_utf8 failed");
471         return false;
472     }
473     str = buf.data();
474     return true;
475 }
476 
477 EXTERN_C_START
478 /*
479  * function for module exports
480  */
MotionInit(napi_env env,napi_value exports)481 static napi_value MotionInit(napi_env env, napi_value exports)
482 {
483     FI_HILOGD("Enter");
484     napi_value ret = MotionNapi::Init(env, exports);
485     if (ret == nullptr) {
486         FI_HILOGE("Failed to init");
487         return ret;
488     }
489     FI_HILOGD("Exit");
490     return ret;
491 }
492 EXTERN_C_END
493 
494 /*
495  * Module definition
496  */
497 static napi_module g_module = {
498     .nm_version = 1,
499     .nm_flags = 0,
500     .nm_filename = "multimodalAwareness.motion",
501     .nm_register_func = MotionInit,
502     .nm_modname = "multimodalAwareness.motion",
503     .nm_priv = (static_cast<void *>(nullptr)),
504     .reserved = { nullptr }
505 };
506 
507 /*
508  * Module registration
509  */
RegisterModule(void)510 extern "C" __attribute__((constructor)) void RegisterModule(void)
511 {
512     napi_module_register(&g_module);
513 }
514 } // namespace Msdp
515 } // namespace OHOS
516