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