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