* Copyright (c) 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 <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include "br_proxy.h"
#include "br_proxy_session_manager.h"
#include "comm_log.h"
#include "hilog/log.h"
#include "napi/native_api.h"
#include "securec.h"
#include "softbus_error_code.h"
#include "softbus_napi_utils.h"
#include "trans_log.h"
#define ARGS_SIZE_1 1
#define ARGS_SIZE_2 2
#define ARGS_SIZE_3 3
#define ARGS_INDEX_0 0
#define ARGS_INDEX_1 1
#define ARGS_INDEX_2 2
#define FUNC_NAME_MAX_LEN 22
typedef struct {
napi_env env;
napi_async_work work;
napi_deferred deferred;
BrProxyChannelInfo channelInfo;
int32_t channelId;
int32_t openResult;
int32_t ret;
int32_t sessionId;
} AsyncOpenChannelData;
static void OnDataReceived(int32_t channelId, const char* data, uint32_t dataLen);
static void OnChannelStatusChanged(int32_t channelId, int32_t state);
static int32_t ChannelOpened(int32_t sessionId, int32_t channelId, int32_t result)
{
TRANS_LOGI(TRANS_SDK, "[br_proxy] sessionId:%{public}d.", sessionId);
int32_t ret = UpdateListBySessionId(sessionId, channelId, result);
if (ret != SOFTBUS_OK) {
TRANS_LOGI(TRANS_SDK, "[br_proxy] ret:%{public}d.", ret);
return ret;
}
return BrProxyPostCond(sessionId);
}
static void OpenProxyChannelExecute(napi_env env, void* data)
{
BrProxyChannelInfo channelInfo;
AsyncOpenChannelData* asyncData = (AsyncOpenChannelData*)data;
if (memcpy_s(channelInfo.peerBRMacAddr, sizeof(channelInfo.peerBRMacAddr),
asyncData->channelInfo.peerBRMacAddr, sizeof(asyncData->channelInfo.peerBRMacAddr)) != EOK ||
memcpy_s(channelInfo.peerBRUuid, sizeof(channelInfo.peerBRUuid),
asyncData->channelInfo.peerBRUuid, sizeof(asyncData->channelInfo.peerBRUuid)) != EOK) {
asyncData->ret = SOFTBUS_MEM_ERR;
return;
}
channelInfo.recvPri = asyncData->channelInfo.recvPri;
IBrProxyListener listener = {
.onChannelOpened = ChannelOpened,
.onDataReceived = OnDataReceived,
.onChannelStatusChanged = OnChannelStatusChanged,
};
int32_t ret = AddSessionToList(asyncData->sessionId);
if (ret != SOFTBUS_OK) {
asyncData->ret = ret;
return;
}
ret = OpenBrProxy(asyncData->sessionId, &channelInfo, &listener);
asyncData->ret = ret;
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] openBrProxy failed! ret:%{public}d.", ret);
return;
}
ret = BrProxyWaitCond(asyncData->sessionId);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] BrProxyWaitCond failed! ret:%{public}d.", ret);
asyncData->ret = ret;
return;
}
SessionInfo info;
ret = GetSessionInfoBySessionId(asyncData->sessionId, &info);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] GetSessionInfoBySessionId failed! ret:%{public}d.", ret);
asyncData->ret = ret;
return;
}
asyncData->channelId = info.channelId;
asyncData->openResult = info.openResult;
}
static void OpenProxyChannelComplete(napi_env env, napi_status status, void* data)
{
AsyncOpenChannelData* asyncData = (AsyncOpenChannelData*)data;
napi_status napiStatus;
napi_value channelIdValue;
int32_t ret = asyncData->ret;
int32_t openResult = asyncData->openResult;
int32_t sessionId = asyncData->sessionId;
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] open channel failed, ret=%{public}d.", ret);
napi_reject_deferred(env, asyncData->deferred, GetBusinessError(env, ret));
goto exit;
}
if (openResult != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] openResult=%{public}d.", openResult);
napi_reject_deferred(env, asyncData->deferred, GetBusinessError(env, openResult));
goto exit;
}
napiStatus = napi_create_int32(env, asyncData->channelId, &channelIdValue);
if (napiStatus != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create channelId failed.");
goto cleanup;
}
napiStatus = napi_resolve_deferred(env, asyncData->deferred, channelIdValue);
cleanup:
if (napiStatus != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] resolve promise failed.");
napi_reject_deferred(env, asyncData->deferred, NULL);
}
exit:
DeleteSessionById(sessionId);
napi_delete_async_work(env, asyncData->work);
SoftBusFree(asyncData);
}
static int32_t GetChannelInfoParam(napi_env env, napi_value arg, AsyncOpenChannelData *asyncData)
{
napi_status status;
napi_value peerBRMacAddrValue;
napi_value peerBRUuidValue;
size_t strLen;
size_t macLen;
size_t uuidLen;
napi_value linkTypeValue;
status = napi_get_named_property(env, arg, "linkType", &linkTypeValue);
if (status != napi_ok) {
goto EXIT;
}
status = napi_get_value_int32(env, linkTypeValue, (int32_t *)&asyncData->channelInfo.linktype);
if (status != napi_ok) {
goto EXIT;
}
if (napi_get_named_property(env, arg, "peerDevAddr", &peerBRMacAddrValue) != napi_ok ||
napi_get_named_property(env, arg, "peerUuid", &peerBRUuidValue) != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get peerDevAddr/peerUuid failed.");
goto EXIT;
}
if (napi_get_value_string_utf8(env, peerBRMacAddrValue, NULL, 0, &macLen) != napi_ok ||
napi_get_value_string_utf8(env, peerBRUuidValue, NULL, 0, &uuidLen) != napi_ok) {
goto EXIT;
}
if (macLen < MAC_MIN_LENGTH || (macLen > MAC_MAX_LENGTH && macLen != MAC_SHA256_LEN)) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] mac len is wrong, macLen:%{public}zu", macLen);
goto EXIT;
}
if (uuidLen != UUID_STD_LENGTH && uuidLen != UUID_NO_HYPHEN_LENGTH) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] uuid len is wrong, uuidLen:%{public}zu", uuidLen);
goto EXIT;
}
if (napi_get_value_string_utf8(env, peerBRMacAddrValue, asyncData->channelInfo.peerBRMacAddr,
sizeof(asyncData->channelInfo.peerBRMacAddr), &strLen) != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get peerDevAddr failed.");
goto EXIT;
}
if (napi_get_value_string_utf8(env, peerBRUuidValue, asyncData->channelInfo.peerBRUuid,
sizeof(asyncData->channelInfo.peerBRUuid), &strLen) != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get peerUuid failed.");
goto EXIT;
}
return SOFTBUS_OK;
EXIT:
TRANS_LOGE(TRANS_SDK, "[br_proxy] get channel info param failed.");
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
static int32_t StartWork(napi_env env, AsyncOpenChannelData *asyncData)
{
napi_value resourceName;
napi_status status = napi_create_string_utf8(env, "OpenProxyChannelAsyncWork", NAPI_AUTO_LENGTH, &resourceName);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create string failed.");
napi_reject_deferred(env, asyncData->deferred, NULL);
return SOFTBUS_NO_INIT;
}
status = napi_create_async_work(env, NULL, resourceName, OpenProxyChannelExecute, OpenProxyChannelComplete,
asyncData, &asyncData->work);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create async work failed.");
napi_reject_deferred(env, asyncData->deferred, NULL);
return SOFTBUS_NO_INIT;
}
status = napi_queue_async_work(env, asyncData->work);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] queue async work failed.");
napi_reject_deferred(env, asyncData->deferred, NULL);
napi_delete_async_work(env, asyncData->work);
return SOFTBUS_NO_INIT;
}
return SOFTBUS_OK;
}
napi_value NapiOpenProxyChannel(napi_env env, napi_callback_info info)
{
TRANS_LOGI(TRANS_SDK, "[br_proxy] enter.");
size_t argc = ARGS_SIZE_1;
napi_value args[ARGS_SIZE_1];
napi_value thisArg;
void* data;
napi_status status = napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
if (status != napi_ok || argc < ARGS_SIZE_1) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
napi_valuetype valuetype;
status = napi_typeof(env, args[0], &valuetype);
if (status != napi_ok || valuetype != napi_object) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
AsyncOpenChannelData* asyncData = (AsyncOpenChannelData*)SoftBusCalloc(sizeof(AsyncOpenChannelData));
if (asyncData == NULL) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INTERNAL_ERROR);
return NULL;
}
asyncData->env = env;
if (GetChannelInfoParam(env, args[0], asyncData) != SOFTBUS_OK || SessionInit() != SOFTBUS_OK) {
goto EXIT;
}
int32_t sessionId = GetSessionId();
if (sessionId <= 0) {
goto EXIT;
}
asyncData->sessionId = sessionId;
napi_value promise;
status = napi_create_promise(env, &asyncData->deferred, &promise);
if (status != napi_ok) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INTERNAL_ERROR);
goto EXIT;
}
int32_t ret = StartWork(env, asyncData);
if (ret != SOFTBUS_OK) {
goto EXIT;
}
TRANS_LOGI(TRANS_SDK, "[br_proxy] open success, sessionId=%{public}d.", sessionId);
return promise;
EXIT:
TRANS_LOGE(TRANS_SDK, "[br_proxy] open proxy failed");
SoftBusFree(asyncData);
return NULL;
}
napi_value ChannelStateEnumInit(napi_env env, napi_value exports)
{
napi_status status;
napi_value typeEnum;
status = napi_create_object(env, &typeEnum);
if (status != napi_ok) {
return NULL;
}
napi_value typeValue;
if (napi_create_int32(env, CHANNEL_WAIT_RESUME, &typeValue) != napi_ok ||
napi_set_named_property(env, typeEnum, "CHANNEL_WAIT_RESUME", typeValue)) {
return NULL;
}
if (napi_create_int32(env, CHANNEL_RESUME, &typeValue) != napi_ok ||
napi_set_named_property(env, typeEnum, "CHANNEL_RESUME", typeValue) != napi_ok) {
return NULL;
}
if (napi_create_int32(env, CHANNEL_EXCEPTION_SOFTWARE_FAILED, &typeValue) != napi_ok ||
napi_set_named_property(env, typeEnum, "CHANNEL_EXCEPTION_SOFTWARE_FAILED", typeValue) != napi_ok) {
return NULL;
}
if (napi_create_int32(env, CHANNEL_BR_NO_PAIRED, &typeValue) != napi_ok ||
napi_set_named_property(env, typeEnum, "CHANNEL_BR_NO_PAIRED", typeValue) != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "ChannelState", typeEnum);
if (status != napi_ok) {
return NULL;
}
return exports;
}
napi_value NapiCloseProxyChannel(napi_env env, napi_callback_info info)
{
TRANS_LOGI(TRANS_SDK, "[br_proxy] enter.");
size_t argc = ARGS_SIZE_1;
napi_value args[ARGS_SIZE_1];
napi_value thisArg;
void* data;
napi_status status = napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
if (status != napi_ok || argc < ARGS_SIZE_1) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
napi_valuetype valuetype;
status = napi_typeof(env, args[0], &valuetype);
if (status != napi_ok || valuetype != napi_number) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
int32_t channelId;
status = napi_get_value_int32(env, args[0], &channelId);
if (status != napi_ok) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
double value;
status = napi_get_value_double(env, args[0], &value);
if (status != napi_ok) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
int32_t intValue = (int32_t)value;
bool isInteger = (double)intValue == value;
if (!isInteger) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
int32_t ret = CloseBrProxy(channelId);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] close failed, ret=%{public}d.", ret);
ThrowErrFromC2Js(env, ret);
return NULL;
}
napi_value undefined;
status = napi_get_undefined(env, &undefined);
if (status != napi_ok) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INTERNAL_ERROR);
return NULL;
}
TRANS_LOGI(TRANS_SDK, "[br_proxy] close success, channelId=%{public}d.", channelId);
return undefined;
}
typedef struct {
napi_env env;
napi_async_work work;
napi_deferred deferred;
int32_t channelId;
char* data;
size_t dataLength;
int32_t ret;
} AsyncSendData;
typedef struct QueueNode {
AsyncSendData* data;
struct QueueNode* next;
} QueueNode;
typedef struct {
QueueNode* head;
QueueNode* tail;
pthread_mutex_t mutex;
bool isProcessing;
} TaskQueue;
static TaskQueue g_taskQueue = {
.head = NULL,
.tail = NULL,
.isProcessing = false
};
void InitTaskQueue()
{
pthread_mutex_init(&g_taskQueue.mutex, NULL);
g_taskQueue.head = g_taskQueue.tail = NULL;
g_taskQueue.isProcessing = false;
}
static void AsyncWorkComplete(napi_env env, napi_status status, void* data);
static void AsyncWorkExecute(napi_env env, void* data);
static void ProcessTaskQueue()
{
bool shouldExit = false;
while (!shouldExit) {
pthread_mutex_lock(&g_taskQueue.mutex);
if (g_taskQueue.head == NULL || g_taskQueue.isProcessing) {
shouldExit = true;
pthread_mutex_unlock(&g_taskQueue.mutex);
return;
}
g_taskQueue.isProcessing = true;
QueueNode* node = g_taskQueue.head;
g_taskQueue.head = g_taskQueue.head->next;
if (g_taskQueue.head == NULL) {
shouldExit = true;
g_taskQueue.tail = NULL;
}
pthread_mutex_unlock(&g_taskQueue.mutex);
AsyncSendData* asyncData = node->data;
SoftBusFree(node);
napi_env env = asyncData->env;
napi_value resourceName;
napi_status status = napi_create_string_utf8(env, "SendDataAsyncWork", NAPI_AUTO_LENGTH, &resourceName);
if (status != napi_ok) {
napi_reject_deferred(env, asyncData->deferred, NULL);
goto cleanup;
}
status = napi_create_async_work(env, NULL, resourceName, AsyncWorkExecute, AsyncWorkComplete,
asyncData, &asyncData->work);
if (status != napi_ok) {
napi_reject_deferred(env, asyncData->deferred, NULL);
goto cleanup;
}
status = napi_queue_async_work(env, asyncData->work);
if (status != napi_ok) {
napi_reject_deferred(env, asyncData->deferred, NULL);
napi_delete_async_work(env, asyncData->work);
goto cleanup;
}
continue;
cleanup:
TRANS_LOGE(TRANS_SDK, "[br_proxy] process task failed.");
SoftBusFree(asyncData->data);
SoftBusFree(asyncData);
pthread_mutex_lock(&g_taskQueue.mutex);
g_taskQueue.isProcessing = false;
pthread_mutex_unlock(&g_taskQueue.mutex);
}
}
static void AsyncWorkExecute(napi_env env, void* data)
{
AsyncSendData* asyncData = (AsyncSendData*)data;
int32_t ret = SendBrProxyData(asyncData->channelId, asyncData->data, asyncData->dataLength);
asyncData->ret = ret;
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] send data failed, ret=%{public}d.", ret);
}
}
static void AsyncWorkComplete(napi_env env, napi_status status, void* data)
{
AsyncSendData* asyncData = (AsyncSendData*)data;
napi_status napiStatus;
if (asyncData->ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] send data failed, ret=%{public}d.", asyncData->ret);
napi_reject_deferred(env, asyncData->deferred, GetBusinessError(env, asyncData->ret));
goto cleanup;
}
napi_value undefined;
napiStatus = napi_get_undefined(env, &undefined);
if (napiStatus != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get undefined failed.");
napi_reject_deferred(env, asyncData->deferred, NULL);
goto cleanup;
}
napiStatus = napi_resolve_deferred(env, asyncData->deferred, undefined);
if (napiStatus != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] resolve promise failed.");
napi_reject_deferred(env, asyncData->deferred, NULL);
}
cleanup:
SoftBusFree(asyncData->data);
napi_delete_async_work(env, asyncData->work);
SoftBusFree(asyncData);
pthread_mutex_lock(&g_taskQueue.mutex);
g_taskQueue.isProcessing = false;
pthread_mutex_unlock(&g_taskQueue.mutex);
ProcessTaskQueue();
}
static bool ChanneIdIsInt(napi_env env, napi_value *args)
{
double value;
napi_status status = napi_get_value_double(env, args[0], &value);
if (status != napi_ok) {
return false;
}
int32_t intValue = (int32_t)value;
bool isInteger = (double)intValue == value;
return isInteger;
}
static int32_t GetSendParam(napi_env env, napi_callback_info info, AsyncSendData *asyncData)
{
size_t argc = ARGS_SIZE_2;
napi_value args[ARGS_SIZE_2];
napi_value thisArg;
void* data;
napi_status status = napi_get_cb_info(env, info, &argc, args, &thisArg, &data);
if (status != napi_ok || argc < ARGS_SIZE_2) {
goto EXIT;
}
napi_valuetype valueTypeNum;
status = napi_typeof(env, args[0], &valueTypeNum);
if (status != napi_ok || valueTypeNum != napi_number) {
goto EXIT;
}
status = napi_get_value_int32(env, args[0], &asyncData->channelId);
if (status != napi_ok) {
goto EXIT;
}
if (!ChanneIdIsInt(env, args)) {
goto EXIT;
}
napi_valuetype valueTypeBuffer;
status = napi_typeof(env, args[1], &valueTypeBuffer);
if (status != napi_ok) {
goto EXIT;
}
void* bufferData;
status = napi_get_arraybuffer_info(env, args[1], &bufferData, &asyncData->dataLength);
if (status != napi_ok || asyncData->dataLength == 0) {
goto EXIT;
}
asyncData->data = (char*)SoftBusCalloc(asyncData->dataLength);
if (asyncData->data == NULL) {
ThrowErrFromC2Js(env, SOFTBUS_MEM_ERR);
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
if (memcpy_s(asyncData->data, asyncData->dataLength, bufferData, asyncData->dataLength) != EOK) {
SoftBusFree(asyncData->data);
ThrowErrFromC2Js(env, SOFTBUS_MEM_ERR);
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
return SOFTBUS_OK;
EXIT:
TRANS_LOGE(TRANS_SDK, "[br_proxy] get send param failed.");
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
napi_value SendDataAsync(napi_env env, napi_callback_info info)
{
napi_value promise;
AsyncSendData* asyncData = (AsyncSendData*)SoftBusCalloc(sizeof(AsyncSendData));
if (asyncData == NULL) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INTERNAL_ERROR);
return NULL;
}
asyncData->env = env;
int32_t ret = GetSendParam(env, info, asyncData);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get param failed.");
SoftBusFree(asyncData);
return NULL;
}
napi_status status = napi_create_promise(env, &asyncData->deferred, &promise);
if (status != napi_ok) {
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INTERNAL_ERROR);
goto cleanup;
}
TRANS_LOGI(TRANS_SDK, "[br_proxy] channelId=%{public}d.", asyncData->channelId);
pthread_mutex_lock(&g_taskQueue.mutex);
QueueNode* node = (QueueNode*)SoftBusCalloc(sizeof(QueueNode));
if (node == NULL) {
pthread_mutex_unlock(&g_taskQueue.mutex);
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INTERNAL_ERROR);
goto cleanup;
}
node->data = asyncData;
node->next = NULL;
if (g_taskQueue.tail == NULL) {
g_taskQueue.head = g_taskQueue.tail = node;
} else {
g_taskQueue.tail->next = node;
g_taskQueue.tail = node;
}
pthread_mutex_unlock(&g_taskQueue.mutex);
ProcessTaskQueue();
return promise;
cleanup:
TRANS_LOGE(TRANS_SDK, "[br_proxy] send data failed.");
SoftBusFree(asyncData->data);
SoftBusFree(asyncData);
return NULL;
}
static napi_threadsafe_function tsfn_data_received = NULL;
static napi_threadsafe_function tsfn_channel_status = NULL;
typedef struct {
int32_t channelId;
char* data;
uint32_t dataLen;
} DataReceiveArgs;
typedef struct {
int32_t channelId;
int32_t status;
} ChannelStatusArgs;
static void DataReceivedCallback(napi_env env, napi_value callback, void *context, void *data)
{
DataReceiveArgs* args = (DataReceiveArgs*)data;
napi_value dataInfo;
napi_status status = napi_create_object(env, &dataInfo);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create object failed.");
goto cleanup;
}
napi_value channelIdValue;
status = napi_create_int32(env, args->channelId, &channelIdValue);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create channelId failed.");
goto cleanup;
}
status = napi_set_named_property(env, dataInfo, "channelId", channelIdValue);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] set channelId failed.");
goto cleanup;
}
napi_value arrayBuffer;
void *dataBuffer;
status = napi_create_arraybuffer(env, args->dataLen, &dataBuffer, &arrayBuffer);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create arraybuffer failed.");
goto cleanup;
}
if (memcpy_s(dataBuffer, args->dataLen, args->data, args->dataLen) != EOK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] memcpy data failed.");
goto cleanup;
}
status = napi_set_named_property(env, dataInfo, "data", arrayBuffer);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] set data property failed.");
goto cleanup;
}
napi_value result;
napi_value args_array[1] = {dataInfo};
status = napi_call_function(env, NULL, callback, 1, args_array, &result);
cleanup:
SoftBusFree(args->data);
SoftBusFree(args);
}
static void ChannelStatusCallback(napi_env env, napi_value callback, void *context, void *data)
{
ChannelStatusArgs* args = (ChannelStatusArgs*)data;
napi_value statusInfo;
napi_status status = napi_create_object(env, &statusInfo);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create object failed.");
goto cleanup;
}
napi_value channelIdValue;
status = napi_create_int32(env, args->channelId, &channelIdValue);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create channelId failed.");
goto cleanup;
}
status = napi_set_named_property(env, statusInfo, "channelId", channelIdValue);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] set channelId failed.");
goto cleanup;
}
napi_value statusValue;
status = napi_create_int32(env, args->status, &statusValue);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] create status failed.");
goto cleanup;
}
status = napi_set_named_property(env, statusInfo, "state", statusValue);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] set state failed.");
goto cleanup;
}
napi_value result;
napi_value args_array[1] = {statusInfo};
status = napi_call_function(env, NULL, callback, 1, args_array, &result);
cleanup:
SoftBusFree(args);
}
static void OnDataReceived(int32_t channelId, const char* data, uint32_t dataLen)
{
if (data == NULL || dataLen == 0 || dataLen > BR_PROXY_SEND_MAX_LEN) {
TRANS_LOGE(TRANS_SVC, "[br_proxy] data is null or invalid dataLen=%{public}u", dataLen);
return;
}
if (tsfn_data_received == NULL) {
TRANS_LOGE(TRANS_SVC, "[br_proxy] tsfn_data_received is null.");
return;
}
DataReceiveArgs* args = (DataReceiveArgs*)SoftBusCalloc(sizeof(DataReceiveArgs));
if (args == NULL) {
TRANS_LOGE(TRANS_SVC, "[br_proxy] alloc args failed.");
return;
}
args->channelId = channelId;
args->dataLen = dataLen;
args->data = (char *)SoftBusCalloc(dataLen);
if (args->data == NULL) {
TRANS_LOGE(TRANS_SVC, "[br_proxy] alloc data failed.");
SoftBusFree(args);
return;
}
if (memcpy_s(args->data, dataLen, data, dataLen) != EOK) {
TRANS_LOGE(TRANS_SVC, "[br_proxy] memcpy failed.");
SoftBusFree(args->data);
SoftBusFree(args);
return;
}
napi_status status = napi_call_threadsafe_function(tsfn_data_received, args, napi_tsfn_nonblocking);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SVC, "[br_proxy] call threadsafe failed.");
SoftBusFree(args->data);
SoftBusFree(args);
}
}
static void OnChannelStatusChanged(int32_t channelId, int32_t status)
{
if (tsfn_channel_status == NULL) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] tsfn_channel_status is null.");
return;
}
ChannelStatusArgs* args = (ChannelStatusArgs*)SoftBusCalloc(sizeof(ChannelStatusArgs));
if (args == NULL) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] alloc args failed.");
return;
}
args->channelId = channelId;
args->status = status;
napi_status ret = napi_call_threadsafe_function(tsfn_channel_status, args, napi_tsfn_nonblocking);
if (ret != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] call threadsafe function failed.");
SoftBusFree(args);
}
}
static void SetCallbackInternal(napi_env env, napi_value callback, int32_t channelId, ListenerType type)
{
int32_t ret;
switch (type) {
case DATA_RECEIVE:
if (tsfn_data_received != NULL) {
napi_release_threadsafe_function(tsfn_data_received, napi_tsfn_abort);
tsfn_data_received = NULL;
}
napi_value dataReceived;
napi_create_string_utf8(env, "DataReceived", NAPI_AUTO_LENGTH, &dataReceived);
napi_create_threadsafe_function(
env, callback, NULL, dataReceived,
0, 1, NULL, NULL, NULL,
DataReceivedCallback, &tsfn_data_received);
ret = SetListenerState(channelId, DATA_RECEIVE, true);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] set receive listener failed, ret=%{public}d.", ret);
napi_release_threadsafe_function(tsfn_data_received, napi_tsfn_abort);
tsfn_data_received = NULL;
}
ThrowErrFromC2Js(env, ret);
break;
case CHANNEL_STATE:
if (tsfn_channel_status != NULL) {
napi_release_threadsafe_function(tsfn_channel_status, napi_tsfn_abort);
tsfn_channel_status = NULL;
}
napi_value channelStatus;
napi_create_string_utf8(env, "ChannelStatus", NAPI_AUTO_LENGTH, &channelStatus);
napi_create_threadsafe_function(
env, callback, NULL, channelStatus,
0, 1, NULL, NULL, NULL,
ChannelStatusCallback, &tsfn_channel_status);
ret = SetListenerState(channelId, CHANNEL_STATE, true);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] set state listener failed, ret=%{public}d.", ret);
napi_release_threadsafe_function(tsfn_channel_status, napi_tsfn_abort);
tsfn_channel_status = NULL;
}
ThrowErrFromC2Js(env, ret);
break;
default:
break;
}
}
napi_value On(napi_env env, napi_callback_info info)
{
TRANS_LOGI(TRANS_SDK, "[br_proxy] enter.");
size_t argc = ARGS_SIZE_3;
napi_value args[ARGS_SIZE_3];
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
if (status != napi_ok || argc != ARGS_SIZE_3) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] invalid args.");
goto EXIT;
}
char type[FUNC_NAME_MAX_LEN];
size_t typeLen;
status = napi_get_value_string_utf8(env, args[ARGS_INDEX_0], type, sizeof(type), &typeLen);
if (status != napi_ok) {
goto EXIT;
}
int32_t channelId;
status = napi_get_value_int32(env, args[ARGS_INDEX_1], &channelId);
if (status != napi_ok) {
goto EXIT;
}
double value;
status = napi_get_value_double(env, args[ARGS_INDEX_1], &value);
if (status != napi_ok) {
goto EXIT;
}
int32_t intValue = (int32_t)value;
bool isInteger = (double)intValue == value;
if (!isInteger) {
goto EXIT;
}
napi_valuetype funcType;
status = napi_typeof(env, args[ARGS_INDEX_2], &funcType);
if (status != napi_ok || funcType != napi_function) {
goto EXIT;
}
if (strcmp(type, "receiveData") == 0) {
SetCallbackInternal(env, args[ARGS_INDEX_2], channelId, DATA_RECEIVE);
} else if (strcmp(type, "channelStateChange") == 0) {
SetCallbackInternal(env, args[ARGS_INDEX_2], channelId, CHANNEL_STATE);
} else {
TRANS_LOGE(TRANS_SDK, "[br_proxy] unknown type=%{public}s.", type);
goto EXIT;
}
TRANS_LOGI(TRANS_SDK, "[br_proxy] on success, type=%{public}s, channelId=%{public}d.", type, channelId);
return NULL;
EXIT:
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
static int32_t GetOffParam(napi_env env, napi_value *args, size_t argc, int32_t *channelId)
{
if (channelId == NULL) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] channelId is null.");
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
napi_status status = napi_get_value_int32(env, args[ARGS_INDEX_1], channelId);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get channelId failed.");
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
double value;
status = napi_get_value_double(env, args[ARGS_INDEX_1], &value);
if (status != napi_ok) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get double failed.");
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
int32_t intValue = (int32_t)value;
bool isInteger = (double)intValue == value;
if (!isInteger) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] not integer.");
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
if (argc != ARGS_SIZE_3) {
return SOFTBUS_OK;
}
napi_valuetype funcType;
status = napi_typeof(env, args[ARGS_INDEX_2], &funcType);
if (status != napi_ok || funcType != napi_function) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] callback not function.");
return SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM;
}
return SOFTBUS_OK;
}
napi_value Off(napi_env env, napi_callback_info info)
{
TRANS_LOGI(TRANS_SDK, "[br_proxy] enter.");
size_t argc = ARGS_SIZE_3;
napi_value args[ARGS_SIZE_3];
napi_status status = napi_get_cb_info(env, info, &argc, args, NULL, NULL);
if (status != napi_ok || argc < ARGS_SIZE_2) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] invalid args.");
goto EXIT;
}
char type[FUNC_NAME_MAX_LEN];
size_t typeLen;
status = napi_get_value_string_utf8(env, args[ARGS_INDEX_0], type, sizeof(type), &typeLen);
if (status != napi_ok) {
goto EXIT;
}
int32_t channelId = 0;
int32_t ret = GetOffParam(env, args, argc, &channelId);
if (ret != SOFTBUS_OK) {
TRANS_LOGE(TRANS_SDK, "[br_proxy] get off param failed.");
goto EXIT;
}
if (strcmp(type, "receiveData") == 0) {
if (tsfn_data_received != NULL) {
napi_release_threadsafe_function(tsfn_data_received, napi_tsfn_abort);
tsfn_data_received = NULL;
}
ret = SetListenerState(channelId, DATA_RECEIVE, false);
ThrowErrFromC2Js(env, ret);
} else if (strcmp(type, "channelStateChange") == 0) {
if (tsfn_channel_status != NULL) {
napi_release_threadsafe_function(tsfn_channel_status, napi_tsfn_abort);
tsfn_channel_status = NULL;
}
ret = SetListenerState(channelId, CHANNEL_STATE, false);
ThrowErrFromC2Js(env, ret);
} else {
TRANS_LOGE(TRANS_SDK, "[br_proxy] unknown type=%{public}s.", type);
goto EXIT;
}
TRANS_LOGI(TRANS_SDK, "[br_proxy] off success, type=%{public}s, channelId=%{public}d.", type, channelId);
return NULL;
EXIT:
ThrowErrFromC2Js(env, SOFTBUS_TRANS_BR_PROXY_INVALID_PARAM);
return NULL;
}
napi_value LinkTypeEnumInit(napi_env env, napi_value exports)
{
napi_status status;
napi_value typeEnum;
status = napi_create_object(env, &typeEnum);
if (status != napi_ok) {
return NULL;
}
napi_value typeValue;
status = napi_create_int32(env, LINK_BR, &typeValue);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, typeEnum, "LINK_BR", typeValue);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "LinkType", typeEnum);
if (status != napi_ok) {
return NULL;
}
return exports;
}
static napi_value NapiSoftbusTransInit(napi_env env, napi_value exports)
{
napi_status status;
napi_value fn;
status = napi_create_function(env, NULL, NAPI_AUTO_LENGTH, NapiOpenProxyChannel, NULL, &fn);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "openProxyChannel", fn);
if (status != napi_ok) {
return NULL;
}
status = napi_create_function(env, NULL, NAPI_AUTO_LENGTH, NapiCloseProxyChannel, NULL, &fn);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "closeProxyChannel", fn);
if (status != napi_ok) {
return NULL;
}
status = napi_create_function(env, NULL, NAPI_AUTO_LENGTH, SendDataAsync, NULL, &fn);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "sendData", fn);
if (status != napi_ok) {
return NULL;
}
if (ChannelStateEnumInit(env, exports) == NULL || LinkTypeEnumInit(env, exports) == NULL) {
return NULL;
}
status = napi_create_function(env, NULL, NAPI_AUTO_LENGTH, On, NULL, &fn);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "on", fn);
if (status != napi_ok) {
return NULL;
}
status = napi_create_function(env, NULL, NAPI_AUTO_LENGTH, Off, NULL, &fn);
if (status != napi_ok) {
return NULL;
}
status = napi_set_named_property(env, exports, "off", fn);
if (status != napi_ok) {
return NULL;
}
return exports;
}
* Module definition
*/
static napi_module g_module = {
.nm_version = 1,
.nm_flags = 0,
.nm_filename = "distributedsched.proxyChannelManager",
.nm_register_func = NapiSoftbusTransInit,
.nm_modname = "distributedsched.proxyChannelManager",
.nm_priv = ((void *)0),
.reserved = { 0 }
};
* Module registration
*/
__attribute__((constructor)) void RegisterSoftbusTransModule(void)
{
InitTaskQueue();
napi_module_register(&g_module);
}
__attribute__((destructor)) void DestructSoftbusTransModule(void)
{
if (tsfn_data_received != NULL) {
napi_release_threadsafe_function(tsfn_data_received, napi_tsfn_abort);
tsfn_data_received = NULL;
}
if (tsfn_channel_status != NULL) {
napi_release_threadsafe_function(tsfn_channel_status, napi_tsfn_abort);
tsfn_channel_status = NULL;
}
}