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