/* * Copyright (C) 2021 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "napi_state_registry.h" #include <map> #include <utility> #include "event_listener_manager.h" #include "napi_parameter_util.h" #include "napi_telephony_observer.h" #include "napi_util.h" #include "state_registry_errors.h" #include "telephony_errors.h" #include "telephony_log_wrapper.h" #include "telephony_state_manager.h" namespace OHOS { namespace Telephony { namespace { constexpr const char *OBSERVER_JS_PERMISSION_ERROR_STRING = "Permission denied. An attempt was made to Observer " "On forbidden by permission : ohos.permission.GET_NETWORK_INFO or ohos.permission.LOCATION "; constexpr size_t PARAMETER_COUNT_ONE = 1; constexpr size_t PARAMETER_COUNT_TWO = 2; constexpr size_t PARAMETER_COUNT_THREE = 3; const std::map<std::string_view, TelephonyUpdateEventType> eventMap { { "networkStateChange", TelephonyUpdateEventType::EVENT_NETWORK_STATE_UPDATE }, { "callStateChange", TelephonyUpdateEventType::EVENT_CALL_STATE_UPDATE }, { "signalInfoChange", TelephonyUpdateEventType::EVENT_SIGNAL_STRENGTHS_UPDATE }, { "simStateChange", TelephonyUpdateEventType::EVENT_SIM_STATE_UPDATE }, { "cellInfoChange", TelephonyUpdateEventType::EVENT_CELL_INFO_UPDATE }, { "cellularDataConnectionStateChange", TelephonyUpdateEventType::EVENT_DATA_CONNECTION_UPDATE }, { "cellularDataFlowChange", TelephonyUpdateEventType::EVENT_CELLULAR_DATA_FLOW_UPDATE }, { "cfuIndicatorChange", TelephonyUpdateEventType::EVENT_CFU_INDICATOR_UPDATE }, { "voiceMailMsgIndicatorChange", TelephonyUpdateEventType::EVENT_VOICE_MAIL_MSG_INDICATOR_UPDATE }, { "iccAccountInfoChange", TelephonyUpdateEventType::EVENT_ICC_ACCOUNT_CHANGE }, }; TelephonyUpdateEventType GetEventType(std::string_view event) { auto serched = eventMap.find(event); return (serched != eventMap.end() ? serched->second : TelephonyUpdateEventType::NONE_EVENT_TYPE); } } // namespace static inline bool IsValidSlotIdEx(TelephonyUpdateEventType eventType, int32_t slotId) { int32_t defaultSlotId = DEFAULT_SIM_SLOT_ID; if (eventType == TelephonyUpdateEventType::EVENT_CALL_STATE_UPDATE) { defaultSlotId = -1; } // One more slot for VSim. return ((slotId >= defaultSlotId) && (slotId < SIM_SLOT_COUNT + 1)); } static void NativeOn(napi_env env, void *data) { if (data == nullptr) { TELEPHONY_LOGE("NativeOn data is nullptr"); NapiUtil::ThrowParameterError(env); return; } ObserverContext *asyncContext = static_cast<ObserverContext *>(data); if (SIM_SLOT_COUNT == 0) { TELEPHONY_LOGE("The device is not support sim card."); asyncContext->resolved = true; return; } if (!IsValidSlotIdEx(asyncContext->eventType, asyncContext->slotId)) { TELEPHONY_LOGE("NativeOn slotId is invalid"); asyncContext->errorCode = ERROR_SLOT_ID_INVALID; return; } std::shared_ptr<bool> isDeleting = std::make_shared<bool>(false); EventListener listener { env, asyncContext->eventType, asyncContext->slotId, asyncContext->callbackRef, isDeleting, }; asyncContext->errorCode = EventListenerManager::RegisterEventListener(listener); if (asyncContext->errorCode == TELEPHONY_SUCCESS) { asyncContext->resolved = true; } } static void OnCallback(napi_env env, void *data) { if (data == nullptr) { TELEPHONY_LOGE("OnCallback data is nullptr"); NapiUtil::ThrowParameterError(env); return; } ObserverContext *asyncContext = static_cast<ObserverContext *>(data); if (!asyncContext->resolved) { TELEPHONY_LOGE("OnCallback error by add observer failed"); if (asyncContext->errorCode == TELEPHONY_STATE_REGISTRY_PERMISSION_DENIED) { NapiUtil::ThrowError(env, JS_ERROR_TELEPHONY_PERMISSION_DENIED, OBSERVER_JS_PERMISSION_ERROR_STRING); } else if (asyncContext->errorCode != TELEPHONY_ERR_CALLBACK_ALREADY_REGISTERED) { JsError error = NapiUtil::ConverErrorMessageForJs(asyncContext->errorCode); NapiUtil::ThrowError(env, error.errorCode, error.errorMessage); } if (env != nullptr && asyncContext->callbackRef != nullptr) { napi_delete_reference(env, asyncContext->callbackRef); asyncContext->callbackRef = nullptr; } } delete asyncContext; } static napi_value On(napi_env env, napi_callback_info info) { size_t parameterCount = PARAMETER_COUNT_THREE; napi_value parameters[] = { nullptr, nullptr, nullptr }; napi_get_cb_info(env, info, ¶meterCount, parameters, nullptr, nullptr); std::unique_ptr<ObserverContext> asyncContext = std::make_unique<ObserverContext>(); if (asyncContext == nullptr) { TELEPHONY_LOGE("On asyncContext is nullptr."); NapiUtil::ThrowParameterError(env); return nullptr; } std::array<char, ARRAY_SIZE> eventType {}; napi_value object = NapiUtil::CreateUndefined(env); std::optional<NapiError> errCode; if (parameterCount == std::size(parameters)) { auto paraTuple = std::make_tuple(std::data(eventType), &object, &asyncContext->callbackRef); errCode = MatchParameters(env, parameters, parameterCount, paraTuple); if (!errCode.has_value()) { napi_value slotId = NapiUtil::GetNamedProperty(env, object, "slotId"); if (slotId) { NapiValueToCppValue(env, slotId, napi_number, &asyncContext->slotId); TELEPHONY_LOGI("state registry on slotId = %{public}d, eventType = %{public}d", asyncContext->slotId, asyncContext->eventType); } } } else { auto paraTuple = std::make_tuple(std::data(eventType), &asyncContext->callbackRef); errCode = MatchParameters(env, parameters, parameterCount, paraTuple); if (GetEventType(eventType.data()) == TelephonyUpdateEventType::EVENT_CALL_STATE_UPDATE) { TELEPHONY_LOGI("state registry observer has no slotId"); asyncContext->slotId = -1; } } if (errCode.has_value()) { TELEPHONY_LOGE("On parameter matching failed."); NapiUtil::ThrowParameterError(env); return nullptr; } ObserverContext *observerContext = asyncContext.release(); observerContext->eventType = GetEventType(eventType.data()); if (observerContext->eventType != TelephonyUpdateEventType::NONE_EVENT_TYPE) { NativeOn(env, observerContext); } else { NapiUtil::ThrowParameterError(env); } OnCallback(env, observerContext); return NapiUtil::CreateUndefined(env); } static void NativeOff(napi_env env, void *data) { if (data == nullptr) { TELEPHONY_LOGE("NativeOff data is nullptr"); NapiUtil::ThrowParameterError(env); return; } ObserverContext *asyncContext = static_cast<ObserverContext *>(data); if (asyncContext->callbackRef == nullptr) { asyncContext->errorCode = EventListenerManager::UnregisterEventListener( env, asyncContext->eventType, asyncContext->removeListenerList); } else { asyncContext->errorCode = EventListenerManager::UnregisterEventListener( env, asyncContext->eventType, asyncContext->callbackRef, asyncContext->removeListenerList); } if (asyncContext->errorCode == TELEPHONY_SUCCESS) { asyncContext->resolved = true; } } static void OffCallback(napi_env env, void *data) { if (data == nullptr) { TELEPHONY_LOGE("OffCallback data is nullptr"); NapiUtil::ThrowParameterError(env); return; } ObserverContext *asyncContext = static_cast<ObserverContext *>(data); for (auto listener : asyncContext->removeListenerList) { if (env == listener.env && listener.env != nullptr && listener.callbackRef != nullptr) { napi_delete_reference(listener.env, listener.callbackRef); } } asyncContext->removeListenerList.clear(); if (!asyncContext->resolved) { TELEPHONY_LOGE("OffCallback error by remove observer failed"); if (asyncContext->errorCode == TELEPHONY_STATE_REGISTRY_PERMISSION_DENIED) { NapiUtil::ThrowError(env, JS_ERROR_TELEPHONY_PERMISSION_DENIED, OBSERVER_JS_PERMISSION_ERROR_STRING); } else { JsError error = NapiUtil::ConverErrorMessageForJs(asyncContext->errorCode); NapiUtil::ThrowError(env, error.errorCode, error.errorMessage); } } if (env != nullptr && asyncContext->callbackRef != nullptr) { napi_delete_reference(env, asyncContext->callbackRef); asyncContext->callbackRef = nullptr; } delete asyncContext; } static napi_value Off(napi_env env, napi_callback_info info) { size_t parameterCount = PARAMETER_COUNT_TWO; napi_value parameters[] = { nullptr, nullptr }; napi_get_cb_info(env, info, ¶meterCount, parameters, nullptr, nullptr); std::array<char, ARRAY_SIZE> eventType {}; std::unique_ptr<ObserverContext> asyncContext = std::make_unique<ObserverContext>(); if (asyncContext == nullptr) { TELEPHONY_LOGE("asyncContext is nullptr."); NapiUtil::ThrowParameterError(env); return nullptr; } napi_valuetype valueTypeTemp = napi_undefined; napi_typeof(env, parameters[parameterCount - 1], &valueTypeTemp); if (valueTypeTemp == napi_undefined || valueTypeTemp == napi_null) { TELEPHONY_LOGI("undefined or null parameter is ignored."); parameterCount = PARAMETER_COUNT_ONE; } auto paraTuple = std::make_tuple(std::data(eventType), &asyncContext->callbackRef); std::optional<NapiError> errCode = MatchParameters(env, parameters, parameterCount, paraTuple); if (errCode.has_value()) { TELEPHONY_LOGE("parameter matching failed."); NapiUtil::ThrowParameterError(env); return nullptr; } ObserverContext *observerContext = asyncContext.release(); observerContext->eventType = GetEventType(eventType.data()); if (observerContext->eventType != TelephonyUpdateEventType::NONE_EVENT_TYPE) { NativeOff(env, observerContext); } else { NapiUtil::ThrowParameterError(env); } OffCallback(env, observerContext); return NapiUtil::CreateUndefined(env); } napi_status InitEnumLockReason(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { DECLARE_NAPI_STATIC_PROPERTY("SIM_NONE", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_NONE))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PIN))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PUK))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PN_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PN_PIN))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PN_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PN_PUK))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PU_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PU_PIN))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PU_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PU_PUK))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PP_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PP_PIN))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PP_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PP_PUK))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PC_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PC_PIN))), DECLARE_NAPI_STATIC_PROPERTY("SIM_PC_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_PC_PUK))), DECLARE_NAPI_STATIC_PROPERTY("SIM_SIM_PIN", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_SIM_PIN))), DECLARE_NAPI_STATIC_PROPERTY("SIM_SIM_PUK", GetNapiValue(env, static_cast<int32_t>(LockReason::SIM_SIM_PUK))), }; constexpr size_t arrSize = sizeof(desc) / sizeof(desc[0]); NapiUtil::DefineEnumClassByName(env, exports, "LockReason", arrSize, desc); return napi_define_properties(env, exports, arrSize, desc); } EXTERN_C_START napi_value InitNapiStateRegistry(napi_env env, napi_value exports) { napi_property_descriptor desc[] = { DECLARE_NAPI_FUNCTION("on", On), DECLARE_NAPI_FUNCTION("off", Off), }; NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc)); NAPI_CALL(env, InitEnumLockReason(env, exports)); const char *nativeStr = "InitNapiStateRegistry"; napi_wrap( env, exports, static_cast<void *>(const_cast<char *>(nativeStr)), [](napi_env env, void *data, void *hint) { EventListenerManager::UnRegisterAllListener(env); }, nullptr, nullptr); return exports; } EXTERN_C_END static napi_module _stateRegistryModule = { .nm_version = 1, .nm_flags = 0, .nm_filename = nullptr, .nm_register_func = InitNapiStateRegistry, .nm_modname = "telephony.observer", .nm_priv = nullptr, .reserved = { nullptr }, }; extern "C" __attribute__((constructor)) void RegisterTelephonyObserverModule(void) { napi_module_register(&_stateRegistryModule); } } // namespace Telephony } // namespace OHOS