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