* Copyright (c) 2023-2025 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_async_event.h"
#include "log.h"
#include "napi_utils.h"
#include "napi/native_api.h"
#include "napi/native_common.h"
#include "plugins/interfaces/native/inner_api/plugin_utils_napi.h"
#include "buffer_mapping.h"
#include "bridge_binary_codec.h"
#include "method_data_converter.h"
namespace OHOS::Plugin::Bridge {
NAPIAsyncEvent::NAPIAsyncEvent(napi_env env) : env_(env) {}
NAPIAsyncEvent::~NAPIAsyncEvent()
{
DeleteRefData();
DeleteRefErrorData();
DeleteCallback();
DeleteAsyncWork();
eventSuccess_ = nullptr;
eventError_ = nullptr;
}
void NAPIAsyncEvent::SetAsyncEventSuccess(OnAsyncEventSuccess eventSuccess)
{
eventSuccess_ = eventSuccess;
}
void NAPIAsyncEvent::SetAsyncEventError(OnAsyncEventError eventError)
{
eventError_ = eventError;
}
void NAPIAsyncEvent::SetErrorCode(int errorCode)
{
errorCode_ = errorCode;
}
napi_env NAPIAsyncEvent::GetEnv(void)
{
return env_;
}
int NAPIAsyncEvent::GetErrorCode(void)
{
return errorCode_;
}
const std::string& NAPIAsyncEvent::GetAsyncWorkName(void)
{
return asyncWorkName_;
}
void NAPIAsyncEvent::SetBridgeName(const std::string& bridgeName)
{
bridgeName_ = bridgeName;
}
void NAPIAsyncEvent::SetData(napi_value data)
{
data_ = data;
}
napi_value NAPIAsyncEvent::GetData(void)
{
return data_;
}
void NAPIAsyncEvent::DeleteRefData(void)
{
if (refData_ != nullptr) {
PluginUtilsNApi::DeleteReference(env_, refData_);
refData_ = nullptr;
}
}
void NAPIAsyncEvent::SetRefData(napi_value data)
{
DeleteRefData();
refData_ = PluginUtilsNApi::CreateReference(env_, data);
}
napi_value NAPIAsyncEvent::GetRefData(void)
{
return PluginUtilsNApi::GetReference(env_, refData_);
}
void NAPIAsyncEvent::DeleteRefErrorData(void)
{
if (refErrorData_ != nullptr) {
PluginUtilsNApi::DeleteReference(env_, refErrorData_);
refErrorData_ = nullptr;
}
}
void NAPIAsyncEvent::SetRefErrorData(napi_value data)
{
DeleteRefErrorData();
refErrorData_ = PluginUtilsNApi::CreateReference(env_, data);
}
napi_value NAPIAsyncEvent::GetRefErrorData(void)
{
return PluginUtilsNApi::GetReference(env_, refErrorData_);
}
void NAPIAsyncEvent::SetMethodParameter(const std::string& jsonStr)
{
std::lock_guard<std::mutex> lock(queueLock_);
methodParameters_.emplace(jsonStr);
}
void NAPIAsyncEvent::SetMethodParameter(uint8_t* data, size_t size)
{
std::tuple<uint8_t*, size_t> methodBuffer;
std::get<uint8_t*>(methodBuffer) = data;
std::get<size_t>(methodBuffer) = size;
taskQueue_.push_back(methodBuffer);
}
std::tuple<uint8_t*, size_t> NAPIAsyncEvent::GetMethodParameter(void)
{
std::tuple<uint8_t*, size_t> nextTask = taskQueue_.front();
taskQueue_.pop_front();
return nextTask;
}
bool NAPIAsyncEvent::CreateCallback(napi_value callback)
{
if (callback_ != nullptr) {
PluginUtilsNApi::DeleteReference(env_, callback_);
callback_ = nullptr;
}
if (PluginUtilsNApi::GetValueType(env_, callback) != napi_function) {
return false;
}
callback_ = PluginUtilsNApi::CreateReference(env_, callback);
return callback_ != nullptr;
}
void NAPIAsyncEvent::DeleteCallback(void)
{
if (callback_ != nullptr) {
PluginUtilsNApi::DeleteReference(env_, callback_);
callback_ = nullptr;
}
}
bool NAPIAsyncEvent::IsCallback(void)
{
return callback_ != nullptr;
}
bool NAPIAsyncEvent::CreateAsyncWork(const std::string& asyncWorkName,
AsyncWorkExecutor executor, AsyncWorkComplete callback)
{
asyncWorkName_ = asyncWorkName;
napi_value workName = PluginUtilsNApi::CreateStringUtf8(env_, asyncWorkName);
asyncWork_ = PluginUtilsNApi::CreateAsyncWork(env_, workName, executor, callback, this);
return asyncWork_ != nullptr;
}
void NAPIAsyncEvent::DeleteAsyncWork(void)
{
if (asyncWork_ != nullptr) {
PluginUtilsNApi::DeleteAsyncWork(env_, asyncWork_);
asyncWork_ = nullptr;
}
}
napi_value NAPIAsyncEvent::CreatePromise(void)
{
napi_value result = nullptr;
NAPI_CALL(env_, napi_create_promise(env_, &deferred_, &result));
return result;
}
void NAPIAsyncEvent::TriggerEventSuccess(napi_value result)
{
if (eventSuccess_ == nullptr) {
return;
}
eventSuccess_(env_, bridgeName_, asyncWorkName_, result);
}
void NAPIAsyncEvent::TriggerEventError(ErrorCode code)
{
if (eventError_ == nullptr) {
return;
}
eventError_(env_, bridgeName_, asyncWorkName_, static_cast<int>(code));
}
void NAPIAsyncEvent::AsyncWorkCallback(void)
{
ScopedHandleScope scope(env_);
size_t argc = PluginUtilsNApi::ARG_NUM_2;
napi_value argv[PluginUtilsNApi::ARG_NUM_2] = { GetRefErrorData(), GetRefData() };
if (callback_) {
napi_value callback = PluginUtilsNApi::GetReference(env_, callback_);
PluginUtilsNApi::CallFunction(env_, PluginUtilsNApi::CreateUndefined(env_), callback, argc, argv);
} else {
if (deferred_ == nullptr) {
return;
}
if (errorCode_ == 0) {
napi_resolve_deferred(env_, deferred_, argv[PluginUtilsNApi::ARG_NUM_1]);
} else {
napi_reject_deferred(env_, deferred_, argv[PluginUtilsNApi::ARG_NUM_0]);
}
}
}
void NAPIAsyncEvent::AsyncWorkCallMethod(void)
{
ScopedHandleScope scope(env_);
SetErrorCode(0);
if (callback_ == nullptr) {
TriggerEventError(ErrorCode::BRIDGE_METHOD_UNIMPL);
return;
}
napi_value callback = nullptr;
napi_value methodResultValue = nullptr;
napi_get_reference_value(env_, callback_, &callback);
std::string methodParameter = "";
{
std::lock_guard<std::mutex> lock(queueLock_);
methodParameter = methodParameters_.front();
methodParameters_.pop();
}
if (methodParameter.empty()) {
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, 0, nullptr);
TriggerEventSuccess(methodResultValue);
} else {
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
bool ret = NAPIUtils::JsonStringToNapiValues(env_, methodParameter, argc, argv);
if (ret) {
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, argc, argv);
TriggerEventSuccess(methodResultValue);
} else {
TriggerEventError(ErrorCode::BRIDGE_METHOD_PARAM_ERROR);
}
}
}
void NAPIAsyncEvent::AsyncWorkCallMethod(size_t argc, const napi_value* argv)
{
ScopedHandleScope scope(env_);
SetErrorCode(0);
if (callback_ == nullptr) {
TriggerEventError(ErrorCode::BRIDGE_METHOD_UNIMPL);
return;
}
if (argc > 0 && argv == nullptr) {
TriggerEventError(ErrorCode::BRIDGE_METHOD_PARAM_ERROR);
return;
}
napi_value callback = nullptr;
napi_value methodResultValue = nullptr;
napi_get_reference_value(env_, callback_, &callback);
if (argc == 0) {
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, 0, nullptr);
TriggerEventSuccess(methodResultValue);
} else {
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, argc, argv);
TriggerEventSuccess(methodResultValue);
}
}
void NAPIAsyncEvent::AsyncWorkMessage(void)
{
ScopedHandleScope scope(env_);
if (callback_ == nullptr) {
TriggerEventError(ErrorCode::BRIDGE_METHOD_UNIMPL);
return;
}
napi_value callback = nullptr;
napi_value result = nullptr;
napi_get_reference_value(env_, callback_, &callback);
napi_value data = GetRefData();
result = PluginUtilsNApi::CallFunction(env_, PluginUtilsNApi::CreateUndefined(env_), callback, 1, &data);
TriggerEventSuccess(result);
PluginUtilsNApi::DetachArrayBufferFromTypedArray(env_, data);
DeleteRefData();
DeleteRefErrorData();
}
napi_value NAPIAsyncEvent::AsyncWorkCallMethodSync(const std::string& parameter)
{
SetErrorCode(0);
if (callback_ == nullptr) {
LOGE("AsyncWorkCallMethodSync: No callback registered");
return nullptr;
}
napi_value callback = nullptr;
napi_get_reference_value(env_, callback_, &callback);
if (callback == nullptr) {
LOGE("AsyncWorkCallMethodSync: Failed to get callback reference");
return nullptr;
}
napi_value methodResultValue = nullptr;
if (parameter.empty()) {
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, 0, nullptr);
} else {
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
bool ret = NAPIUtils::JsonStringToNapiValues(env_, parameter, argc, argv);
if (!ret) {
LOGE("AsyncWorkCallMethodSync: Failed to parse parameters");
return nullptr;
}
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, argc, argv);
}
LOGD("AsyncWorkCallMethodSync: Successfully called method");
return methodResultValue;
}
napi_value NAPIAsyncEvent::AsyncWorkCallMethodSyncBinary(uint8_t* data, size_t size)
{
LOGD("NAPIAsyncEvent::AsyncWorkCallMethodSyncBinary called, size=%{public}zu", size);
SetErrorCode(0);
if (callback_ == nullptr) {
LOGE("AsyncWorkCallMethodSyncBinary: No callback registered");
return nullptr;
}
napi_value callback = nullptr;
napi_get_reference_value(env_, callback_, &callback);
if (callback == nullptr) {
LOGE("AsyncWorkCallMethodSyncBinary: Failed to get callback reference");
return nullptr;
}
napi_value methodResultValue = nullptr;
if (data == nullptr || size == 0) {
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, 0, nullptr);
return methodResultValue;
}
auto decoded = BridgeBinaryCodec::GetInstance().DecodeBuffer(data, size);
if (!decoded) {
LOGE("AsyncWorkCallMethodSyncBinary: Decode buffer failed");
return nullptr;
}
size_t argc = PluginUtilsNApi::MAX_ARG_NUM;
napi_value argv[PluginUtilsNApi::MAX_ARG_NUM] = { nullptr };
MethodDataConverter::ConvertToNapiValues(env_, *decoded, argc, argv);
methodResultValue = PluginUtilsNApi::CallFunction(
env_, PluginUtilsNApi::CreateUndefined(env_), callback, argc, argv);
return methodResultValue;
}
}