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