1 /*
2 * Copyright (c) 2022-2023 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 "devicestatus_napi.h"
17
18 #include <js_native_api.h>
19
20 #include "napi/native_api.h"
21 #include "napi/native_node_api.h"
22
23 #include "devicestatus_client.h"
24 #include "devicestatus_common.h"
25
26 using namespace OHOS;
27 using namespace OHOS::Msdp;
28 namespace {
29 constexpr size_t ARG_0 = 0;
30 constexpr size_t ARG_1 = 1;
31 constexpr size_t ARG_2 = 2;
32 constexpr size_t ARG_3 = 3;
33 constexpr size_t ARG_4 = 4;
34 constexpr int32_t NAPI_BUF_LENGTH = 256;
35 static const std::vector<std::string> vecDeviceStatusValue {
36 "VALUE_ENTER", "VALUE_EXIT"
37 };
38 thread_local DeviceStatusNapi *g_obj = nullptr;
39 } // namespace
40 std::map<int32_t, sptr<IdevicestatusCallback>> DeviceStatusNapi::callbackMap_;
41 napi_ref DeviceStatusNapi::devicestatusValueRef_ = nullptr;
42
43 struct ResponseEntity {
44 DevicestatusDataUtils::DevicestatusValue value;
45 };
46
OnDevicestatusChanged(const DevicestatusDataUtils::DevicestatusData & devicestatusData)47 void DeviceStatusCallback::OnDevicestatusChanged(const DevicestatusDataUtils::DevicestatusData& devicestatusData)
48 {
49 DEV_HILOGD(JS_NAPI, "OnDevicestatusChanged enter");
50 std::lock_guard<std::mutex> guard(mutex_);
51 uv_loop_s *loop = nullptr;
52 napi_get_uv_event_loop(env_, &loop);
53 if (loop == nullptr) {
54 DEV_HILOGE(JS_NAPI, "loop is nullptr");
55 return;
56 }
57 uv_work_t *work = new (std::nothrow) uv_work_t;
58 if (work == nullptr) {
59 DEV_HILOGE(JS_NAPI, "work is nullptr");
60 return;
61 }
62 DEV_HILOGD(JS_NAPI, "devicestatusData.type:%{public}d, devicestatusData.value:%{public}d",
63 devicestatusData.type, devicestatusData.value);
64 data_ = devicestatusData;
65 work->data = static_cast<void *>(&data_);
66 int ret = uv_queue_work(loop, work, [] (uv_work_t *work) {}, EmitOnEvent);
67 if (ret != 0) {
68 DEV_HILOGE(JS_NAPI, "Failed to execute work queue");
69 }
70 }
71
EmitOnEvent(uv_work_t * work,int status)72 void DeviceStatusCallback::EmitOnEvent(uv_work_t *work, int status)
73 {
74 DevicestatusDataUtils::DevicestatusData* data = static_cast<DevicestatusDataUtils::DevicestatusData*>(work->data);
75 delete work;
76 if (data == nullptr) {
77 DEV_HILOGE(JS_NAPI, "work->data is nullptr");
78 return;
79 }
80 DeviceStatusNapi* deviceStatusNapi = DeviceStatusNapi::GetDeviceStatusNapi();
81 if (deviceStatusNapi == nullptr) {
82 DEV_HILOGE(JS_NAPI, "deviceStatusNapi is nullptr");
83 return;
84 }
85
86 int32_t type = static_cast<int32_t>(data->type);
87 int32_t value = static_cast<int32_t>(data->value);
88 DEV_HILOGD(JS_NAPI, "type:%{public}d, value:%{public}d", type, value);
89 deviceStatusNapi->OnDeviceStatusChangedDone(type, value, false);
90 }
91
GetDeviceStatusNapi()92 DeviceStatusNapi* DeviceStatusNapi::GetDeviceStatusNapi()
93 {
94 DEV_HILOGD(JS_NAPI, "Enter");
95 return g_obj;
96 }
97
DeviceStatusNapi(napi_env env)98 DeviceStatusNapi::DeviceStatusNapi(napi_env env) : DeviceStatusEvent(env)
99 {
100 env_ = env;
101 callbackRef_ = nullptr;
102 devicestatusValueRef_ = nullptr;
103 }
104
~DeviceStatusNapi()105 DeviceStatusNapi::~DeviceStatusNapi()
106 {
107 if (callbackRef_ != nullptr) {
108 napi_delete_reference(env_, callbackRef_);
109 }
110 if (devicestatusValueRef_ != nullptr) {
111 napi_delete_reference(env_, devicestatusValueRef_);
112 }
113 if (g_obj != nullptr) {
114 delete g_obj;
115 g_obj = nullptr;
116 }
117 }
118
OnDeviceStatusChangedDone(int32_t type,int32_t value,bool isOnce)119 void DeviceStatusNapi::OnDeviceStatusChangedDone(int32_t type, int32_t value, bool isOnce)
120 {
121 DEV_HILOGD(JS_NAPI, "Enter, value:%{public}d", value);
122 OnEvent(type, ARG_1, value, isOnce);
123 DEV_HILOGD(JS_NAPI, "Exit");
124 }
125
ConvertTypeToInt(const std::string & type)126 int32_t DeviceStatusNapi::ConvertTypeToInt(const std::string &type)
127 {
128 if (type == "still") {
129 return DevicestatusDataUtils::DevicestatusType::TYPE_STILL;
130 } else if (type == "relativeStill") {
131 return DevicestatusDataUtils::DevicestatusType::TYPE_RELATIVE_STILL;
132 } else if (type == "carBluetooth") {
133 return DevicestatusDataUtils::DevicestatusType::TYPE_CAR_BLUETOOTH;
134 } else {
135 return DevicestatusDataUtils::DevicestatusType::TYPE_INVALID;
136 }
137 }
138
CheckArguments(napi_env env,napi_callback_info info)139 bool DeviceStatusNapi::CheckArguments(napi_env env, napi_callback_info info)
140 {
141 DEV_HILOGD(JS_NAPI, "Enter");
142 int arr[ARG_4] = {};
143 size_t argc = ARG_4;
144 napi_value args[ARG_4] = {};
145 napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
146 if (status != napi_ok) {
147 DEV_HILOGE(JS_NAPI, "Failed to get_cb_info");
148 return false;
149 }
150 for (size_t i = 0; i < ARG_4; i++) {
151 napi_valuetype valueType = napi_undefined;
152 status = napi_typeof(env, args[i], &valueType);
153 if (status != napi_ok) {
154 DEV_HILOGE(JS_NAPI, "Failed to get valueType");
155 return false;
156 }
157 DEV_HILOGD(JS_NAPI, "valueType:%{public}d", valueType);
158 arr[i] = valueType;
159 }
160 if (arr[ARG_0] != napi_string || arr[ARG_1] != napi_number || arr[ARG_2] != napi_number ||
161 arr[ARG_3] != napi_function) {
162 DEV_HILOGE(JS_NAPI, "Failed to get arguements");
163 return false;
164 }
165 DEV_HILOGD(JS_NAPI, "Exit");
166 return true;
167 }
168
CheckUnsubArguments(napi_env env,napi_callback_info info)169 bool DeviceStatusNapi::CheckUnsubArguments(napi_env env, napi_callback_info info)
170 {
171 DEV_HILOGD(JS_NAPI, "Enter");
172 int arr[ARG_3] = {};
173 size_t argc = ARG_3;
174 napi_value args[ARG_3] = {};
175 napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
176 if (status != napi_ok) {
177 DEV_HILOGE(JS_NAPI, "Failed to get_cb_info");
178 return false;
179 }
180 for (size_t i = 0; i < ARG_3; i++) {
181 napi_valuetype valueType = napi_undefined;
182 status = napi_typeof(env, args[i], &valueType);
183 if (status != napi_ok) {
184 DEV_HILOGE(JS_NAPI, "Failed to get valueType");
185 return false;
186 }
187 DEV_HILOGD(JS_NAPI, "valueType:%{public}d", valueType);
188 arr[i] = valueType;
189 }
190 if (arr[ARG_0] != napi_string || arr[ARG_1] != napi_number || arr[ARG_2] != napi_function) {
191 DEV_HILOGE(JS_NAPI, "Failed to get arguements");
192 return false;
193 }
194 DEV_HILOGD(JS_NAPI, "Exit");
195 return true;
196 }
197
CheckGetArguments(napi_env env,napi_callback_info info)198 bool DeviceStatusNapi::CheckGetArguments(napi_env env, napi_callback_info info)
199 {
200 DEV_HILOGD(JS_NAPI, "Enter");
201 int arr[ARG_2] = {};
202 size_t argc = ARG_2;
203 napi_value args[ARG_2] = {};
204 napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
205 if (status != napi_ok) {
206 DEV_HILOGE(JS_NAPI, "Failed to get_cb_info");
207 return false;
208 }
209 for (size_t i = 0; i < ARG_2; i++) {
210 napi_valuetype valueType = napi_undefined;
211 status = napi_typeof(env, args[i], &valueType);
212 if (status != napi_ok) {
213 DEV_HILOGE(JS_NAPI, "Failed to get valueType");
214 return false;
215 }
216 DEV_HILOGD(JS_NAPI, "valueType:%{public}d", valueType);
217 arr[i] = valueType;
218 }
219 if (arr[ARG_0] != napi_string || arr[ARG_1] != napi_function) {
220 DEV_HILOGE(JS_NAPI, "Failed to get arguements");
221 return false;
222 }
223 DEV_HILOGD(JS_NAPI, "Exit");
224 return true;
225 }
226
SubscribeDeviceStatusCallback(napi_env env,napi_callback_info info,napi_value * args,int32_t type,int32_t event,int32_t latency)227 napi_value DeviceStatusNapi::SubscribeDeviceStatusCallback(napi_env env, napi_callback_info info, napi_value *args,
228 int32_t type, int32_t event, int32_t latency)
229 {
230 if (g_obj == nullptr) {
231 g_obj = new (std::nothrow) DeviceStatusNapi(env);
232 if (g_obj == nullptr) {
233 DEV_HILOGE(JS_NAPI, "Failed to new g_obj");
234 return nullptr;
235 }
236 DEV_HILOGD(JS_NAPI, "Didn't find object, so created it");
237 }
238 napi_wrap(env, nullptr, reinterpret_cast<void *>(g_obj),
239 [](napi_env env, void *data, void *hint) {
240 (void)env;
241 (void)hint;
242 DeviceStatusNapi *devicestatus = static_cast<DeviceStatusNapi *>(data);
243 delete devicestatus;
244 },
245 nullptr, &(g_obj->callbackRef_));
246 if (!g_obj->On(type, args[ARG_3], false)) {
247 DEV_HILOGE(JS_NAPI, "type:%{public}d already exists", type);
248 return nullptr;
249 }
250 auto callbackIter = callbackMap_.find(type);
251 if (callbackIter != callbackMap_.end()) {
252 DEV_HILOGD(JS_NAPI, "Callback exists");
253 return nullptr;
254 }
255 sptr<IdevicestatusCallback> callback;
256 callback = new (std::nothrow) DeviceStatusCallback(env);
257 if (callback == nullptr) {
258 DEV_HILOGE(JS_NAPI, "callback is nullptr");
259 return nullptr;
260 }
261 DevicestatusClient::GetInstance().SubscribeCallback(DevicestatusDataUtils::DevicestatusType(type), callback);
262 auto ret = callbackMap_.insert(std::pair<int32_t, sptr<IdevicestatusCallback>>(type, callback));
263 if (!ret.second) {
264 DEV_HILOGE(JS_NAPI, "Failed to insert");
265 return nullptr;
266 }
267 DEV_HILOGD(JS_NAPI, "Exit");
268 return nullptr;
269 }
270
SubscribeDeviceStatus(napi_env env,napi_callback_info info)271 napi_value DeviceStatusNapi::SubscribeDeviceStatus(napi_env env, napi_callback_info info)
272 {
273 DEV_HILOGD(JS_NAPI, "Subscribe Enter");
274 size_t argc = ARG_4;
275 napi_value args[ARG_4] = {};
276 napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
277 NAPI_ASSERT(env, (status == napi_ok) && (argc >= ARG_4), "Bad parameters");
278 if (!CheckArguments(env, info)) {
279 DEV_HILOGE(JS_NAPI, "Failed to get arguements");
280 return nullptr;
281 }
282 size_t modeLen = 0;
283 status = napi_get_value_string_utf8(env, args[ARG_0], nullptr, 0, &modeLen);
284 if (status != napi_ok) {
285 DEV_HILOGE(JS_NAPI, "Failed to get string item");
286 return nullptr;
287 }
288 char mode[NAPI_BUF_LENGTH] = {};
289 status = napi_get_value_string_utf8(env, args[ARG_0], mode, modeLen + 1, &modeLen);
290 if (status != napi_ok) {
291 DEV_HILOGE(JS_NAPI, "Failed to get string item");
292 return nullptr;
293 }
294 std::string typeMode = mode;
295 int32_t eventMode = 0;
296 status = napi_get_value_int32(env, args[ARG_1], &eventMode);
297 DEV_HILOGD(JS_NAPI, "mode:%{public}s,eventMode:%{public}d", typeMode.c_str(), eventMode);
298 if (status != napi_ok) {
299 DEV_HILOGE(JS_NAPI, "Failed to get event value item");
300 return nullptr;
301 }
302 int32_t latencyMode = 0;
303 status = napi_get_value_int32(env, args[ARG_2], &latencyMode);
304 if (status != napi_ok) {
305 DEV_HILOGE(JS_NAPI, "Failed to get latency value item");
306 return nullptr;
307 }
308 int32_t type = ConvertTypeToInt(typeMode);
309 int32_t event = eventMode;
310 int32_t latency = latencyMode;
311 DEV_HILOGD(JS_NAPI, "type:%{public}d, event:%{public}d, latency:%{public}d", type, event, latency);
312 NAPI_ASSERT(env, (type >= DevicestatusDataUtils::DevicestatusType::TYPE_STILL) &&
313 (type <= DevicestatusDataUtils::DevicestatusType::TYPE_LID_OPEN), "type is illegal");
314 return SubscribeDeviceStatusCallback(env, info, args, type, event, latency);
315 }
316
UnsubscribeDeviceStatus(napi_env env,napi_callback_info info)317 napi_value DeviceStatusNapi::UnsubscribeDeviceStatus(napi_env env, napi_callback_info info)
318 {
319 DEV_HILOGD(JS_NAPI, "Enter");
320 if (g_obj == nullptr) {
321 DEV_HILOGE(JS_NAPI, "g_obj is nullptr");
322 return nullptr;
323 }
324 size_t argc = ARG_3;
325 napi_value args[ARG_3] = {};
326 napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
327 NAPI_ASSERT(env, (status == napi_ok) && (argc >= ARG_3), "Bad parameters");
328 if (!CheckUnsubArguments(env, info)) {
329 DEV_HILOGE(JS_NAPI, "Failed to get unsub arguements");
330 return nullptr;
331 }
332 size_t len;
333 status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
334 if (status != napi_ok) {
335 DEV_HILOGE(JS_NAPI, "Failed to get string item");
336 return nullptr;
337 }
338 std::vector<char> typeBuf(len + 1);
339 status = napi_get_value_string_utf8(env, args[0], typeBuf.data(), len + 1, &len);
340 if (status != napi_ok) {
341 DEV_HILOGE(JS_NAPI, "Failed to get string item");
342 return nullptr;
343 }
344 int32_t type = ConvertTypeToInt(typeBuf.data());
345 NAPI_ASSERT(env, (type >= DevicestatusDataUtils::DevicestatusType::TYPE_STILL) &&
346 (type <= DevicestatusDataUtils::DevicestatusType::TYPE_LID_OPEN), "type is illegal");
347 int32_t eventMode = 0;
348 status = napi_get_value_int32(env, args[ARG_1], &eventMode);
349 if (status != napi_ok) {
350 DEV_HILOGE(JS_NAPI, "Failed to get int32 item");
351 return nullptr;
352 }
353 int32_t event = eventMode;
354 DEV_HILOGD(JS_NAPI, "type:%{public}d, event:%{public}d", type, event);
355 if (!g_obj->Off(type, args[ARG_2])) {
356 DEV_HILOGE(JS_NAPI, "Not ready to Unsubscribe for type:%{public}d", type);
357 return nullptr;
358 }
359 auto callbackIter = callbackMap_.find(type);
360 if (callbackIter != callbackMap_.end()) {
361 DevicestatusClient::GetInstance().UnSubscribeCallback(DevicestatusDataUtils::DevicestatusType(type),
362 callbackIter->second);
363 callbackMap_.erase(type);
364 } else {
365 NAPI_ASSERT(env, false, "No existed callback");
366 return nullptr;
367 }
368 DEV_HILOGD(JS_NAPI, "Exit");
369 return nullptr;
370 }
371
GetDeviceStatus(napi_env env,napi_callback_info info)372 napi_value DeviceStatusNapi::GetDeviceStatus(napi_env env, napi_callback_info info)
373 {
374 DEV_HILOGD(JS_NAPI, "Enter");
375 size_t argc = ARG_2;
376 napi_value args[ARG_2] = {};
377 napi_status status = napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
378 NAPI_ASSERT(env, (status == napi_ok) && (argc >= ARG_2), "Bad parameters");
379 if (!CheckGetArguments(env, info)) {
380 DEV_HILOGE(JS_NAPI, "Failed to get once arguements");
381 return nullptr;
382 }
383 size_t len;
384 status = napi_get_value_string_utf8(env, args[0], nullptr, 0, &len);
385 if (status != napi_ok) {
386 DEV_HILOGE(JS_NAPI, "Failed to get string item");
387 return nullptr;
388 }
389 std::vector<char> typeBuf(len + 1);
390 status = napi_get_value_string_utf8(env, args[0], typeBuf.data(), len + 1, &len);
391 if (status != napi_ok) {
392 DEV_HILOGE(JS_NAPI, "Failed to get string item");
393 return nullptr;
394 }
395 int32_t type = ConvertTypeToInt(typeBuf.data());
396 NAPI_ASSERT(env, (type >= DevicestatusDataUtils::DevicestatusType::TYPE_STILL) &&
397 (type <= DevicestatusDataUtils::DevicestatusType::TYPE_LID_OPEN), "type is illegal");
398 if (g_obj == nullptr) {
399 g_obj = new (std::nothrow) DeviceStatusNapi(env);
400 if (g_obj == nullptr) {
401 DEV_HILOGE(JS_NAPI, "Failed to new g_obj");
402 return nullptr;
403 }
404 napi_wrap(env, nullptr, reinterpret_cast<void *>(g_obj),
405 [](napi_env env, void *data, void *hint) {
406 (void)env;
407 (void)hint;
408 DeviceStatusNapi *devicestatus = static_cast<DeviceStatusNapi *>(data);
409 delete devicestatus;
410 },
411 nullptr, &(g_obj->callbackRef_));
412 }
413 if (!g_obj->On(type, args[ARG_1], true)) {
414 DEV_HILOGE(JS_NAPI, "type:%{public}d already exists", type);
415 return nullptr;
416 }
417 DevicestatusDataUtils::DevicestatusData devicestatusData =
418 DevicestatusClient::GetInstance().GetDevicestatusData(DevicestatusDataUtils::DevicestatusType(type));
419 g_obj->OnDeviceStatusChangedDone(devicestatusData.type, devicestatusData.value, true);
420 g_obj->OffOnce(devicestatusData.type, args[ARG_1]);
421 return nullptr;
422 }
423
Init(napi_env env,napi_value exports)424 napi_value DeviceStatusNapi::Init(napi_env env, napi_value exports)
425 {
426 DEV_HILOGD(JS_NAPI, "Enter");
427 napi_property_descriptor desc[] = {
428 DECLARE_NAPI_FUNCTION("on", SubscribeDeviceStatus),
429 DECLARE_NAPI_FUNCTION("off", UnsubscribeDeviceStatus),
430 DECLARE_NAPI_FUNCTION("once", GetDeviceStatus),
431 };
432 NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
433 DEV_HILOGD(JS_NAPI, "Exit");
434 return exports;
435 }
436
437 EXTERN_C_START
438 /*
439 * function for module exports
440 */
DeviceStatusInit(napi_env env,napi_value exports)441 static napi_value DeviceStatusInit(napi_env env, napi_value exports)
442 {
443 DEV_HILOGD(JS_NAPI, "Enter");
444 napi_value ret = DeviceStatusNapi::Init(env, exports);
445 DEV_HILOGD(JS_NAPI, "Exit");
446 return ret;
447 }
448 EXTERN_C_END
449
450 /*
451 * Module definition
452 */
453 static napi_module g_module = {
454 .nm_version = 1,
455 .nm_flags = 0,
456 .nm_filename = "stationary",
457 .nm_register_func = DeviceStatusInit,
458 .nm_modname = "stationary",
459 .nm_priv = (static_cast<void *>(0)),
460 .reserved = {0}
461 };
462
463 /*
464 * Module registration
465 */
RegisterModule(void)466 extern "C" __attribute__((constructor)) void RegisterModule(void)
467 {
468 napi_module_register(&g_module);
469 }
470