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, ¶meterCount, 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, ¶meterCount, 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