• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2025 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 "underage_model_napi.h"
17 
18 #include <cstdlib>
19 #include <dlfcn.h>
20 #include <string_view>
21 
22 #include "devicestatus_define.h"
23 #include "fi_log.h"
24 #include "napi_constants.h"
25 #include "underage_model_napi_error.h"
26 #include "util_napi_error.h"
27 
28 #undef LOG_TAG
29 #define LOG_TAG "DeviceUnderageModelNapi"
30 
31 namespace OHOS {
32 namespace Msdp {
33 namespace DeviceStatus {
34 namespace {
35 constexpr uint32_t INVALID_TYPE = 0;
36 constexpr uint32_t UNDERAGE_MODEL_TYPE_KID = 16;
37 constexpr size_t MAX_ARG_STRING_LEN = 512;
38 constexpr int32_t MAX_ERROR_CODE = 1000;
39 constexpr uint32_t UNSUBSCRIBE_ONE_PARA = 1;
40 constexpr uint32_t UNSUBSCRIBE_TWO_PARA = 2;
41 constexpr int32_t OTHERS = 0;
42 constexpr int32_t CHILD = 1;
43 constexpr int32_t DEVICE_UNSUPPORT_ERR = 0x3A10028;
44 std::mutex g_mutex; // mutex:Subscribe/Unsubscribe/OnListener
45 const std::array<napi_valuetype, 2> EXPECTED_SUB_ARG_TYPES = { napi_string, napi_function };
46 const std::array<napi_valuetype, 1> EXPECTED_UNSUB_ONE_ARG_TYPES = { napi_string };
47 const std::array<napi_valuetype, 2> EXPECTED_UNSUB_TWO_ARG_TYPES = { napi_string, napi_function };
48 const std::string USER_AGE_GROUP_DETECTED = "userAgeGroupDetected";
49 const std::string USER_STATUS_CLIENT_SO_PATH = "libuser_status_client.z.so";
50 const std::string_view REGISTER_LISTENER_FUNC_NAME = { "RegisterListener" };
51 const std::string_view SUBSCRIBE_FUNC_NAME = { "Subscribe" };
52 const std::string_view UNSUBSCRIBE_FUNC_NAME = { "Unsubscribe" };
53 UnderageModelNapi *g_underageModelObj = nullptr;
54 };
55 
LoadLibrary()56 bool UnderageModelNapi::LoadLibrary()
57 {
58     if (g_underageModelObj->g_userStatusHandle == nullptr) {
59         g_underageModelObj->g_userStatusHandle = dlopen(USER_STATUS_CLIENT_SO_PATH.c_str(), RTLD_LAZY);
60         if (g_underageModelObj->g_userStatusHandle == nullptr) {
61             FI_HILOGE("Load failed, path is %{private}s, error after: %{public}s",
62                 USER_STATUS_CLIENT_SO_PATH.c_str(), dlerror());
63             return false;
64         }
65     }
66     return true;
67 }
68 
OnUnderageModelListener(uint32_t eventType,int32_t result,float confidence) const69 void UnderageModelListener::OnUnderageModelListener(uint32_t eventType, int32_t result, float confidence) const
70 {
71     FI_HILOGD("Enter");
72     auto task = [eventType, result, confidence]() {
73         std::lock_guard<std::mutex> guard(g_mutex);
74         CHKPV(g_underageModelObj);
75         g_underageModelObj->OnEventChanged(eventType, result, confidence);
76     };
77     if (napi_send_event(env_, task, napi_eprio_immediate) != napi_status::napi_ok) {
78         FI_HILOGE("Failed to SendEvent");
79     }
80     FI_HILOGD("Exit");
81 }
82 
UnderageModelNapi(napi_env env,napi_value thisVar)83 UnderageModelNapi::UnderageModelNapi(napi_env env, napi_value thisVar) : UnderageModelNapiEvent(env, thisVar)
84 {
85     env_ = env;
86 }
87 
~UnderageModelNapi()88 UnderageModelNapi::~UnderageModelNapi()
89 {
90     if (g_userStatusHandle != nullptr) {
91         dlclose(g_userStatusHandle);
92         g_userStatusHandle = nullptr;
93     }
94     g_registerListenerFunc = nullptr;
95     g_subscribeFunc = nullptr;
96     g_unsubscribeFunc = nullptr;
97 }
98 
GetUnderageModelType(const std::string & type)99 uint32_t UnderageModelNapi::GetUnderageModelType(const std::string &type)
100 {
101     FI_HILOGD("Enter");
102     if (type == USER_AGE_GROUP_DETECTED) {
103         return UNDERAGE_MODEL_TYPE_KID;
104     }
105     FI_HILOGD("Don't find this type");
106     return INVALID_TYPE;
107 }
108 
SubscribeCallback(napi_env env,uint32_t type)109 bool UnderageModelNapi::SubscribeCallback(napi_env env, uint32_t type)
110 {
111     if (g_underageModelObj == nullptr) {
112         ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "g_underageModelObj is nullptr");
113         return false;
114     }
115     auto iter = g_underageModelObj->callbacks_.find(type);
116     if (iter == g_underageModelObj->callbacks_.end()) {
117         FI_HILOGD("Find no callback, to create");
118         if (g_underageModelObj->g_registerListenerFunc == nullptr) {
119             g_underageModelObj->g_registerListenerFunc = reinterpret_cast<RegisterListenerFunc>(
120                 dlsym(g_underageModelObj->g_userStatusHandle, REGISTER_LISTENER_FUNC_NAME.data()));
121             if (g_underageModelObj->g_registerListenerFunc == nullptr) {
122                 FI_HILOGE("RegisterListener find symbol failed, error: %{public}s", dlerror());
123                 ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "Find symbol failed");
124                 return false;
125             }
126         }
127         auto listener = std::make_shared<UnderageModelListener>(env);
128         int32_t ret = std::abs(g_underageModelObj->g_registerListenerFunc(type, listener));
129         if (ret < MAX_ERROR_CODE) {
130             FI_HILOGE("RegisterListener failed, ret:%{public}d", ret);
131             ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "RegisterListener failed");
132             return false;
133         }
134         if (!Subscribe(env, type)) {
135             return false;
136         }
137         g_underageModelObj->callbacks_.insert(std::make_pair(type, listener));
138     }
139     return true;
140 }
141 
UnsubscribeCallback(napi_env env,uint32_t type)142 bool UnderageModelNapi::UnsubscribeCallback(napi_env env, uint32_t type)
143 {
144     if (g_underageModelObj == nullptr) {
145         ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "g_underageModelObj is nullptr");
146         return false;
147     }
148     if (!g_underageModelObj->CheckEvents(type)) {
149         auto iter = g_underageModelObj->callbacks_.find(type);
150         if (iter == g_underageModelObj->callbacks_.end()) {
151             FI_HILOGE("Failed to find callback, %{public}d", type);
152             ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "Find type failed");
153             return false;
154         }
155         if (g_underageModelObj->g_unsubscribeFunc == nullptr) {
156             g_underageModelObj->g_unsubscribeFunc = reinterpret_cast<UnsubscribeFunc>(
157                 dlsym(g_underageModelObj->g_userStatusHandle, UNSUBSCRIBE_FUNC_NAME.data()));
158             if (g_underageModelObj->g_unsubscribeFunc == nullptr) {
159                 FI_HILOGE("%{public}s find symbol failed, error: %{public}s", UNSUBSCRIBE_FUNC_NAME.data(), dlerror());
160                 ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "Find symbol failed");
161                 return false;
162             }
163         }
164         auto ret = g_underageModelObj->g_unsubscribeFunc(type);
165         if (ret == RET_OK) {
166             g_underageModelObj->callbacks_.erase(iter);
167             return true;
168         } else if (ret == DEVICE_UNSUPPORT_ERR) {
169             FI_HILOGE("failed to unsubscribe");
170             ThrowUnderageModelErr(env, DEVICE_EXCEPTION, "Device not support");
171             return false;
172         }
173         FI_HILOGE("Unsubscribe failed, ret: %{public}d", ret);
174         ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "Unsubscribe failed");
175     }
176     return false;
177 }
178 
Subscribe(napi_env env,uint32_t type)179 bool UnderageModelNapi::Subscribe(napi_env env, uint32_t type)
180 {
181     if (g_underageModelObj->g_subscribeFunc == nullptr) {
182         g_underageModelObj->g_subscribeFunc = reinterpret_cast<SubscribeFunc>(
183             dlsym(g_underageModelObj->g_userStatusHandle, SUBSCRIBE_FUNC_NAME.data()));
184         if (g_underageModelObj->g_subscribeFunc == nullptr) {
185             FI_HILOGE("%{public}s find symbol failed, error: %{public}s", SUBSCRIBE_FUNC_NAME.data(), dlerror());
186             ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "Find symbol failed");
187             return false;
188         }
189     }
190     int32_t ret = g_underageModelObj->g_subscribeFunc(type);
191     if (ret == RET_OK) {
192         return true;
193     } else if (ret == DEVICE_UNSUPPORT_ERR) {
194         FI_HILOGE("failed to subscribe");
195         ThrowUnderageModelErr(env, DEVICE_EXCEPTION, "Device not support");
196         return false;
197     }
198     FI_HILOGE("failed to subscribe");
199     ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "Subscribe failed");
200     return false;
201 }
202 
SubscribeUnderageModel(napi_env env,napi_callback_info info)203 napi_value UnderageModelNapi::SubscribeUnderageModel(napi_env env, napi_callback_info info)
204 {
205     FI_HILOGD("Enter");
206     size_t argc = 2;
207     napi_value args[2] = { nullptr };
208     napi_value jsThis = nullptr;
209     napi_value result = nullptr;
210     if (napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr) != napi_ok) {
211         ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "napi_get_cb_info failed");
212         return nullptr;
213     }
214     if (!ValidateArgsType(env, args, argc, EXPECTED_SUB_ARG_TYPES)) {
215         ThrowUnderageModelErr(env, PARAM_EXCEPTION, "validateargstype failed");
216         return nullptr;
217     }
218     std::string typeStr;
219     if (!TransJsToStr(env, args[0], typeStr)) {
220         ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "Trans to string failed");
221         return nullptr;
222     }
223     uint32_t type = GetUnderageModelType(typeStr);
224     if (type == INVALID_TYPE) {
225         ThrowUnderageModelErr(env, PARAM_EXCEPTION, "Type is illegal");
226         return nullptr;
227     }
228     {
229         std::lock_guard<std::mutex> guard(g_mutex);
230         if (!ConstructUnderageModel(env, jsThis)) {
231             ThrowUnderageModelErr(env, SUBSCRIBE_EXCEPTION, "Failed to get g_underageModelObj");
232             return nullptr;
233         }
234         if (g_underageModelObj->g_userStatusHandle == nullptr && !LoadLibrary()) {
235             ThrowUnderageModelErr(env, DEVICE_EXCEPTION, "Device not support");
236             return nullptr;
237         }
238         if (!SubscribeCallback(env, type)) {
239             return nullptr;
240         }
241         if (!g_underageModelObj->AddCallback(type, args[1])) {
242             ThrowUnderageModelErr(env, SERVICE_EXCEPTION, "AddCallback failed");
243             return nullptr;
244         }
245     }
246     napi_get_undefined(env, &result);
247     return result;
248 }
249 
UnsubscribeUnderageModel(napi_env env,napi_callback_info info)250 napi_value UnderageModelNapi::UnsubscribeUnderageModel(napi_env env, napi_callback_info info)
251 {
252     if (g_underageModelObj == nullptr) {
253         ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "g_underageModelObj is nullptr");
254         return nullptr;
255     }
256     size_t argc = 2;
257     napi_value args[2] = { nullptr };
258     napi_value jsThis = nullptr;
259     napi_value result = nullptr;
260     if (napi_get_cb_info(env, info, &argc, args, &jsThis, nullptr) != napi_ok) {
261         ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "napi_get_cb_info is failed");
262         return nullptr;
263     }
264     bool validateArgsRes = false;
265     if (argc == UNSUBSCRIBE_ONE_PARA) {
266         validateArgsRes = ValidateArgsType(env, args, argc, EXPECTED_UNSUB_ONE_ARG_TYPES);
267     } else if (argc == UNSUBSCRIBE_TWO_PARA) {
268         validateArgsRes = ValidateArgsType(env, args, argc, EXPECTED_UNSUB_TWO_ARG_TYPES);
269     }
270     if (!validateArgsRes) {
271         ThrowUnderageModelErr(env, PARAM_EXCEPTION, "validateargstype failed");
272         return nullptr;
273     }
274     std::string typeStr;
275     if (!TransJsToStr(env, args[0], typeStr)) {
276         ThrowUnderageModelErr(env, UNSUBSCRIBE_EXCEPTION, "Trans to string failed");
277         return nullptr;
278     }
279     uint32_t type = GetUnderageModelType(typeStr);
280     if (type == INVALID_TYPE) {
281         ThrowUnderageModelErr(env, PARAM_EXCEPTION, "Type is illegal");
282         return nullptr;
283     }
284     {
285         std::lock_guard<std::mutex> guard(g_mutex);
286         if (g_underageModelObj->g_userStatusHandle == nullptr && !LoadLibrary()) {
287             ThrowUnderageModelErr(env, DEVICE_EXCEPTION, "Device not support");
288             return nullptr;
289         }
290         if (!RemoveCallbackArgs(type, argc, args)) {
291             ThrowUnderageModelErr(env, SERVICE_EXCEPTION, "RemoveCallback failed");
292             return nullptr;
293         }
294         if (!UnsubscribeCallback(env, type)) {
295             return nullptr;
296         }
297     }
298     napi_get_undefined(env, &result);
299     return result;
300 }
301 
RemoveCallbackArgs(uint32_t type,size_t argc,napi_value args[])302 bool UnderageModelNapi::RemoveCallbackArgs(uint32_t type, size_t argc, napi_value args[])
303 {
304     auto removeRet = false;
305     if (argc != UNSUBSCRIBE_TWO_PARA) {
306         removeRet = g_underageModelObj->RemoveAllCallback(type);
307     } else {
308         removeRet = g_underageModelObj->RemoveCallback(type, args[1]);
309     }
310     return removeRet;
311 }
312 
ConstructUnderageModel(napi_env env,napi_value jsThis)313 bool UnderageModelNapi::ConstructUnderageModel(napi_env env, napi_value jsThis) __attribute__((no_sanitize("cfi")))
314 {
315     if (g_underageModelObj == nullptr) {
316         g_underageModelObj = new (std::nothrow) UnderageModelNapi(env, jsThis);
317         if (g_underageModelObj == nullptr) {
318             FI_HILOGE("faild to get g_underageModelObj");
319             return false;
320         }
321         napi_status status = napi_wrap(env, jsThis, reinterpret_cast<void *>(g_underageModelObj),
322             [](napi_env env, void *data, void *hint) {
323                 (void)env;
324                 (void)hint;
325                 if (data != nullptr) {
326                     UnderageModelNapi *underageMode = reinterpret_cast<UnderageModelNapi *>(data);
327                     delete underageMode;
328                     underageMode = nullptr;
329                 }
330             }, nullptr, nullptr);
331         if (status != napi_ok) {
332             delete g_underageModelObj;
333             g_underageModelObj = nullptr;
334             FI_HILOGE("napi_wrap failed");
335             return false;
336         }
337     }
338     return true;
339 }
340 
CreateUserAgeGroup(napi_env env,napi_value exports)341 bool UnderageModelNapi::CreateUserAgeGroup(napi_env env, napi_value exports)
342 {
343     napi_handle_scope scope = nullptr;
344     napi_open_handle_scope(env, &scope);
345     CHKPF(scope);
346     napi_value userAgeGroup;
347     CHKRF_SCOPE(env, napi_create_object(env, &userAgeGroup), CREATE_OBJECT, scope);
348     napi_value prop = nullptr;
349     CHKRF_SCOPE(env, napi_create_int32(env, OTHERS, &prop), CREATE_INT32, scope);
350     CHKRF_SCOPE(env, napi_set_named_property(env, userAgeGroup, "OTHERS", prop), SET_NAMED_PROPERTY, scope);
351     CHKRF_SCOPE(env, napi_create_int32(env, CHILD, &prop), CREATE_INT32, scope);
352     CHKRF_SCOPE(env, napi_set_named_property(env, userAgeGroup, "CHILD", prop), SET_NAMED_PROPERTY, scope);
353     CHKRF_SCOPE(env, napi_set_named_property(env, exports, "UserAgeGroup", userAgeGroup), SET_NAMED_PROPERTY, scope);
354     napi_close_handle_scope(env, scope);
355     return true;
356 }
357 
358 template<size_t N>
ValidateArgsType(napi_env env,napi_value * args,size_t argc,const std::array<napi_valuetype,N> & expectedTypes)359 bool UnderageModelNapi::ValidateArgsType(napi_env env, napi_value *args, size_t argc,
360     const std::array<napi_valuetype, N> &expectedTypes)
361 {
362     FI_HILOGD("Enter");
363     napi_status status = napi_ok;
364     napi_valuetype valueType = napi_undefined;
365     if (argc > expectedTypes.size()) {
366         FI_HILOGE("Wrong number of arguments");
367         return false;
368     }
369     for (size_t i = 0; i < argc; ++i) {
370         status = napi_typeof(env, args[i], &valueType);
371         if (status != napi_ok) {
372             FI_HILOGE("Error while checking arguments types");
373             return false;
374         }
375         if (valueType != expectedTypes[i]) {
376             FI_HILOGE("Wrong argument type");
377             return false;
378         }
379     }
380     return true;
381 }
382 
TransJsToStr(napi_env env,napi_value value,std::string & str)383 bool UnderageModelNapi::TransJsToStr(napi_env env, napi_value value, std::string &str)
384 {
385     FI_HILOGD("Enter");
386     size_t strlen = 0;
387     napi_status status = napi_get_value_string_utf8(env, value, nullptr, 0, &strlen);
388     if (status != napi_ok) {
389         FI_HILOGE("Error string length invalid");
390         return false;
391     }
392     if (strlen > MAX_ARG_STRING_LEN) {
393         FI_HILOGE("The string length invalid");
394         return false;
395     }
396     std::vector<char> buf(strlen + 1);
397     status = napi_get_value_string_utf8(env, value, buf.data(), strlen+1, &strlen);
398     if (status != napi_ok) {
399         FI_HILOGE("napi_get_value_string_utf8 failed");
400         return false;
401     }
402     str.assign(buf.data());
403     return true;
404 }
405 
Init(napi_env env,napi_value exports)406 napi_value UnderageModelNapi::Init(napi_env env, napi_value exports)
407 {
408     FI_HILOGD("Enter");
409     napi_property_descriptor desc[] = {
410         DECLARE_NAPI_STATIC_FUNCTION("on", SubscribeUnderageModel),
411         DECLARE_NAPI_STATIC_FUNCTION("off", UnsubscribeUnderageModel),
412     };
413     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc)/sizeof(desc[0]), desc));
414     if (!CreateUserAgeGroup(env, exports)) {
415         FI_HILOGE("Failed create UserAgeGroup");
416     }
417     FI_HILOGD("Exit");
418     return exports;
419 }
420 
421 EXTERN_C_START
422 /*
423  * function for module exports
424  */
UnderageModelInit(napi_env env,napi_value exports)425 static napi_value UnderageModelInit(napi_env env, napi_value exports)
426 {
427     FI_HILOGD("Enter");
428     napi_value ret = UnderageModelNapi::Init(env, exports);
429     if (ret == nullptr) {
430         FI_HILOGE("Failed to init");
431         return ret;
432     }
433     FI_HILOGD("Exit");
434     return ret;
435 }
436 EXTERN_C_END
437 
438 /*
439  * Module definition
440  */
441 static napi_module g_module = {
442     .nm_version = 1,
443     .nm_flags = 0,
444     .nm_filename = "multimodalAwareness.userStatus",
445     .nm_register_func = UnderageModelInit,
446     .nm_modname = "multimodalAwareness.userStatus",
447     .nm_priv = (static_cast<void *>(0)),
448     .reserved = { nullptr }
449 };
450 
451 /*
452  * Module registration
453  */
RegisterModule(void)454 extern "C" __attribute__((constructor)) void RegisterModule(void)
455 {
456     napi_module_register(&g_module);
457 }
458 } // namespace DeviceStatus
459 } // namespace Msdp
460 } // namespace OHOS