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