* Copyright (c) 2023-2026 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_common_event.h"
#include <mutex>
#include <unordered_map>
#include <uv.h>
#include <vector>
#include "errors.h"
#include "napi/native_common.h"
#include "nlohmann/json.hpp"
namespace OHOS {
namespace Plugin {
static const int32_t CREATE_MAX_PARA = 2;
static const int32_t SUBSCRIBE_MAX_PARA = 2;
static const int32_t UNSUBSCRIBE_MAX_PARA = 2;
static const int32_t STR_MAX_SIZE = 256;
static const int32_t STR_DATA_MAX_SIZE = 64 * 1024;
static const int32_t ARGS_TWO_EVENT = 2;
static const int32_t ARGS_THREE = 3;
static const int32_t PARAM0_EVENT = 0;
static const int32_t PARAM1_EVENT = 1;
static const int8_t ERR_CES_FAILED = 1;
static const int32_t PUBLISH_MAX_PARA = 1;
static const int32_t ERROR_USERID = -1;
static const int32_t PUBLISH_MAX_PARA_BY_PUBLISHDATA = 2;
static const int32_t ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID = 401;
static const char* const BUNDLE_NAME_PROPERTY = "bundleName";
static const char* const CODE_PROPERTY = "code";
static const char* const DATA_PROPERTY = "data";
static const char* const PARAMETERS_PROPERTY = "parameters";
static thread_local napi_ref g_CommonEventSubscriber = nullptr;
static std::vector<std::shared_ptr<SubscriberInstance>> subscriberInstances;
static std::mutex subscriberInsMutex;
struct PublishPayload {
std::string bundleName;
std::string data;
std::string parameters;
int32_t code = 0;
bool hasBundleName = false;
bool hasData = false;
bool hasParameters = false;
bool hasCode = false;
};
static const std::unordered_map<int32_t, std::string> ErrorCodeToMsg {
{ ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID, "Parameter error." },
};
bool GetStringValue(napi_env env, napi_value value, std::string& result, size_t limit)
{
size_t length = 0;
if (napi_get_value_string_utf8(env, value, nullptr, 0, &length) != napi_ok) {
return false;
}
if (length > limit) {
LOGE("String value truncated: original length %{public}zu exceeds limit %{public}zu.", length, limit);
length = limit;
}
std::vector<char> buffer(length + 1, 0);
if (napi_get_value_string_utf8(env, value, buffer.data(), buffer.size(), &length) != napi_ok) {
return false;
}
result.assign(buffer.data(), length);
return true;
}
void ClearPendingException(napi_env env)
{
bool isPending = false;
if (napi_is_exception_pending(env, &isPending) != napi_ok || !isPending) {
return;
}
napi_value exception = nullptr;
napi_get_and_clear_last_exception(env, &exception);
napi_value message = nullptr;
if (exception != nullptr && napi_get_named_property(env, exception, "message", &message) == napi_ok) {
std::string msg;
GetStringValue(env, message, msg, STR_MAX_SIZE - 1);
LOGW("N-API exception cleared: %{public}s", msg.c_str());
}
}
bool ParseJsonString(napi_env env, const std::string& value, napi_value& result)
{
napi_value global = nullptr;
napi_value json = nullptr;
napi_value parse = nullptr;
napi_value input = nullptr;
bool isJsonParseReady = napi_get_global(env, &global) == napi_ok &&
napi_get_named_property(env, global, "JSON", &json) == napi_ok &&
napi_get_named_property(env, json, "parse", &parse) == napi_ok &&
napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &input) == napi_ok;
if (!isJsonParseReady) {
return false;
}
if (napi_call_function(env, json, parse, 1, &input, &result) != napi_ok) {
ClearPendingException(env);
return false;
}
return true;
}
bool StringifyJsonValue(napi_env env, napi_value value, std::string& result)
{
napi_value global = nullptr;
napi_value json = nullptr;
napi_value stringify = nullptr;
napi_value output = nullptr;
bool isJsonStringifyReady = napi_get_global(env, &global) == napi_ok &&
napi_get_named_property(env, global, "JSON", &json) == napi_ok &&
napi_get_named_property(env, json, "stringify", &stringify) == napi_ok;
if (!isJsonStringifyReady) {
return false;
}
if (napi_call_function(env, json, stringify, 1, &value, &output) != napi_ok) {
ClearPendingException(env);
return false;
}
return GetStringValue(env, output, result, STR_DATA_MAX_SIZE - 1);
}
bool ReadBundleName(napi_env env, napi_value object, PublishPayload& payload)
{
bool hasProperty = false;
napi_value value = nullptr;
napi_valuetype valueType = napi_undefined;
if (napi_has_named_property(env, object, BUNDLE_NAME_PROPERTY, &hasProperty) != napi_ok) {
return false;
}
if (!hasProperty) {
return true;
}
if (napi_get_named_property(env, object, BUNDLE_NAME_PROPERTY, &value) != napi_ok ||
napi_typeof(env, value, &valueType) != napi_ok) {
return false;
}
if (valueType != napi_string || !GetStringValue(env, value, payload.bundleName, STR_MAX_SIZE - 1)) {
LOGE("Parameter type error. String expected.");
return false;
}
payload.hasBundleName = true;
return true;
}
bool ReadCode(napi_env env, napi_value object, PublishPayload& payload)
{
bool hasProperty = false;
napi_value value = nullptr;
napi_valuetype valueType = napi_undefined;
if (napi_has_named_property(env, object, CODE_PROPERTY, &hasProperty) != napi_ok) {
return false;
}
if (!hasProperty) {
return true;
}
if (napi_get_named_property(env, object, CODE_PROPERTY, &value) != napi_ok ||
napi_typeof(env, value, &valueType) != napi_ok) {
return false;
}
if (valueType != napi_number || napi_get_value_int32(env, value, &payload.code) != napi_ok) {
LOGE("Parameter type error. Number expected.");
return false;
}
payload.hasCode = true;
return true;
}
bool ReadData(napi_env env, napi_value object, PublishPayload& payload)
{
bool hasProperty = false;
napi_value value = nullptr;
napi_valuetype valueType = napi_undefined;
if (napi_has_named_property(env, object, DATA_PROPERTY, &hasProperty) != napi_ok) {
return false;
}
if (!hasProperty) {
return true;
}
if (napi_get_named_property(env, object, DATA_PROPERTY, &value) != napi_ok ||
napi_typeof(env, value, &valueType) != napi_ok) {
return false;
}
if (valueType != napi_string || !GetStringValue(env, value, payload.data, STR_DATA_MAX_SIZE - 1)) {
LOGE("Parameter type error. String expected.");
return false;
}
payload.hasData = true;
return true;
}
bool ReadParameters(napi_env env, napi_value object, PublishPayload& payload)
{
bool hasProperty = false;
bool isArray = false;
napi_value value = nullptr;
napi_valuetype valueType = napi_undefined;
if (napi_has_named_property(env, object, PARAMETERS_PROPERTY, &hasProperty) != napi_ok) {
return false;
}
if (!hasProperty) {
return true;
}
if (napi_get_named_property(env, object, PARAMETERS_PROPERTY, &value) != napi_ok ||
napi_typeof(env, value, &valueType) != napi_ok) {
return false;
}
if (valueType != napi_object || napi_is_array(env, value, &isArray) != napi_ok || isArray ||
!StringifyJsonValue(env, value, payload.parameters)) {
LOGE("Parameter type error. Object expected.");
return false;
}
payload.hasParameters = true;
return true;
}
bool BuildPublishJson(const PublishPayload& payload, std::string& data)
{
nlohmann::json jsonValue = nlohmann::json::object();
if (payload.hasBundleName) {
jsonValue[BUNDLE_NAME_PROPERTY] = payload.bundleName;
}
if (payload.hasCode) {
jsonValue[CODE_PROPERTY] = payload.code;
}
if (payload.hasData) {
jsonValue[DATA_PROPERTY] = payload.data;
}
if (payload.hasParameters) {
auto parameters = nlohmann::json::parse(payload.parameters, nullptr, false);
if (parameters.is_discarded() || !parameters.is_object()) {
return false;
}
jsonValue[PARAMETERS_PROPERTY] = parameters;
}
data = jsonValue.empty() ? "{}" : jsonValue.dump();
return true;
}
bool SetStringProperty(napi_env env, napi_value result, const char* name, const std::string& value)
{
napi_value property = nullptr;
return napi_create_string_utf8(env, value.c_str(), NAPI_AUTO_LENGTH, &property) == napi_ok &&
napi_set_named_property(env, result, name, property) == napi_ok;
}
bool SetInt32Property(napi_env env, napi_value result, const char* name, int32_t value)
{
napi_value property = nullptr;
return napi_create_int32(env, value, &property) == napi_ok &&
napi_set_named_property(env, result, name, property) == napi_ok;
}
bool SetParametersProperty(const CommonEventDataWorker& worker, napi_value result)
{
if (worker.parameters.empty()) {
return true;
}
napi_value parameters = nullptr;
if (!ParseJsonString(worker.env, worker.parameters, parameters)) {
return false;
}
return napi_set_named_property(worker.env, result, PARAMETERS_PROPERTY, parameters) == napi_ok;
}
napi_value NapiGetNull(napi_env env)
{
napi_value result = nullptr;
napi_get_null(env, &result);
return result;
}
napi_value GetCallbackErrorValue(napi_env env, int8_t errorCode)
{
napi_value result = NapiGetNull(env);
napi_value eCode = NapiGetNull(env);
if (errorCode == ERR_OK) {
return result;
}
NAPI_CALL(env, napi_create_int32(env, errorCode, &eCode));
NAPI_CALL(env, napi_create_object(env, &result));
NAPI_CALL(env, napi_set_named_property(env, result, "code", eCode));
auto iter = ErrorCodeToMsg.find(errorCode);
std::string errMsg = iter != ErrorCodeToMsg.end() ? iter->second : "";
napi_value message = nullptr;
napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &message);
napi_set_named_property(env, result, "message", message);
return result;
}
void SetPromise(const napi_env& env, const napi_deferred& deferred, const int8_t& errorCode, const napi_value& result)
{
if (errorCode == 0) {
napi_resolve_deferred(env, deferred, result);
} else {
napi_reject_deferred(env, deferred, GetCallbackErrorValue(env, errorCode));
}
}
void SetCallback(const napi_env& env, const napi_ref& callbackIn, const int8_t& errorCode, const napi_value& result)
{
napi_value undefined = nullptr;
napi_get_undefined(env, &undefined);
napi_value callback = nullptr;
napi_value resultout = nullptr;
napi_get_reference_value(env, callbackIn, &callback);
napi_value results[ARGS_TWO_EVENT] = { nullptr };
results[PARAM0_EVENT] = GetCallbackErrorValue(env, errorCode);
results[PARAM1_EVENT] = result;
NAPI_CALL_RETURN_VOID(
env, napi_call_function(env, undefined, callback, ARGS_TWO_EVENT, &results[PARAM0_EVENT], &resultout));
}
void ReturnCallbackPromise(const napi_env& env, const CallbackPromiseInfo& info, const napi_value& result)
{
if (info.isCallback) {
SetCallback(env, info.callback, info.errorCode, result);
} else {
SetPromise(env, info.deferred, info.errorCode, result);
}
}
napi_value ParseParametersByCreateSubscriber(
const napi_env& env, const napi_value (&argv)[CREATE_MAX_PARA], const size_t& argc, napi_ref& callback)
{
napi_valuetype valuetype;
NAPI_CALL(env, napi_typeof(env, argv[0], &valuetype));
if (valuetype != napi_object) {
LOGE("Wrong argument type. object expected.");
return nullptr;
}
if (argc >= CREATE_MAX_PARA) {
NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
if (valuetype != napi_function) {
LOGE("Wrong argument type. Function expected.");
return nullptr;
}
napi_create_reference(env, argv[1], 1, &callback);
}
return NapiGetNull(env);
}
void PaddingAsyncCallbackInfoCreateSubscriber(const napi_env& env, const size_t& argc,
AsyncCallbackInfoCreate*& asyncCallbackInfo, const napi_ref& callback, napi_value& promise)
{
if (argc >= CREATE_MAX_PARA) {
asyncCallbackInfo->info.callback = callback;
asyncCallbackInfo->info.isCallback = true;
} else {
napi_deferred deferred = nullptr;
napi_create_promise(env, &deferred, &promise);
asyncCallbackInfo->info.deferred = deferred;
asyncCallbackInfo->info.isCallback = false;
}
}
std::shared_ptr<SubscriberInstance> GetSubscriber(const napi_env& env, const napi_value& value)
{
LOGI("Called.");
SubscriberInstanceWrapper* wrapper = nullptr;
napi_unwrap(env, value, (void**)&wrapper);
if (wrapper == nullptr) {
LOGW("Wrapper is nullptr.");
return nullptr;
}
return wrapper->GetSubscriber();
}
napi_value GetSubscriberByUnsubscribe(
const napi_env& env, const napi_value& value, std::shared_ptr<SubscriberInstance>& subscriber, bool& isFind)
{
LOGI("Called.");
isFind = false;
subscriber = GetSubscriber(env, value);
if (subscriber == nullptr) {
LOGE("Subscriber is nullptr.");
return NapiGetNull(env);
}
std::lock_guard<std::mutex> lock(subscriberInsMutex);
for (auto subscriberInstance : subscriberInstances) {
if (subscriberInstance.get() == subscriber.get()) {
subscriber = subscriberInstance;
isFind = true;
break;
}
}
return NapiGetNull(env);
}
napi_value ParseParametersByUnsubscribe(const napi_env& env, const size_t& argc,
const napi_value (&argv)[UNSUBSCRIBE_MAX_PARA], std::shared_ptr<SubscriberInstance>& subscriber, napi_ref& callback)
{
LOGI("Called.");
napi_valuetype valuetype;
napi_value result = nullptr;
NAPI_CALL(env, napi_typeof(env, argv[0], &valuetype));
if (valuetype != napi_object) {
LOGI("Wrong argument type for arg0. Subscribe expected.");
return nullptr;
}
bool isFind = false;
if (GetSubscriberByUnsubscribe(env, argv[0], subscriber, isFind) == nullptr) {
return nullptr;
}
if (argc >= UNSUBSCRIBE_MAX_PARA) {
NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
if (valuetype != napi_function) {
LOGI("Wrong argument type. Function expected.");
return nullptr;
}
napi_create_reference(env, argv[1], 1, &callback);
}
napi_get_boolean(env, isFind, &result);
return result;
}
SubscriberInstanceWrapper::SubscriberInstanceWrapper(const CommonEventSubscribeInfo& info)
{
auto objectInfo = new (std::nothrow) SubscriberInstance(info);
if (objectInfo == nullptr) {
LOGE("Object info is nullptr.");
return;
}
subscriber = std::shared_ptr<SubscriberInstance>(objectInfo);
}
std::shared_ptr<SubscriberInstance> SubscriberInstanceWrapper::GetSubscriber()
{
return subscriber;
}
napi_value SetCommonEventData(const CommonEventDataWorker* commonEventDataWorkerData, napi_value& result)
{
if (commonEventDataWorkerData == nullptr) {
return nullptr;
}
if (!SetStringProperty(commonEventDataWorkerData->env, result, "event", commonEventDataWorkerData->event) ||
!SetStringProperty(
commonEventDataWorkerData->env, result, BUNDLE_NAME_PROPERTY, commonEventDataWorkerData->bundleName) ||
!SetInt32Property(commonEventDataWorkerData->env, result, CODE_PROPERTY, commonEventDataWorkerData->code) ||
!SetStringProperty(commonEventDataWorkerData->env, result, DATA_PROPERTY, commonEventDataWorkerData->data)) {
return nullptr;
}
if (!SetParametersProperty(*commonEventDataWorkerData, result)) {
LOGW("Failed to parse optional common event parameters, core event data will still be delivered.");
}
return NapiGetNull(commonEventDataWorkerData->env);
}
napi_value ParseParametersBySubscribe(const napi_env& env, const napi_value (&argv)[SUBSCRIBE_MAX_PARA],
std::shared_ptr<SubscriberInstance>& subscriber, napi_ref& callback)
{
LOGI("Called.");
napi_valuetype valuetype;
NAPI_CALL(env, napi_typeof(env, argv[0], &valuetype));
if (valuetype != napi_object) {
LOGE("Wrong argument type for arg0. Subscribe expected.");
return nullptr;
}
subscriber = GetSubscriber(env, argv[0]);
if (subscriber == nullptr) {
LOGE("subscriber is nullptr.");
return nullptr;
}
NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
if (valuetype != napi_function) {
LOGE("Wrong argument type. Function expected.");
return nullptr;
}
napi_create_reference(env, argv[1], 1, &callback);
return NapiGetNull(env);
}
void UvQueueWorkOnReceiveEvent(uv_work_t* work, int status)
{
if (work == nullptr || work->data == nullptr) {
return;
}
CommonEventDataWorker* commonEventDataWorker = reinterpret_cast<CommonEventDataWorker*>(work->data);
napi_handle_scope scope;
napi_open_handle_scope(commonEventDataWorker->env, &scope);
napi_value result = nullptr;
napi_create_object(commonEventDataWorker->env, &result);
if (SetCommonEventData(commonEventDataWorker, result) == nullptr) {
napi_close_handle_scope(commonEventDataWorker->env, scope);
delete work;
work = nullptr;
delete commonEventDataWorker;
commonEventDataWorker = nullptr;
return;
}
napi_value undefined = nullptr;
napi_get_undefined(commonEventDataWorker->env, &undefined);
napi_value callback = nullptr;
napi_value resultout = nullptr;
napi_get_reference_value(commonEventDataWorker->env, commonEventDataWorker->ref, &callback);
napi_value results[ARGS_TWO_EVENT] = { nullptr };
results[PARAM0_EVENT] = GetCallbackErrorValue(commonEventDataWorker->env, 0);
results[PARAM1_EVENT] = result;
napi_call_function(
commonEventDataWorker->env, undefined, callback, ARGS_TWO_EVENT, &results[PARAM0_EVENT], &resultout);
napi_close_handle_scope(commonEventDataWorker->env, scope);
delete commonEventDataWorker;
commonEventDataWorker = nullptr;
delete work;
}
SubscriberInstance::SubscriberInstance(const CommonEventSubscribeInfo& subscribeInfo)
: CommonEventSubscriber(subscribeInfo)
{}
SubscriberInstance::~SubscriberInstance() {}
void SubscriberInstance::SetEnv(const napi_env& env)
{
env_ = env;
}
void SubscriberInstance::SetCallbackRef(const napi_ref& ref)
{
ref_ = ref;
}
napi_value ParseParametersByGetSubscribeInfo(
const napi_env& env, const size_t& argc, const napi_value (&argv)[1], napi_ref& callback)
{
napi_valuetype valuetype;
if (argc >= 1) {
NAPI_CALL(env, napi_typeof(env, argv[0], &valuetype));
if (valuetype != napi_function) {
LOGE("Wrong argument type. Function expected.");
return nullptr;
}
napi_create_reference(env, argv[0], 1, &callback);
}
return NapiGetNull(env);
}
void PaddingAsyncCallbackInfoGetSubscribeInfo(const napi_env& env, const size_t& argc,
AsyncCallbackInfoSubscribeInfo*& asyncCallbackInfo, const napi_ref& callback, napi_value& promise)
{
LOGI("Called.");
if (argc >= 1) {
asyncCallbackInfo->info.callback = callback;
asyncCallbackInfo->info.isCallback = true;
} else {
napi_deferred deferred = nullptr;
NAPI_CALL_RETURN_VOID(env, napi_create_promise(env, &deferred, &promise));
asyncCallbackInfo->info.deferred = deferred;
asyncCallbackInfo->info.isCallback = false;
}
}
void PaddingNapiCreateAsyncWorkCallbackInfo(AsyncCallbackInfoSubscribeInfo*& asyncCallbackInfo)
{
LOGI("Called.");
asyncCallbackInfo->events = asyncCallbackInfo->subscriber->GetSubscribeInfo().GetEvents();
asyncCallbackInfo->permission = "";
asyncCallbackInfo->deviceId = "";
asyncCallbackInfo->userId = ERROR_USERID;
asyncCallbackInfo->priority = 0;
}
void SetEventsResult(const napi_env& env, const std::vector<std::string>& events, napi_value& commonEventSubscribeInfo)
{
LOGI("Called.");
napi_value value = nullptr;
if (events.size() > 0) {
napi_value nEvents = nullptr;
if (napi_create_array(env, &nEvents) != napi_ok) {
return;
}
size_t index = 0;
for (auto event : events) {
napi_create_string_utf8(env, event.c_str(), NAPI_AUTO_LENGTH, &value);
napi_set_element(env, nEvents, index, value);
index++;
}
napi_set_named_property(env, commonEventSubscribeInfo, "events", nEvents);
}
}
void SetNapiResult(const napi_env& env, const AsyncCallbackInfoSubscribeInfo* asyncCallbackInfo, napi_value& result)
{
LOGI("Called.");
SetEventsResult(env, asyncCallbackInfo->events, result);
}
napi_value GetSubscribeInfo(napi_env env, napi_callback_info info)
{
size_t argc = 1;
napi_value argv[1] = { nullptr };
napi_value thisVar = nullptr;
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, NULL));
napi_ref callback = nullptr;
if (ParseParametersByGetSubscribeInfo(env, argc, argv, callback) == nullptr) {
return NapiGetNull(env);
}
AsyncCallbackInfoSubscribeInfo* asyncCallbackInfo =
new (std::nothrow) AsyncCallbackInfoSubscribeInfo { .env = env, .asyncWork = nullptr, .userId = ERROR_USERID };
if (asyncCallbackInfo == nullptr) {
return NapiGetNull(env);
}
asyncCallbackInfo->subscriber = GetSubscriber(env, thisVar);
if (asyncCallbackInfo->subscriber == nullptr) {
return NapiGetNull(env);
}
napi_value promise = nullptr;
PaddingAsyncCallbackInfoGetSubscribeInfo(env, argc, asyncCallbackInfo, callback, promise);
napi_value resourceName = nullptr;
napi_create_string_latin1(env, "getSubscribeInfo", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(
env, nullptr, resourceName,
[](napi_env env, void* data) {
AsyncCallbackInfoSubscribeInfo* asyncCallbackInfo = (AsyncCallbackInfoSubscribeInfo*)data;
PaddingNapiCreateAsyncWorkCallbackInfo(asyncCallbackInfo);
},
[](napi_env env, napi_status status, void* data) {
AsyncCallbackInfoSubscribeInfo* asyncCallbackInfo = (AsyncCallbackInfoSubscribeInfo*)data;
if (asyncCallbackInfo) {
napi_value result = nullptr;
napi_create_object(env, &result);
SetNapiResult(env, asyncCallbackInfo, result);
ReturnCallbackPromise(env, asyncCallbackInfo->info, result);
if (asyncCallbackInfo->info.callback != nullptr) {
napi_delete_reference(env, asyncCallbackInfo->info.callback);
}
napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
delete asyncCallbackInfo;
asyncCallbackInfo = nullptr;
}
},
(void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork);
NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));
if (asyncCallbackInfo->info.isCallback) {
return NapiGetNull(env);
} else {
return promise;
}
}
void SubscriberInstance::OnReceiveEvent(const CommonEventData& data)
{
uv_loop_s* loop = nullptr;
napi_get_uv_event_loop(env_, &loop);
if (loop == nullptr) {
LOGE("Loop instance is nullptr.");
return;
}
uv_work_t* work = new (std::nothrow) uv_work_t;
if (work == nullptr) {
LOGE("Work is null.");
return;
}
CommonEventDataWorker* commonEventDataWorker = new (std::nothrow) CommonEventDataWorker();
if (commonEventDataWorker == nullptr) {
LOGE("Common event data worker is null.");
delete work;
work = nullptr;
return;
}
commonEventDataWorker->event = data.GetEvent();
commonEventDataWorker->code = data.GetCode();
commonEventDataWorker->data = data.GetData();
commonEventDataWorker->parameters = data.GetParameters();
commonEventDataWorker->env = env_;
commonEventDataWorker->ref = ref_;
commonEventDataWorker->bundleName = data.GetBundleName();
work->data = (void*)commonEventDataWorker;
uv_queue_work(
loop, work, [](uv_work_t* work) {}, UvQueueWorkOnReceiveEvent);
}
napi_value GetEventsByCreateSubscriber(const napi_env& env, const napi_value& argv, std::vector<std::string>& events)
{
napi_valuetype valuetype;
bool hasProperty = false;
bool isArray = false;
napi_value eventsNapi = nullptr;
size_t strLen = 0;
uint32_t length = 0;
NAPI_CALL(env, napi_has_named_property(env, argv, "events", &hasProperty));
if (!hasProperty) {
LOGE("Property events expected.");
return nullptr;
}
napi_get_named_property(env, argv, "events", &eventsNapi);
napi_is_array(env, eventsNapi, &isArray);
if (!isArray) {
LOGE("Wrong argument type. Array expected.");
return nullptr;
}
napi_get_array_length(env, eventsNapi, &length);
if (length <= 0) {
LOGE("The array is empty.");
return nullptr;
}
for (size_t i = 0; i < length; i++) {
napi_value event = nullptr;
napi_get_element(env, eventsNapi, i, &event);
NAPI_CALL(env, napi_typeof(env, event, &valuetype));
if (valuetype != napi_string) {
LOGE("Wrong argument type. String expected.");
return nullptr;
}
char str[STR_MAX_SIZE] = { 0 };
NAPI_CALL(env, napi_get_value_string_utf8(env, event, str, STR_MAX_SIZE - 1, &strLen));
LOGI("Event is %{public}s", str);
events.emplace_back(str);
}
return NapiGetNull(env);
}
napi_value ParseParametersConstructor(
const napi_env& env, const napi_callback_info& info, napi_value& thisVar, CommonEventSubscribeInfo& params)
{
size_t argc = 1;
napi_value argv[1] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, &thisVar, nullptr));
if (argc < 1) {
LOGE("Wrong number of arguments.");
return nullptr;
}
std::vector<std::string> events;
if (!GetEventsByCreateSubscriber(env, argv[0], events)) {
return nullptr;
}
CommonEventSubscribeInfo subscribeInfo(events);
params = subscribeInfo;
return NapiGetNull(env);
}
napi_value CommonEventSubscriberConstructor(napi_env env, napi_callback_info info)
{
napi_value thisVar = nullptr;
CommonEventSubscribeInfo subscribeInfo;
if (!ParseParametersConstructor(env, info, thisVar, subscribeInfo)) {
return NapiGetNull(env);
}
auto wrapper = new (std::nothrow) SubscriberInstanceWrapper(subscribeInfo);
if (wrapper == nullptr) {
LOGE("Wrapper is nullptr.");
return NapiGetNull(env);
}
napi_wrap(
env, thisVar, wrapper,
[](napi_env env, void* data, void* hint) {
auto* wrapper = reinterpret_cast<SubscriberInstanceWrapper*>(data);
std::lock_guard<std::mutex> lock(subscriberInsMutex);
for (auto it = subscriberInstances.begin(); it != subscriberInstances.end(); it++) {
if (it->get() == wrapper->GetSubscriber().get()) {
wrapper->GetSubscriber()->SetCallbackRef(nullptr);
CommonEventManager::GetInstance().UnSubscribeCommonEvent(*it, nullptr);
subscriberInstances.erase(it);
break;
}
}
delete wrapper;
wrapper = nullptr;
},
nullptr, nullptr);
return thisVar;
}
void NapiThrow(napi_env env, int32_t errCode)
{
napi_value code = nullptr;
napi_create_int32(env, errCode, &code);
auto iter = ErrorCodeToMsg.find(errCode);
std::string errMsg = iter != ErrorCodeToMsg.end() ? iter->second : "";
napi_value message = nullptr;
napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &message);
napi_value error = nullptr;
napi_create_error(env, nullptr, message, &error);
napi_set_named_property(env, error, "code", code);
napi_throw(env, error);
}
void NapiThrow(napi_env env, int32_t errCode, const std::string& detail)
{
napi_value code = nullptr;
napi_create_int32(env, errCode, &code);
auto iter = ErrorCodeToMsg.find(errCode);
std::string errMsg = (iter != ErrorCodeToMsg.end() ? iter->second : "");
if (!detail.empty()) {
errMsg.append(" ").append(detail);
}
napi_value message = nullptr;
napi_create_string_utf8(env, errMsg.c_str(), NAPI_AUTO_LENGTH, &message);
napi_value error = nullptr;
napi_create_error(env, nullptr, message, &error);
napi_set_named_property(env, error, "code", code);
napi_throw(env, error);
}
napi_value CreateSubscriber(napi_env env, napi_callback_info info)
{
size_t argc = CREATE_MAX_PARA;
napi_value argv[CREATE_MAX_PARA] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
if (argc < 1) {
return NapiGetNull(env);
}
napi_ref callback = nullptr;
if (ParseParametersByCreateSubscriber(env, argv, argc, callback) == nullptr) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID,
std::string("Incorrect parameter types.The type of param must be object."));
return NapiGetNull(env);
}
AsyncCallbackInfoCreate* asyncCallbackInfo =
new (std::nothrow) AsyncCallbackInfoCreate { .env = env, .asyncWork = nullptr, .subscribeInfo = nullptr };
if (asyncCallbackInfo == nullptr) {
return NapiGetNull(env);
}
napi_value promise = nullptr;
PaddingAsyncCallbackInfoCreateSubscriber(env, argc, asyncCallbackInfo, callback, promise);
napi_create_reference(env, argv[0], 1, &asyncCallbackInfo->subscribeInfo);
napi_value resourceName = nullptr;
napi_create_string_latin1(env, "CreateSubscriber", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(
env, nullptr, resourceName,
[](napi_env env, void* data) { LOGI("CreateSubscriber napi_create_async_work start"); },
[](napi_env env, napi_status status, void* data) {
AsyncCallbackInfoCreate* asyncCallbackInfo = (AsyncCallbackInfoCreate*)data;
if (asyncCallbackInfo) {
napi_value constructor = nullptr;
napi_value subscribeInfoRefValue = nullptr;
napi_get_reference_value(env, asyncCallbackInfo->subscribeInfo, &subscribeInfoRefValue);
napi_get_reference_value(env, g_CommonEventSubscriber, &constructor);
napi_new_instance(env, constructor, 1, &subscribeInfoRefValue, &asyncCallbackInfo->result);
if (asyncCallbackInfo->result == nullptr) {
asyncCallbackInfo->info.errorCode = ERR_CES_FAILED;
}
ReturnCallbackPromise(env, asyncCallbackInfo->info, asyncCallbackInfo->result);
if (asyncCallbackInfo->info.callback != nullptr) {
napi_delete_reference(env, asyncCallbackInfo->info.callback);
}
if (asyncCallbackInfo->subscribeInfo != nullptr) {
napi_delete_reference(env, asyncCallbackInfo->subscribeInfo);
}
napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
delete asyncCallbackInfo;
asyncCallbackInfo = nullptr;
}
},
(void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork);
NAPI_CALL(env, napi_queue_async_work(env, asyncCallbackInfo->asyncWork));
if (asyncCallbackInfo->info.isCallback) {
return NapiGetNull(env);
} else {
return promise;
}
}
napi_value GetDataByPublish(const napi_env& env, const napi_value& value, std::string& data)
{
PublishPayload payload;
static const std::string stringParamErr =
"Incorrect parameter types.The type of param must be string.";
static const std::string numberParamErr =
"Incorrect parameter types.The type of param must be number.";
static const std::string objectParamErr =
"Incorrect parameter types.The type of param must be object.";
if (!ReadBundleName(env, value, payload)) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID, stringParamErr);
return nullptr;
}
if (!ReadCode(env, value, payload)) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID, numberParamErr);
return nullptr;
}
if (!ReadData(env, value, payload)) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID, stringParamErr);
return nullptr;
}
if (!ReadParameters(env, value, payload)) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID, objectParamErr);
return nullptr;
}
if (!BuildPublishJson(payload, data)) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID, "Failed to build publish data.");
return nullptr;
}
return NapiGetNull(env);
}
napi_value ParseParametersByPublish(const napi_env& env, const napi_value (&argv)[ARGS_THREE], const size_t& argc,
std::string& event, std::string& commonEventPublishData, napi_ref& callback)
{
napi_valuetype valuetype;
NAPI_CALL(env, napi_typeof(env, argv[0], &valuetype));
if (valuetype != napi_string) {
LOGE("Parameter type error. String expected.");
return nullptr;
}
char str[STR_MAX_SIZE] = { 0 };
size_t strLen = 0;
napi_get_value_string_utf8(env, argv[0], str, STR_MAX_SIZE - 1, &strLen);
event = str;
if (argc == ARGS_THREE) {
NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
if (valuetype != napi_object) {
LOGE("Wrong argument type. Object expected.");
return nullptr;
}
if (GetDataByPublish(env, argv[1], commonEventPublishData) == nullptr) {
return nullptr;
}
NAPI_CALL(env, napi_typeof(env, argv[PUBLISH_MAX_PARA_BY_PUBLISHDATA], &valuetype));
if (valuetype != napi_function) {
LOGE("Wrong argument type. Function expected.");
return nullptr;
}
napi_create_reference(env, argv[PUBLISH_MAX_PARA_BY_PUBLISHDATA], 1, &callback);
} else {
NAPI_CALL(env, napi_typeof(env, argv[1], &valuetype));
if (valuetype != napi_function) {
LOGE("Wrong argument type. Function expected.");
return nullptr;
}
napi_create_reference(env, argv[1], 1, &callback);
}
return NapiGetNull(env);
}
void CreateAsyncCallbackWork(napi_env env, AsyncCallbackInfo* asyncCallbackInfo)
{
if (asyncCallbackInfo == nullptr) {
LOGE("Async callback info is nullptr.");
return;
}
napi_value resourceName = nullptr;
napi_create_string_latin1(env, "CreateAsyncCallbackWork", NAPI_AUTO_LENGTH, &resourceName);
napi_create_async_work(
env, nullptr, resourceName, [](napi_env env, void* data) { LOGD("Napi create async work start."); },
[](napi_env env, napi_status status, void* data) {
LOGD("Napi async work complete.");
AsyncCallbackInfo* asyncCallbackInfo = (AsyncCallbackInfo*)data;
if (asyncCallbackInfo != nullptr) {
if (asyncCallbackInfo->status < 0) {
asyncCallbackInfo->status = ERR_INVALID_VALUE;
} else {
asyncCallbackInfo->status = ERR_OK;
}
SetCallback(env, asyncCallbackInfo->callback[0], asyncCallbackInfo->status, NapiGetNull(env));
if (asyncCallbackInfo->callback != nullptr) {
LOGD("Delete napi async callback reference.");
napi_delete_reference(env, asyncCallbackInfo->callback[0]);
}
napi_delete_async_work(env, asyncCallbackInfo->asyncWork);
delete asyncCallbackInfo;
asyncCallbackInfo = nullptr;
}
},
(void*)asyncCallbackInfo, &asyncCallbackInfo->asyncWork);
napi_queue_async_work(env, asyncCallbackInfo->asyncWork);
}
napi_value PublishCommonEvent(napi_env env, napi_callback_info info)
{
size_t argc = ARGS_THREE;
napi_value argv[ARGS_THREE] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NAPI_ASSERT(env, argc >= PUBLISH_MAX_PARA, "Wrong number of arguments");
std::string event;
std::string commonEventPublishData = "";
napi_ref callback;
if (ParseParametersByPublish(env, argv, argc, event, commonEventPublishData, callback) == nullptr) {
bool isPending = false;
if (napi_is_exception_pending(env, &isPending) != napi_ok || !isPending) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID);
}
return NapiGetNull(env);
}
AsyncCallbackInfo* asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo { .env = env, .asyncWork = nullptr };
asyncCallbackInfo->callback[0] = callback;
CommonEventManager::GetInstance().PublishCommonEvent(event, commonEventPublishData, asyncCallbackInfo);
CreateAsyncCallbackWork(env, asyncCallbackInfo);
return NapiGetNull(env);
}
napi_value SubscribeCommonEvent(napi_env env, napi_callback_info info)
{
size_t argc = SUBSCRIBE_MAX_PARA;
napi_value argv[SUBSCRIBE_MAX_PARA] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NAPI_ASSERT(env, argc >= SUBSCRIBE_MAX_PARA, "Wrong number of arguments");
napi_ref callback = nullptr;
std::shared_ptr<SubscriberInstance> subscriber = nullptr;
if (ParseParametersBySubscribe(env, argv, subscriber, callback) == nullptr) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID,
std::string("Incorrect parameter types.The type of param must be object."));
return NapiGetNull(env);
}
AsyncCallbackInfo* asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo { .env = env, .asyncWork = nullptr };
if (asyncCallbackInfo == nullptr) {
LOGE("Async callback is null");
return NapiGetNull(env);
}
asyncCallbackInfo->callback[0] = callback;
subscriber->SetEnv(env);
subscriber->SetCallbackRef(callback);
{
std::lock_guard<std::mutex> lock(subscriberInsMutex);
subscriberInstances.push_back(subscriber);
}
CommonEventManager::GetInstance().SubscribeCommonEvent(subscriber);
return NapiGetNull(env);
}
napi_value UnSubscribeCommonEvent(napi_env env, napi_callback_info info)
{
size_t argc = UNSUBSCRIBE_MAX_PARA;
napi_value argv[UNSUBSCRIBE_MAX_PARA] = { nullptr };
NAPI_CALL(env, napi_get_cb_info(env, info, &argc, argv, NULL, NULL));
NAPI_ASSERT(env, argc >= 1, "Wrong number of arguments");
napi_ref callback = nullptr;
std::shared_ptr<SubscriberInstance> subscriber = nullptr;
napi_value result = nullptr;
result = ParseParametersByUnsubscribe(env, argc, argv, subscriber, callback);
if (result == nullptr) {
NapiThrow(env, ERR_NOTIFICATION_CES_COMMON_PARAM_INVALID);
return NapiGetNull(env);
}
bool isFind = false;
napi_get_value_bool(env, result, &isFind);
if (!isFind) {
LOGI("Unsubscribe failed. The current subscriber does not exist");
return NapiGetNull(env);
}
AsyncCallbackInfo* asyncCallbackInfo = new (std::nothrow) AsyncCallbackInfo();
if (asyncCallbackInfo == nullptr) {
LOGE("Async callback is nullptr.");
return NapiGetNull(env);
}
asyncCallbackInfo->env = env;
if (argc >= UNSUBSCRIBE_MAX_PARA) {
asyncCallbackInfo->callback[0] = callback;
}
CommonEventManager::GetInstance().UnSubscribeCommonEvent(subscriber, asyncCallbackInfo);
CreateAsyncCallbackWork(env, asyncCallbackInfo);
return NapiGetNull(env);
}
napi_value CommonEventSubscriberInit(napi_env env, napi_value exports)
{
napi_value constructor = nullptr;
napi_property_descriptor properties[] = {
DECLARE_NAPI_FUNCTION("getSubscribeInfo", GetSubscribeInfo),
};
NAPI_CALL(env, napi_define_class(env, "commonEventSubscriber", NAPI_AUTO_LENGTH, CommonEventSubscriberConstructor,
nullptr, sizeof(properties) / sizeof(*properties), properties, &constructor));
napi_create_reference(env, constructor, 1, &g_CommonEventSubscriber);
if (g_CommonEventSubscriber == nullptr) {
LOGI("g_CommonEventSubscriber is nullptr.");
}
napi_set_named_property(env, exports, "commonEventSubscriber", constructor);
if (g_CommonEventSubscriber == nullptr) {
LOGI("g_CommonEventSubscriber is nullptr.");
}
return exports;
}
napi_value CommonEventInit(napi_env env, napi_value exports)
{
static napi_property_descriptor desc[] = {
DECLARE_NAPI_FUNCTION("createSubscriber", CreateSubscriber),
DECLARE_NAPI_FUNCTION("publish", PublishCommonEvent),
DECLARE_NAPI_FUNCTION("subscribe", SubscribeCommonEvent),
DECLARE_NAPI_FUNCTION("unsubscribe", UnSubscribeCommonEvent),
};
NAPI_CALL(env, napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc));
return exports;
}
}
}