1 /*
2  * Copyright (C) 2021 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 "napi_state_registry.h"
17 
18 #include <map>
19 #include <utility>
20 
21 #include "event_listener_manager.h"
22 #include "napi_parameter_util.h"
23 #include "napi_telephony_observer.h"
24 #include "napi_util.h"
25 #include "state_registry_errors.h"
26 #include "telephony_errors.h"
27 #include "telephony_log_wrapper.h"
28 #include "telephony_state_manager.h"
29 
30 namespace OHOS {
31 namespace Telephony {
32 namespace {
33 constexpr const char *OBSERVER_JS_PERMISSION_ERROR_STRING =
34     "Permission denied. An attempt was made to Observer "
35     "On forbidden by permission : ohos.permission.GET_NETWORK_INFO or ohos.permission.LOCATION ";
36 constexpr size_t PARAMETER_COUNT_ONE = 1;
37 constexpr size_t PARAMETER_COUNT_TWO = 2;
38 constexpr size_t PARAMETER_COUNT_THREE = 3;
39 
40 const std::map<std::string_view, TelephonyUpdateEventType> eventMap {
41     { "networkStateChange", TelephonyUpdateEventType::EVENT_NETWORK_STATE_UPDATE },
42     { "callStateChange", TelephonyUpdateEventType::EVENT_CALL_STATE_UPDATE },
43     { "signalInfoChange", TelephonyUpdateEventType::EVENT_SIGNAL_STRENGTHS_UPDATE },
44     { "simStateChange", TelephonyUpdateEventType::EVENT_SIM_STATE_UPDATE },
45     { "cellInfoChange", TelephonyUpdateEventType::EVENT_CELL_INFO_UPDATE },
46     { "cellularDataConnectionStateChange", TelephonyUpdateEventType::EVENT_DATA_CONNECTION_UPDATE },
47     { "cellularDataFlowChange", TelephonyUpdateEventType::EVENT_CELLULAR_DATA_FLOW_UPDATE },
48     { "cfuIndicatorChange", TelephonyUpdateEventType::EVENT_CFU_INDICATOR_UPDATE },
49     { "voiceMailMsgIndicatorChange", TelephonyUpdateEventType::EVENT_VOICE_MAIL_MSG_INDICATOR_UPDATE },
50     { "iccAccountInfoChange", TelephonyUpdateEventType::EVENT_ICC_ACCOUNT_CHANGE },
51 };
52 
GetEventType(std::string_view event)53 TelephonyUpdateEventType GetEventType(std::string_view event)
54 {
55     auto serched = eventMap.find(event);
56     return (serched != eventMap.end() ? serched->second : TelephonyUpdateEventType::NONE_EVENT_TYPE);
57 }
58 } // namespace
59 
IsValidSlotIdEx(TelephonyUpdateEventType eventType,int32_t slotId)60 static inline bool IsValidSlotIdEx(TelephonyUpdateEventType eventType, int32_t slotId)
61 {
62     int32_t defaultSlotId = DEFAULT_SIM_SLOT_ID;
63     if (eventType == TelephonyUpdateEventType::EVENT_CALL_STATE_UPDATE) {
64         defaultSlotId = -1;
65     }
66     // One more slot for VSim.
67     return ((slotId >= defaultSlotId) && (slotId < SIM_SLOT_COUNT + 1));
68 }
69 
NativeOn(napi_env env,void * data)70 static void NativeOn(napi_env env, void *data)
71 {
72     if (data == nullptr) {
73         TELEPHONY_LOGE("NativeOn data is nullptr");
74         NapiUtil::ThrowParameterError(env);
75         return;
76     }
77     ObserverContext *asyncContext = static_cast<ObserverContext *>(data);
78     if (SIM_SLOT_COUNT == 0) {
79         TELEPHONY_LOGE("The device is not support sim card.");
80         asyncContext->resolved = true;
81         return;
82     }
83 
84     if (!IsValidSlotIdEx(asyncContext->eventType, asyncContext->slotId)) {
85         TELEPHONY_LOGE("NativeOn slotId is invalid");
86         asyncContext->errorCode = ERROR_SLOT_ID_INVALID;
87         return;
88     }
89     std::shared_ptr<bool> isDeleting = std::make_shared<bool>(false);
90     EventListener listener {
91         env,
92         asyncContext->eventType,
93         asyncContext->slotId,
94         asyncContext->callbackRef,
95         isDeleting,
96     };
97     asyncContext->errorCode = EventListenerManager::RegisterEventListener(listener);
98     if (asyncContext->errorCode == TELEPHONY_SUCCESS) {
99         asyncContext->resolved = true;
100     }
101 }
102 
OnCallback(napi_env env,void * data)103 static void OnCallback(napi_env env, void *data)
104 {
105     if (data == nullptr) {
106         TELEPHONY_LOGE("OnCallback data is nullptr");
107         NapiUtil::ThrowParameterError(env);
108         return;
109     }
110     ObserverContext *asyncContext = static_cast<ObserverContext *>(data);
111     if (!asyncContext->resolved) {
112         TELEPHONY_LOGE("OnCallback error by add observer failed");
113         if (asyncContext->errorCode == TELEPHONY_STATE_REGISTRY_PERMISSION_DENIED) {
114             NapiUtil::ThrowError(env, JS_ERROR_TELEPHONY_PERMISSION_DENIED, OBSERVER_JS_PERMISSION_ERROR_STRING);
115         } else if (asyncContext->errorCode != TELEPHONY_ERR_CALLBACK_ALREADY_REGISTERED) {
116             JsError error = NapiUtil::ConverErrorMessageForJs(asyncContext->errorCode);
117             NapiUtil::ThrowError(env, error.errorCode, error.errorMessage);
118         }
119         if (env != nullptr && asyncContext->callbackRef != nullptr) {
120             napi_delete_reference(env, asyncContext->callbackRef);
121             asyncContext->callbackRef = nullptr;
122         }
123     }
124     delete asyncContext;
125 }
126 
On(napi_env env,napi_callback_info info)127 static napi_value On(napi_env env, napi_callback_info info)
128 {
129     size_t parameterCount = PARAMETER_COUNT_THREE;
130     napi_value parameters[] = { nullptr, nullptr, nullptr };
131     napi_get_cb_info(env, info, &parameterCount, parameters, nullptr, nullptr);
132 
133     std::unique_ptr<ObserverContext> asyncContext = std::make_unique<ObserverContext>();
134     if (asyncContext == nullptr) {
135         TELEPHONY_LOGE("On asyncContext is nullptr.");
136         NapiUtil::ThrowParameterError(env);
137         return nullptr;
138     }
139     std::array<char, ARRAY_SIZE> eventType {};
140     napi_value object = NapiUtil::CreateUndefined(env);
141     std::optional<NapiError> errCode;
142     if (parameterCount == std::size(parameters)) {
143         auto paraTuple = std::make_tuple(std::data(eventType), &object, &asyncContext->callbackRef);
144         errCode = MatchParameters(env, parameters, parameterCount, paraTuple);
145         if (!errCode.has_value()) {
146             napi_value slotId = NapiUtil::GetNamedProperty(env, object, "slotId");
147             if (slotId) {
148                 NapiValueToCppValue(env, slotId, napi_number, &asyncContext->slotId);
149                 TELEPHONY_LOGI("state registry on slotId = %{public}d, eventType = %{public}d", asyncContext->slotId,
150                     asyncContext->eventType);
151             }
152         }
153     } else {
154         auto paraTuple = std::make_tuple(std::data(eventType), &asyncContext->callbackRef);
155         errCode = MatchParameters(env, parameters, parameterCount, paraTuple);
156         if (GetEventType(eventType.data()) == TelephonyUpdateEventType::EVENT_CALL_STATE_UPDATE) {
157             TELEPHONY_LOGI("state registry observer has no slotId");
158             asyncContext->slotId = -1;
159         }
160     }
161 
162     if (errCode.has_value()) {
163         TELEPHONY_LOGE("On parameter matching failed.");
164         NapiUtil::ThrowParameterError(env);
165         return nullptr;
166     }
167 
168     ObserverContext *observerContext = asyncContext.release();
169     observerContext->eventType = GetEventType(eventType.data());
170     if (observerContext->eventType != TelephonyUpdateEventType::NONE_EVENT_TYPE) {
171         NativeOn(env, observerContext);
172     } else {
173         NapiUtil::ThrowParameterError(env);
174     }
175     OnCallback(env, observerContext);
176     return NapiUtil::CreateUndefined(env);
177 }
178 
NativeOff(napi_env env,void * data)179 static void NativeOff(napi_env env, void *data)
180 {
181     if (data == nullptr) {
182         TELEPHONY_LOGE("NativeOff data is nullptr");
183         NapiUtil::ThrowParameterError(env);
184         return;
185     }
186 
187     ObserverContext *asyncContext = static_cast<ObserverContext *>(data);
188     if (asyncContext->callbackRef == nullptr) {
189         asyncContext->errorCode = EventListenerManager::UnregisterEventListener(
190             env, asyncContext->eventType, asyncContext->removeListenerList);
191     } else {
192         asyncContext->errorCode = EventListenerManager::UnregisterEventListener(
193             env, asyncContext->eventType, asyncContext->callbackRef, asyncContext->removeListenerList);
194     }
195 
196     if (asyncContext->errorCode == TELEPHONY_SUCCESS) {
197         asyncContext->resolved = true;
198     }
199 }
200 
OffCallback(napi_env env,void * data)201 static void OffCallback(napi_env env, void *data)
202 {
203     if (data == nullptr) {
204         TELEPHONY_LOGE("OffCallback data is nullptr");
205         NapiUtil::ThrowParameterError(env);
206         return;
207     }
208     ObserverContext *asyncContext = static_cast<ObserverContext *>(data);
209     for (auto listener : asyncContext->removeListenerList) {
210         if (env == listener.env && listener.env != nullptr && listener.callbackRef != nullptr) {
211             napi_delete_reference(listener.env, listener.callbackRef);
212         }
213     }
214     asyncContext->removeListenerList.clear();
215     if (!asyncContext->resolved) {
216         TELEPHONY_LOGE("OffCallback error by remove observer failed");
217         if (asyncContext->errorCode == TELEPHONY_STATE_REGISTRY_PERMISSION_DENIED) {
218             NapiUtil::ThrowError(env, JS_ERROR_TELEPHONY_PERMISSION_DENIED, OBSERVER_JS_PERMISSION_ERROR_STRING);
219         } else {
220             JsError error = NapiUtil::ConverErrorMessageForJs(asyncContext->errorCode);
221             NapiUtil::ThrowError(env, error.errorCode, error.errorMessage);
222         }
223     }
224     if (env != nullptr && asyncContext->callbackRef != nullptr) {
225         napi_delete_reference(env, asyncContext->callbackRef);
226         asyncContext->callbackRef = nullptr;
227     }
228     delete asyncContext;
229 }
230 
Off(napi_env env,napi_callback_info info)231 static napi_value Off(napi_env env, napi_callback_info info)
232 {
233     size_t parameterCount = PARAMETER_COUNT_TWO;
234     napi_value parameters[] = { nullptr, nullptr };
235     napi_get_cb_info(env, info, &parameterCount, parameters, nullptr, nullptr);
236 
237     std::array<char, ARRAY_SIZE> eventType {};
238     std::unique_ptr<ObserverContext> asyncContext = std::make_unique<ObserverContext>();
239     if (asyncContext == nullptr) {
240         TELEPHONY_LOGE("asyncContext is nullptr.");
241         NapiUtil::ThrowParameterError(env);
242         return nullptr;
243     }
244     napi_valuetype valueTypeTemp = napi_undefined;
245     napi_typeof(env, parameters[parameterCount - 1], &valueTypeTemp);
246     if (valueTypeTemp == napi_undefined || valueTypeTemp == napi_null) {
247         TELEPHONY_LOGI("undefined or null parameter is ignored.");
248         parameterCount = PARAMETER_COUNT_ONE;
249     }
250     auto paraTuple = std::make_tuple(std::data(eventType), &asyncContext->callbackRef);
251     std::optional<NapiError> errCode = MatchParameters(env, parameters, parameterCount, paraTuple);
252     if (errCode.has_value()) {
253         TELEPHONY_LOGE("parameter matching failed.");
254         NapiUtil::ThrowParameterError(env);
255         return nullptr;
256     }
257 
258     ObserverContext *observerContext = asyncContext.release();
259     observerContext->eventType = GetEventType(eventType.data());
260     if (observerContext->eventType != TelephonyUpdateEventType::NONE_EVENT_TYPE) {
261         NativeOff(env, observerContext);
262     } else {
263         NapiUtil::ThrowParameterError(env);
264     }
265     OffCallback(env, observerContext);
266     return NapiUtil::CreateUndefined(env);
267 }
268 
InitEnumLockReason(napi_env env,napi_value exports)269 napi_status InitEnumLockReason(napi_env env, napi_value exports)
270 {
271     napi_property_descriptor desc[] = {
272         DECLARE_NAPI_STATIC_PROPERTY("SIM_NONE", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_NONE))),
273         DECLARE_NAPI_STATIC_PROPERTY("SIM_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PIN))),
274         DECLARE_NAPI_STATIC_PROPERTY("SIM_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PUK))),
275         DECLARE_NAPI_STATIC_PROPERTY("SIM_PN_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PN_PIN))),
276         DECLARE_NAPI_STATIC_PROPERTY("SIM_PN_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PN_PUK))),
277         DECLARE_NAPI_STATIC_PROPERTY("SIM_PU_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PU_PIN))),
278         DECLARE_NAPI_STATIC_PROPERTY("SIM_PU_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PU_PUK))),
279         DECLARE_NAPI_STATIC_PROPERTY("SIM_PP_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PP_PIN))),
280         DECLARE_NAPI_STATIC_PROPERTY("SIM_PP_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PP_PUK))),
281         DECLARE_NAPI_STATIC_PROPERTY("SIM_PC_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PC_PIN))),
282         DECLARE_NAPI_STATIC_PROPERTY("SIM_PC_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PC_PUK))),
283         DECLARE_NAPI_STATIC_PROPERTY("SIM_SIM_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_SIM_PIN))),
284         DECLARE_NAPI_STATIC_PROPERTY("SIM_SIM_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_SIM_PUK))),
285     };
286 
287     constexpr size_t arrSize = sizeof(desc) / sizeof(desc[0]);
288     NapiUtil::DefineEnumClassByName(env, exports, "LockReason", arrSize, desc);
289     return napi_define_properties(env, exports, arrSize, desc);
290 }
291 
292 EXTERN_C_START
InitNapiStateRegistry(napi_env env,napi_value exports)293 napi_value InitNapiStateRegistry(napi_env env, napi_value exports)
294 {
295     napi_property_descriptor desc[] = {
296         DECLARE_NAPI_FUNCTION("on", On),
297         DECLARE_NAPI_FUNCTION("off", Off),
298     };
299     NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
300     NAPI_CALL(env, InitEnumLockReason(env, exports));
301     const char *nativeStr = "InitNapiStateRegistry";
302     napi_wrap(
303         env, exports, static_cast<void *>(const_cast<char *>(nativeStr)),
304         [](napi_env env, void *data, void *hint) { EventListenerManager::UnRegisterAllListener(env); }, nullptr,
305         nullptr);
306     return exports;
307 }
308 EXTERN_C_END
309 
310 static napi_module _stateRegistryModule = {
311     .nm_version = 1,
312     .nm_flags = 0,
313     .nm_filename = nullptr,
314     .nm_register_func = InitNapiStateRegistry,
315     .nm_modname = "telephony.observer",
316     .nm_priv = nullptr,
317     .reserved = { nullptr },
318 };
319 
RegisterTelephonyObserverModule(void)320 extern "C" __attribute__((constructor)) void RegisterTelephonyObserverModule(void)
321 {
322     napi_module_register(&_stateRegistryModule);
323 }
324 } // namespace Telephony
325 } // namespace OHOS
326