/*
 * 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 "sdp_connect.h"

#include "sdp_client_parse.h"
#include "sdp_server.h"
#include "sdp_util.h"

#include "l2cap_if.h"

#include "bt_endian.h"

#include "alarm.h"
#include "list.h"
#include "packet.h"

#include "../btm/btm_thread.h"

#include "allocator.h"
#include "log.h"

typedef struct {
    BtAddr addr;
    uint16_t lcid;
    uint16_t mtu;
    uint8_t outConnState;
    uint8_t inConnState;
    Alarm *timer;
    bool flag;       /// 0-server 1-client
    Packet *packet;  /// Fragment packet
    uint16_t totalCount;
    bool wait;
} SdpConnectInfo;

static void SdpSendConnectRequest(const BtAddr *addr);
static void SdpSendDisconnectRequest(uint16_t lcid, bool wait);
static void SdpReceiveConnectRequest(
    uint16_t lcid, uint8_t id, const L2capConnectionInfo *info, uint16_t lpsm, void *context);
static void SdpReceiveConnectResponse(
    uint16_t lcid, const L2capConnectionInfo *info, uint16_t result, uint16_t status, void *context);
static void SdpReceiveConfigRequest(uint16_t lcid, uint8_t id, const L2capConfigInfo *config, void *context);
static void SdpReceiveConfigResponse(uint16_t lcid, const L2capConfigInfo *config, uint16_t result, void *context);
static void SdpReceiveDisconnectRequest(uint16_t lcid, uint8_t id, void *context);
static void SdpReceiveDisconnectResponse(uint16_t lcid, void *context);
static void SdpDisconnectAbnormal(uint16_t lcid, uint8_t reason, void *context);
static void SdpReceiveData(uint16_t lcid, Packet *packet, void *context);

/// Connect List for server and client
static List *g_connectList = NULL;
static L2capService g_registerInfo;

void SdpRegisterToL2cap()
{
    /// Register with L2CAP
    (void)memset_s(&g_registerInfo, sizeof(L2capService), 0, sizeof(L2capService));
    g_registerInfo.recvConnectionReq = SdpReceiveConnectRequest;
    g_registerInfo.recvConnectionRsp = SdpReceiveConnectResponse;
    g_registerInfo.recvConfigReq = SdpReceiveConfigRequest;
    g_registerInfo.recvConfigRsp = SdpReceiveConfigResponse;
    g_registerInfo.recvDisconnectionReq = SdpReceiveDisconnectRequest;
    g_registerInfo.recvDisconnectionRsp = SdpReceiveDisconnectResponse;
    g_registerInfo.disconnectAbnormal = SdpDisconnectAbnormal;
    g_registerInfo.recvData = SdpReceiveData;
    g_registerInfo.remoteBusy = NULL;

    L2CIF_RegisterService(SDP_PSM, &g_registerInfo, NULL, NULL);
}

void SdpDeregisterToL2cap()
{
    /// Deregister with L2CAP
    L2CIF_DeregisterService(SDP_PSM, NULL);
}

static void SdpFreeConnectInfo(const void *data)
{
    if (data == NULL) {
        return;
    }
    SdpConnectInfo *connect = (SdpConnectInfo *)data;
    if (connect->timer != NULL) {
        AlarmDelete(connect->timer);
        connect->timer = NULL;
    }
    if (connect->packet != NULL) {
        PacketFree(connect->packet);
        connect->packet = NULL;
    }
    MEM_MALLOC.free(connect);
    connect = NULL;
}

void SdpCreateConnectList()
{
    g_connectList = ListCreate((void (*)(void *))SdpFreeConnectInfo);
}

void SdpDestroyConnectList()
{
    if (g_connectList != NULL) {
        ListDelete(g_connectList);
        g_connectList = NULL;
    }
}

static void SdpConnectWaitTimeTask(void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *ctx = context;

    if (!SdpGetEnableState()) {
        return;
    }

    SdpSendDisconnectRequest(ctx->lcid, true);
    MEM_MALLOC.free(ctx);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpConnectWaitTime(void *parameter)
{
    int ret;
    SdpConnectInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }

    (void)memset_s(ctx, sizeof(SdpConnectInfo), 0x00, sizeof(SdpConnectInfo));
    (void)memcpy_s(ctx, sizeof(SdpConnectInfo), parameter, sizeof(SdpConnectInfo));

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpConnectWaitTimeTask, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

static void SdpConnectTimeoutTask(void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *ctx = context;

    if (!SdpGetEnableState()) {
        return;
    }

    if (ctx->flag) {
        SdpRemoveAllRequestByAddress(&ctx->addr);
    }
    ListRemoveNode(g_connectList, ctx);
    MEM_MALLOC.free(ctx);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpConnectTimeout(void *parameter)
{
    int ret;
    SdpConnectInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpConnectInfo), 0x00, sizeof(SdpConnectInfo));
    (void)memcpy_s(ctx, sizeof(SdpConnectInfo), parameter, sizeof(SdpConnectInfo));

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpConnectTimeoutTask, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

static SdpConnectInfo *SdpFindConnectByCid(uint16_t lcid)
{
    ListNode *node = NULL;
    SdpConnectInfo *connect = NULL;

    if (g_connectList == NULL) {
        return NULL;
    }
    node = ListGetFirstNode(g_connectList);
    while (node != NULL) {
        connect = (SdpConnectInfo *)ListGetNodeData(node);
        /// Find connect by local channel ID as a server or a client.
        if ((connect->lcid == lcid) && (connect->outConnState != SDP_STATE_IDLE)) {
            return connect;
        }
        node = ListGetNextNode(node);
    }
    return NULL;
}

static SdpConnectInfo *SdpFindConnectByAddress(const BtAddr *addr)
{
    ListNode *node = NULL;
    SdpConnectInfo *connect = NULL;

    if (g_connectList == NULL) {
        return NULL;
    }
    node = ListGetFirstNode(g_connectList);
    while (node != NULL) {
        connect = (SdpConnectInfo *)ListGetNodeData(node);
        if ((connect->flag) && (memcmp(&connect->addr, addr, sizeof(BtAddr)) == 0)) {
            return connect;
        }
        node = ListGetNextNode(node);
    }

    return NULL;
}

static SdpConnectInfo *SdpFindIdleConnectByAddress(const BtAddr *addr)
{
    ListNode *node = NULL;
    SdpConnectInfo *connect = NULL;

    if (g_connectList == NULL) {
        return NULL;
    }
    node = ListGetFirstNode(g_connectList);
    while (node != NULL) {
        connect = (SdpConnectInfo *)ListGetNodeData(node);
        if ((connect->flag) && (memcmp(&connect->addr, addr, sizeof(BtAddr)) == 0) &&
            (connect->inConnState == SDP_STATE_IDLE) && (connect->outConnState == SDP_STATE_IDLE)) {
            return connect;
        }
        node = ListGetNextNode(node);
    }

    return NULL;
}

static void SdpNextConnect(const BtAddr *addr)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpClientRequest *request = NULL;

    SdpRemoveRequestByAddress(addr);
    request = SdpFindRequestByAddress(addr);
    if (request != NULL) {
        SdpSendConnectRequest(addr);
    }

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

typedef struct {
    BtAddr addr;
    uint16_t lcid;
    int result;
} SdpConnectCallbackInfo;

static void SdpConnectReqCallbackTask(const BtAddr *addr, uint16_t lcid, int result, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    /// Check if L2CAP started the connection process
    if ((result != BT_SUCCESS) || (lcid == 0)) {
        LOG_ERROR("[%{public}s][%{public}d] Send connect request failed ", __FUNCTION__, __LINE__);
        SdpNextConnect(addr);
        return;
    }

    connect = SdpFindIdleConnectByAddress(addr);
    /// Save channel id
    connect->lcid = lcid;
    /// Set connection state
    connect->outConnState = SDP_STATE_CONN_SETUP;
    connect->inConnState = SDP_STATE_CONN_SETUP;
    /// Set timer
    connect->timer = AlarmCreate(NULL, false);

    /// Start timer
    AlarmSet(connect->timer, SDP_CONNECT_TIMEOUT, SdpConnectTimeout, connect);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpConnectReqCallbackTsk(void *context)
{
    SdpConnectCallbackInfo *ctx = context;
    SdpConnectReqCallbackTask(&ctx->addr, ctx->lcid, ctx->result, NULL);
    MEM_MALLOC.free(ctx);
}

static void SdpConnectReqCallback(const BtAddr *addr, uint16_t lcid, int result, void *context)
{
    int ret;
    SdpConnectCallbackInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectCallbackInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpConnectCallbackInfo), 0x00, sizeof(SdpConnectCallbackInfo));

    (void)memcpy_s(&ctx->addr, sizeof(BtAddr), addr, sizeof(BtAddr));
    ctx->lcid = lcid;
    ctx->result = result;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpConnectReqCallbackTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

static void SdpConfigReqCallbackTask(uint16_t lcid, int result)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    if (result != BT_SUCCESS) {
        LOG_ERROR("[%{public}s][%{public}d] Send config request failed ", __FUNCTION__, __LINE__);
        connect = SdpFindConnectByCid(lcid);
        if ((connect != NULL) && (connect->flag)) {
            SdpNextConnect(&connect->addr);
        }
        return;
    }

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpConfigReqCallbackTsk(void *context)
{
    SdpConnectCallbackInfo *ctx = context;
    SdpConfigReqCallbackTask(ctx->lcid, ctx->result);
    MEM_MALLOC.free(ctx);
}

void SdpConfigReqCallback(uint16_t lcid, int result)
{
    int ret;
    SdpConnectCallbackInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectCallbackInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpConnectCallbackInfo), 0x00, sizeof(SdpConnectCallbackInfo));

    ctx->lcid = lcid;
    ctx->result = result;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpConfigReqCallbackTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

void SdpConnectRspCallbackTask(uint16_t lcid, int result)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *connect = NULL;
    L2capConfigInfo config;

    if (!SdpGetEnableState()) {
        return;
    }

    if (result != BT_SUCCESS) {
        LOG_ERROR("[%{public}s][%{public}d] Send connect response failed ", __FUNCTION__, __LINE__);
        return;
    }

    connect = MEM_MALLOC.alloc(sizeof(SdpConnectInfo));
    if (connect == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(connect, sizeof(SdpConnectInfo), 0, sizeof(SdpConnectInfo));
    /// Save channel id
    connect->lcid = lcid;
    /// Set connection state, waiting for config
    connect->outConnState = SDP_STATE_CONN_SETUP;
    connect->inConnState = SDP_STATE_CONN_SETUP;
    connect->flag = false;
    connect->timer = NULL;
    connect->wait = false;
    ListAddLast(g_connectList, connect);

    /// L2CAP default configuration. SDP only care about mtu.
    (void)memset_s(&config, sizeof(L2capConfigInfo), 0, sizeof(L2capConfigInfo));
    config.mtu = SDP_MTU_SIZE;
    config.flushTimeout = 0xFFFF;
    config.fcs = 0x01;
    L2CIF_ConfigReq(lcid, &config, SdpConfigReqCallback);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpConnectRspCallbackTsk(void *context)
{
    SdpConnectCallbackInfo *ctx = context;
    SdpConnectRspCallbackTask(ctx->lcid, ctx->result);
    MEM_MALLOC.free(ctx);
}

static void SdpConnectRspCallback(uint16_t lcid, int result)
{
    int ret;
    SdpConnectCallbackInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectCallbackInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpConnectCallbackInfo), 0x00, sizeof(SdpConnectCallbackInfo));

    ctx->lcid = lcid;
    ctx->result = result;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpConnectRspCallbackTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

static void SdpConfigRspCallbackTask(uint16_t lcid, int result)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *connect = NULL;
    SdpClientRequest *request = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_ERROR("[%{public}s][%{public}d] Config response callback failed", __FUNCTION__, __LINE__);
        return;
    }

    if (result != BT_SUCCESS) {
        LOG_ERROR("[%{public}s][%{public}d] Send config response failed ", __FUNCTION__, __LINE__);
        if ((connect != NULL) && (connect->flag)) {
            SdpNextConnect(&connect->addr);
        }
        return;
    }

    connect->inConnState = SDP_STATE_CFG_REQ_SETUP;
    if ((connect->outConnState == SDP_STATE_CFG_RSP_SETUP) && (connect->inConnState == SDP_STATE_CFG_REQ_SETUP)) {
        connect->outConnState = SDP_STATE_CONNECTED;
        connect->inConnState = SDP_STATE_CONNECTED;
        if (connect->flag) {
            // Send client packet
            request = SdpFindRequestByAddress(&connect->addr);
            if (request == NULL) {
                LOG_ERROR("[%{public}s][%{public}d] SdpFindRequestByAddress() return nullptr", __FUNCTION__, __LINE__);
                return;
            }
            request->packetState = SDP_PACKET_SEND;
            SdpSendRequest(lcid, request->transactionId, 0, NULL, request->packet);
        }
    }
    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpConfigRspCallbackTsk(void *context)
{
    SdpConnectCallbackInfo *ctx = context;
    SdpConfigRspCallbackTask(ctx->lcid, ctx->result);
    MEM_MALLOC.free(ctx);
}

static void SdpConfigRspCallback(uint16_t lcid, int result)
{
    int ret;
    SdpConnectCallbackInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectCallbackInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpConnectCallbackInfo), 0x00, sizeof(SdpConnectCallbackInfo));

    ctx->lcid = lcid;
    ctx->result = result;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpConfigRspCallbackTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

static void SdpDisconnectionRspCallbackTask(uint16_t lcid, int result)
{
    LOG_DEBUG("[%{public}s][%{public}d] lcid = 0x%04x start", __FUNCTION__, __LINE__, lcid);

    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    if (result != BT_SUCCESS) {
        LOG_ERROR("[%{public}s][%{public}d] Disconnect response callback failed", __FUNCTION__, __LINE__);
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_ERROR("[%{public}s][%{public}d] Disconnect response callback failed", __FUNCTION__, __LINE__);
        return;
    }
    ListRemoveNode(g_connectList, connect);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpDisconnectionRspCallbackTsk(void *context)
{
    SdpConnectCallbackInfo *ctx = context;
    SdpDisconnectionRspCallbackTask(ctx->lcid, ctx->result);
    MEM_MALLOC.free(ctx);
}

static void SdpDisconnectionRspCallback(uint16_t lcid, int result)
{
    int ret;
    SdpConnectCallbackInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpConnectCallbackInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpConnectCallbackInfo), 0x00, sizeof(SdpConnectCallbackInfo));

    ctx->lcid = lcid;
    ctx->result = result;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpDisconnectionRspCallbackTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

/**
 * @brief   Send connection request from L2CAP as a client.
 *
 * @param addr   The Bluetooth address of the peer.
 * @param packet The packet point for sending data.
 * @return Returns <b>BT_SUCCESS</b> if the operation is successful, otherwise the operation fails.
 */
static void SdpSendConnectRequest(const BtAddr *addr)
{
    SdpConnectInfo *connect = NULL;

    connect = MEM_MALLOC.alloc(sizeof(SdpConnectInfo));
    if (connect == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(connect, sizeof(SdpConnectInfo), 0, sizeof(SdpConnectInfo));

    /// Save address
    (void)memcpy_s(&connect->addr, sizeof(BtAddr), addr, sizeof(BtAddr));
    /// Set client connect
    connect->inConnState = SDP_STATE_IDLE;
    connect->outConnState = SDP_STATE_IDLE;
    connect->flag = true;
    connect->wait = false;
    ListAddLast(g_connectList, connect);

    /// Send request to L2CAP
    L2CIF_ConnectReq(addr, SDP_PSM, SDP_PSM, NULL, SdpConnectReqCallback);
}

typedef struct {
    uint16_t lcid;
    uint8_t id;
    L2capConnectionInfo info;
    uint16_t lpsm;
    void *context;
} SdpReceiveConnectRequestInfo;
/**
 * @brief Receive connection request from L2CAP as a server.
 *
 * @param lcid    Local channel identifier.
 * @param id      Identifier.
 * @param info    L2CAP connection info.
 * @param lpsm    SDP_PSM (0x0001).
 * @param context The context from upper layer.
 */
static void SdpReceiveConnectRequestTask(
    uint16_t lcid, uint8_t id, const L2capConnectionInfo *info, uint16_t lpsm, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    if (!SdpGetEnableState()) {
        return;
    }

    /// Send connect response to L2CAP
    L2CIF_ConnectRsp(
        lcid, id, L2CAP_CONNECTION_SUCCESSFUL, L2CAP_NO_FURTHER_INFORMATION_AVAILABLE, SdpConnectRspCallback);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpReceiveConnectRequestTsk(void *context)
{
    SdpReceiveConnectRequestInfo *ctx = context;
    SdpReceiveConnectRequestTask(ctx->lcid, ctx->id, &ctx->info, ctx->lpsm, ctx->context);
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveConnectRequest(
    uint16_t lcid, uint8_t id, const L2capConnectionInfo *info, uint16_t lpsm, void *context)
{
    int ret;
    SdpReceiveConnectRequestInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveConnectRequestInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpReceiveConnectRequestInfo), 0x00, sizeof(SdpReceiveConnectRequestInfo));

    ctx->lcid = lcid;
    ctx->id = id;
    (void)memcpy_s(&ctx->info, sizeof(L2capConnectionInfo), info, sizeof(L2capConnectionInfo));
    ctx->lpsm = lpsm;
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveConnectRequestTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

typedef struct {
    uint16_t lcid;
    L2capConnectionInfo info;
    uint16_t result;
    uint16_t status;
    void *context;
} SdpReceiveConnectResponseInfo;
/**
 * @brief Receive connection response from L2CAP as a client.
 *
 * @param lcid    Local channel identifier
 * @param info    L2CAP connection info.
 * @param result  L2CAP result
 * @param status  L2CAP status
 * @param context The context from upper layer.
 */
static void SdpReceiveConnectResponseTask(
    uint16_t lcid, const L2capConnectionInfo *info, uint16_t result, uint16_t status, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    L2capConfigInfo config;
    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_WARN("[%{public}s][%{public}d] Recv connect failed with unknown cid [0x%02x]", __FUNCTION__, __LINE__, lcid);
        SdpSendDisconnectRequest(lcid, false);
        return;
    }
    if (result == L2CAP_CONNECTION_PENDING) {
        LOG_WARN("[%{public}s][%{public}d] Recv connect response pending [0x%02x]", __FUNCTION__, __LINE__, lcid);
        return;
    } else if ((result != L2CAP_CONNECTION_SUCCESSFUL) || (connect->outConnState != SDP_STATE_CONN_SETUP) ||
               (connect->inConnState != SDP_STATE_CONN_SETUP)) {
        LOG_ERROR("[%{public}s][%{public}d] Recv connect response failed with cid [0x%02x]", __FUNCTION__, __LINE__, lcid);
        return;
    }

    /// stop timer when received a connect response
    if (connect->timer != NULL) {
        /// Cancel timeout
        AlarmCancel(connect->timer);
    }

    /// L2CAP default configuration. SDP only care about mtu.
    (void)memset_s(&config, sizeof(L2capConfigInfo), 0, sizeof(L2capConfigInfo));
    config.mtu = SDP_MTU_SIZE;
    config.flushTimeout = 0xFFFF;
    config.fcs = 0x01;
    L2CIF_ConfigReq(lcid, &config, SdpConfigReqCallback);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpReceiveConnectResponseTsk(void *context)
{
    SdpReceiveConnectResponseInfo *ctx = context;
    SdpReceiveConnectResponseTask(ctx->lcid, &ctx->info, ctx->result, ctx->status, ctx->context);
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveConnectResponse(
    uint16_t lcid, const L2capConnectionInfo *info, uint16_t result, uint16_t status, void *context)
{
    int ret;
    SdpReceiveConnectResponseInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveConnectResponseInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpReceiveConnectResponseInfo), 0x00, sizeof(SdpReceiveConnectResponseInfo));

    ctx->lcid = lcid;
    (void)memcpy_s(&ctx->info, sizeof(L2capConnectionInfo), info, sizeof(L2capConnectionInfo));
    ctx->result = result;
    ctx->status = status;
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveConnectResponseTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

typedef struct {
    uint16_t lcid;
    uint8_t id;
    L2capConfigInfo config;
    void *context;
} SdpReceiveConfigRequestInfo;
/**
 * @brief Receive Configuration response from L2CAP as a server or a client.
 *
 * @param lcid    Local channel identifier.
 * @param id      Identifier.
 * @param config  L2CAP configuration info.
 * @param context The context from upper layer.
 */
static void SdpReceiveConfigRequestTask(uint16_t lcid, uint8_t id, const L2capConfigInfo *config, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);

    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_ERROR("[%{public}s][%{public}d] Recv config request failed with unknown cid [0x%02x]", __FUNCTION__, __LINE__, lcid);
        return;
    }
    connect->mtu = config->mtu;
    L2CIF_ConfigRsp(lcid, id, config, L2CAP_CONNECTION_SUCCESSFUL, SdpConfigRspCallback);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpReceiveConfigRequestTsk(void *context)
{
    SdpReceiveConfigRequestInfo *ctx = context;
    SdpReceiveConfigRequestTask(ctx->lcid, ctx->id, &ctx->config, ctx->context);
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveConfigRequest(uint16_t lcid, uint8_t id, const L2capConfigInfo *config, void *context)
{
    int ret;
    SdpReceiveConfigRequestInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveConfigRequestInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpReceiveConfigRequestInfo), 0x00, sizeof(SdpReceiveConfigRequestInfo));

    ctx->lcid = lcid;
    ctx->id = id;
    (void)memcpy_s(&ctx->config, sizeof(L2capConfigInfo), config, sizeof(L2capConfigInfo));
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveConfigRequestTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

typedef struct {
    uint16_t lcid;
    L2capConfigInfo config;
    uint16_t result;
    void *context;
} SdpReceiveConfigResponseInfo;
/**
 * @brief Receive configuration response from L2CAP as a server or a client.
 *
 * @param lcid    Local channel identifier.
 * @param config  L2CAP configuration info.
 * @param result  Connect result.
 * @param context The context from upper layer.
 */
static void SdpReceiveConfigResponseTask(uint16_t lcid, const L2capConfigInfo *config, uint16_t result, void *context)
{
    SdpClientRequest *request = NULL;
    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_ERROR("[%{public}s][%{public}d] Recv connect with unknown cid [0x%02x]", __FUNCTION__, __LINE__, lcid);
        return;
    }
    if ((result != L2CAP_CONNECTION_SUCCESSFUL) && (connect->outConnState != SDP_STATE_CONN_SETUP)) {
        return;
    }

    connect->outConnState = SDP_STATE_CFG_RSP_SETUP;
    if ((connect->outConnState == SDP_STATE_CFG_RSP_SETUP) && (connect->inConnState == SDP_STATE_CFG_REQ_SETUP)) {
        connect->outConnState = SDP_STATE_CONNECTED;
        connect->inConnState = SDP_STATE_CONNECTED;
        if (connect->flag) {
            // Send client packet
            request = SdpFindRequestByAddress(&connect->addr);
            if (request == NULL) {
                LOG_ERROR("[%{public}s][%{public}d] SdpFindRequestByAddress() return nullptr", __FUNCTION__, __LINE__);
                return;
            }
            request->packetState = SDP_PACKET_SEND;
            SdpSendRequest(connect->lcid, request->transactionId, 0, NULL, request->packet);
        }
    }
}

static void SdpReceiveConfigResponseTsk(void *context)
{
    SdpReceiveConfigResponseInfo *ctx = context;
    SdpReceiveConfigResponseTask(ctx->lcid, &ctx->config, ctx->result, ctx->context);
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveConfigResponse(uint16_t lcid, const L2capConfigInfo *config, uint16_t result, void *context)
{
    int ret;
    SdpReceiveConfigResponseInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveConfigResponseInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }

    (void)memset_s(ctx, sizeof(SdpReceiveConfigResponseInfo), 0x00, sizeof(SdpReceiveConfigResponseInfo));

    ctx->lcid = lcid;
    (void)memcpy_s(&ctx->config, sizeof(L2capConfigInfo), config, sizeof(L2capConfigInfo));
    ctx->result = result;
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveConfigResponseTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

/**
 * @brief Send disconnection request from L2CAP as a client.
 *
 * @param lcid Local channel identifier.
 */
static void SdpSendDisconnectRequest(uint16_t lcid, bool wait)
{
    LOG_DEBUG("[%{public}s][%{public}d] lcid = 0x%04x start", __FUNCTION__, __LINE__, lcid);

    SdpConnectInfo *connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_WARN("[%{public}s][%{public}d] This connection has been disconnected.", __FUNCTION__, __LINE__);
        return;
    }
    connect->inConnState = SDP_STATE_DISCONNECT;
    connect->outConnState = SDP_STATE_DISCONNECT;
    connect->wait = wait;

    L2CIF_DisconnectionReq(lcid, NULL);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

typedef struct {
    uint16_t lcid;
    uint8_t id;
    void *context;
} SdpReceiveDisconnectRequestInfo;
/**
 * @brief Receive disconnection request from L2CAP as a server.
 *
 * @param lcid    Local channel identifier.
 * @param id      Identifier.
 * @param context The context from upper layer.
 */
static void SdpReceiveDisconnectRequestTask(uint16_t lcid, uint8_t id, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] lcid = 0x%04x start", __FUNCTION__, __LINE__, lcid);

    if (!SdpGetEnableState()) {
        return;
    }

    L2CIF_DisconnectionRsp(lcid, id, SdpDisconnectionRspCallback);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpReceiveDisconnectRequestTsk(void *context)
{
    SdpReceiveDisconnectRequestInfo *ctx = context;
    SdpReceiveDisconnectRequestTask(ctx->lcid, ctx->id, ctx->context);
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveDisconnectRequest(uint16_t lcid, uint8_t id, void *context)
{
    int ret;
    SdpReceiveDisconnectRequestInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveDisconnectRequestInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpReceiveDisconnectRequestInfo), 0x00, sizeof(SdpReceiveDisconnectRequestInfo));

    ctx->lcid = lcid;
    ctx->id = id;
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveDisconnectRequestTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

typedef struct {
    uint16_t lcid;
    void *context;
} SdpReceiveDisconnectResponseInfo;
/**
 * @brief Receive disconnection response from L2CAP as a client.
 *
 * @param lcid    Local channel identifier.
 * @param context The context from upper layer.
 */
static void SdpReceiveDisconnectResponseTask(uint16_t lcid, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] lcid = 0x%04x start", __FUNCTION__, __LINE__, lcid);

    SdpConnectInfo *connect = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_WARN("[%{public}s][%{public}d] This connection has been disconnected.", __FUNCTION__, __LINE__);
        return;
    }
    if (!connect->wait) {
        SdpRemoveRequestByAddress(&connect->addr);
    }
    ListRemoveNode(g_connectList, connect);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpReceiveDisconnectResponseTsk(void *context)
{
    SdpReceiveDisconnectResponseInfo *ctx = context;
    SdpReceiveDisconnectResponseTask(ctx->lcid, ctx->context);
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveDisconnectResponse(uint16_t lcid, void *context)
{
    int ret;
    SdpReceiveDisconnectResponseInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveDisconnectResponseInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpReceiveDisconnectResponseInfo), 0x00, sizeof(SdpReceiveDisconnectResponseInfo));

    ctx->lcid = lcid;
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveDisconnectResponseTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

typedef struct {
    uint16_t lcid;
    uint8_t reason;
    void *context;
} SdpDisconnectAbnormalInfo;
/**
 * @brief Receive abnormal disconnection response from L2CAP as a server or a client.
 *
 * @param lcid    Local channel identifier.
 * @param reason  The reason of abnormal disconnection.
 * @param context The context from upper layer.
 */
static void SdpDisconnectAbnormalTask(uint16_t lcid, uint8_t reason, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] lcid = 0x%04x reason = 0x%02x start", __FUNCTION__, __LINE__, lcid, reason);

    SdpConnectInfo *connect = NULL;
    SdpClientRequest *request = NULL;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_WARN("[%{public}s][%{public}d] This connection has been disconnected.", __FUNCTION__, __LINE__);
        return;
    }
#define HCI_COMMAND_DISALLOWED 0x0c
    if (connect->flag) {
        if (reason == HCI_COMMAND_DISALLOWED) {
            request = SdpFindRequestByAddress(&connect->addr);
            if (request == NULL) {
                LOG_ERROR("[%{public}s][%{public}d] SdpFindRequestByAddress() return nullptr", __FUNCTION__, __LINE__);
                return;
            }
            if (!request->resentFlag) {
                ListRemoveNode(g_connectList, connect);
                SdpSendConnectRequest(&request->addr);
                request->resentFlag = true;
                return;
            }
        } else if (reason == L2CAP_STATE_COLLISION) {
            request = SdpFindRequestByAddress(&connect->addr);
            if (request == NULL) {
                LOG_ERROR("[%{public}s][%{public}d] SdpFindRequestByAddress() return nullptr", __FUNCTION__, __LINE__);
                return;
            }
            ListRemoveNode(g_connectList, connect);
            SdpSendConnectRequest(&request->addr);
            return;
        }
        SdpRemoveAllRequestByAddress(&connect->addr);
    }
    ListRemoveNode(g_connectList, connect);

    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpDisconnectAbnormalTsk(void *context)
{
    SdpDisconnectAbnormalInfo *ctx = context;
    SdpDisconnectAbnormalTask(ctx->lcid, ctx->reason, ctx->context);
    MEM_MALLOC.free(ctx);
}
static void SdpDisconnectAbnormal(uint16_t lcid, uint8_t reason, void *context)
{
    int ret;
    SdpDisconnectAbnormalInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpDisconnectAbnormalInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpDisconnectAbnormalInfo), 0x00, sizeof(SdpDisconnectAbnormalInfo));

    ctx->lcid = lcid;
    ctx->reason = reason;
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpDisconnectAbnormalTsk, ctx);
    if (ret != BT_SUCCESS) {
        MEM_MALLOC.free(ctx);
        return;
    }
}

typedef struct {
    uint16_t lcid;
    Packet *packet;
    void *context;
} SdpReceiveDataInfo;
/**
 * @brief Receive packet from L2CAP as a server or client.
 *
 * @param lcid    Local channel identifier
 * @param packet  The packet point for receiving data
 * @param context The context from upper layer.
 * @return    void
 */
static void SdpReceiveDataTask(uint16_t lcid, const Packet *packet, void *context)
{
    LOG_DEBUG("[%{public}s][%{public}d] start", __FUNCTION__, __LINE__);
    SdpConnectInfo *connect = NULL;
    SdpClientRequest *request = NULL;
    bool flag = false;

    if (!SdpGetEnableState()) {
        return;
    }

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_ERROR("[%{public}s][%{public}d] Recv connect with unknown cid [0x%02x]", __FUNCTION__, __LINE__, lcid);
        return;
    }
    if ((connect->outConnState == SDP_STATE_CONNECTED) && (connect->inConnState == SDP_STATE_CONNECTED)) {
        if (connect->flag) {
            SdpParseServerResponse(&connect->addr, lcid, packet);
            request = SdpFindRemainRequestByAddress(&connect->addr, &flag);
            if (request != NULL) {
                request->packetState = SDP_PACKET_SEND;
                SdpSendRequest(connect->lcid, request->transactionId, 0, NULL, request->packet);
            } else if (!flag) {
                AlarmSet(connect->timer, SDP_CONNECT_WAIT_TIME, SdpConnectWaitTime, connect);
            }
        } else {
            SdpParseClientRequest(lcid, packet);
        }
    }
    LOG_DEBUG("[%{public}s][%{public}d] end", __FUNCTION__, __LINE__);
}

static void SdpReceiveDataTsk(void *context)
{
    SdpReceiveDataInfo *ctx = context;
    SdpReceiveDataTask(ctx->lcid, ctx->packet, ctx->context);
    PacketFree(ctx->packet);
    ctx->packet = NULL;
    MEM_MALLOC.free(ctx);
}

static void SdpReceiveData(uint16_t lcid, Packet *packet, void *context)
{
    int ret;
    SdpReceiveDataInfo *ctx = MEM_MALLOC.alloc(sizeof(SdpReceiveDataInfo));
    if (ctx == NULL) {
        LOG_ERROR("point to NULL");
        return;
    }
    (void)memset_s(ctx, sizeof(SdpReceiveDataInfo), 0x00, sizeof(SdpReceiveDataInfo));

    ctx->lcid = lcid;
    ctx->packet = PacketRefMalloc(packet);
    ctx->context = context;

    ret = BTM_RunTaskInProcessingQueue(PROCESSING_QUEUE_ID_SDP, SdpReceiveDataTsk, ctx);
    if (ret != BT_SUCCESS) {
        PacketFree(ctx->packet);
        ctx->packet = NULL;
        MEM_MALLOC.free(ctx);
        return;
    }
}

void SdpSendErrorResponse(uint16_t lcid, uint16_t transactionId, uint16_t errorCode)
{
    Packet *packet = NULL;
    uint8_t buffer[2] = {0};
    uint8_t *header = NULL;
    uint16_t offset = 0;

    packet = PacketMalloc(SDP_PDU_HEADER_LENGTH, 0, SDP_UINT16_LENGTH);
    *(uint16_t *)buffer = H2BE_16(errorCode);
    PacketPayloadWrite(packet, buffer, 0, SDP_UINT16_LENGTH);

    header = (uint8_t *)BufferPtr(PacketHead(packet));
    /// PduID
    header[0] = SDP_ERROR_RESPONSE;
    offset++;
    /// Transaction ID
    *(uint16_t *)(header + offset) = H2BE_16(transactionId);
    offset += SDP_UINT16_LENGTH;
    /// ParameterLength
    *(uint16_t *)(header + offset) = H2BE_16(SDP_UINT16_LENGTH);

    L2CIF_SendData(lcid, packet, NULL);
    PacketFree(packet);
    packet = NULL;
}

static uint16_t SdpGetCurrentServiceRecordCount(uint16_t mtu, uint16_t maxCount, uint16_t totalServiceRecordCount)
{
    uint16_t number = (mtu - SDP_PACKET_HEADER_AND_TAIL_LENGTH) / SDP_SERVICE_RECORD_HANDLE_BYTE;
    if (maxCount > number) {
        return number;
    }
    return maxCount;
}

static Packet *SdpBuildSearchFragmentResponse(uint16_t transactionId, size_t size, uint16_t totalServiceRecordCount,
    uint16_t currentServiceRecordCount, Packet *fragmentPacket)
{
    Packet *sendPacket = NULL;
    uint8_t *header = NULL;
    uint8_t *tail = NULL;
    uint16_t length = 0;
    uint16_t offset = 0;

    /// ContinuationState
    if (size == 0) {
        sendPacket =
            PacketInheritMalloc(fragmentPacket, SDP_PDU_HEADER_LENGTH + SDP_UINT32_LENGTH, SDP_PACKET_TAIL_ONE_BYTE);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x00;
        length = currentServiceRecordCount * SDP_UINT32_LENGTH + SDP_UINT32_LENGTH + SDP_PACKET_TAIL_ONE_BYTE;
    } else if (size <= 0xFF) {
        sendPacket =
            PacketInheritMalloc(fragmentPacket, SDP_PDU_HEADER_LENGTH + SDP_UINT32_LENGTH, SDP_PACKET_TAIL_TWO_BYTE);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x01;
        tail[1] = size & 0xFF;
        length = currentServiceRecordCount * SDP_UINT32_LENGTH + SDP_UINT32_LENGTH + SDP_PACKET_TAIL_TWO_BYTE;
    } else if (size <= 0xFFFF) {
        sendPacket =
            PacketInheritMalloc(fragmentPacket, SDP_PDU_HEADER_LENGTH + SDP_UINT32_LENGTH, SDP_PACKET_TAIL_THREE_BYTE);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x02;
        *(uint16_t *)(tail + 1) = H2BE_16(size);
        length = currentServiceRecordCount * SDP_UINT32_LENGTH + SDP_UINT32_LENGTH + SDP_PACKET_TAIL_THREE_BYTE;
    } else {
        LOG_ERROR("[%{public}s][%{public}d] Invalid continuation length [%zu]", __FUNCTION__, __LINE__, size);
        return NULL;
    }
    header = BufferPtr(PacketHead(sendPacket));
    /// PduID
    header[offset] = SDP_SERVICE_SEARCH_RESPONSE;
    offset++;
    /// Transaction ID
    *(uint16_t *)(header + offset) = H2BE_16(transactionId);
    offset += SDP_UINT16_LENGTH;
    /// ParameterLength
    *(uint16_t *)(header + offset) = H2BE_16(length);
    offset += SDP_UINT16_LENGTH;
    /// TotalServiceRecordCount
    *(uint16_t *)(header + offset) = H2BE_16(totalServiceRecordCount);
    offset += SDP_UINT16_LENGTH;
    /// CurrentServiceRecordCount
    *(uint16_t *)(header + offset) = H2BE_16(currentServiceRecordCount);

    return sendPacket;
}

void SdpSendSearchFragmentResponse(uint16_t lcid, uint16_t transactionId, uint16_t maxCount, const Packet *searchPacket)
{
    SdpConnectInfo *connect = NULL;
    uint16_t totalServiceRecordCount;
    uint16_t currentServiceRecordCount;
    Packet *packet = NULL;
    Packet *fragmentPacket = NULL;
    Packet *sendPacket = NULL;
    size_t size;

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        return;
    }

    if (searchPacket == NULL) {
        if (connect->packet == NULL) {
            return;
        }
        packet = connect->packet;
        totalServiceRecordCount = connect->totalCount;
    } else {
        packet = PacketRefMalloc(searchPacket);
        totalServiceRecordCount = PacketSize(packet) / SDP_SERVICE_RECORD_HANDLE_BYTE;
    }
    currentServiceRecordCount = SdpGetCurrentServiceRecordCount(connect->mtu, maxCount, totalServiceRecordCount);

    fragmentPacket = PacketMalloc(0, 0, 0);
    size = PacketFragment(packet, fragmentPacket, currentServiceRecordCount * SDP_SERVICE_RECORD_HANDLE_BYTE);
    currentServiceRecordCount = PacketSize(fragmentPacket) / SDP_SERVICE_RECORD_HANDLE_BYTE;

    sendPacket = SdpBuildSearchFragmentResponse(
        transactionId, size, totalServiceRecordCount, currentServiceRecordCount, fragmentPacket);
    if (sendPacket == NULL) {
        SdpSendErrorResponse(lcid, transactionId, SDP_INVALID_CONT_STATE);
        PacketFree(fragmentPacket);
        fragmentPacket = NULL;
        return;
    }

    /// Send data
    L2CIF_SendData(lcid, sendPacket, NULL);

    if (size != 0) {
        connect->packet = packet;
        connect->totalCount = totalServiceRecordCount;
    } else {
        PacketFree(packet);
        connect->packet = NULL;
        packet = NULL;
    }

    PacketFree(fragmentPacket);
    fragmentPacket = NULL;
    PacketFree(sendPacket);
    sendPacket = NULL;
}

void SdpSendSearchResponse(uint16_t lcid, uint16_t transactionId, uint16_t offset, uint8_t *buffer, uint16_t maxCount)
{
    SdpConnectInfo *connect = NULL;
    uint16_t totalServiceRecordCount;
    uint16_t number;
    uint16_t length;
    Packet *packet = NULL;
    Packet *sendPacket = NULL;
    uint8_t *header = NULL;
    uint8_t *tail = NULL;
    uint16_t pos = 0;

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        return;
    }

    /// ServiceSearchPattern
    packet = PacketMalloc(0, 0, offset);
    PacketPayloadWrite(packet, buffer, 0, offset);

    /// TotalServiceRecordCount
    totalServiceRecordCount = offset / SDP_SERVICE_RECORD_HANDLE_BYTE;
    if (totalServiceRecordCount > maxCount) {
        totalServiceRecordCount = maxCount;
    }
    number = (connect->mtu - SDP_PACKET_HEADER_AND_TAIL_LENGTH - 1) / SDP_SERVICE_RECORD_HANDLE_BYTE;
    if (totalServiceRecordCount > number) {
        /// Fragment packet
        SdpSendSearchFragmentResponse(lcid, transactionId, maxCount, packet);
        PacketFree(packet);
        packet = NULL;
        return;
    }

    /// Single packet
    length = totalServiceRecordCount * SDP_SERVICE_RECORD_HANDLE_BYTE + SDP_UINT32_LENGTH + 1;
    sendPacket = PacketInheritMalloc(packet, SDP_PDU_HEADER_LENGTH + SDP_UINT32_LENGTH, 1);
    header = BufferPtr(PacketHead(sendPacket));
    tail = BufferPtr(PacketTail(sendPacket));
    /// PduID
    header[pos] = SDP_SERVICE_SEARCH_RESPONSE;
    pos++;
    /// Transaction ID
    *(uint16_t *)(header + pos) = H2BE_16(transactionId);
    pos += SDP_UINT16_LENGTH;
    /// ParameterLength
    *(uint16_t *)(header + pos) = H2BE_16(length);
    pos += SDP_UINT16_LENGTH;
    /// TotalServiceRecordCount
    *(uint16_t *)(header + pos) = H2BE_16(totalServiceRecordCount);
    pos += SDP_UINT16_LENGTH;
    /// CurrentServiceRecordCount = TotalServiceRecordCount
    *(uint16_t *)(header + pos) = H2BE_16(totalServiceRecordCount);
    /// ContinuationState
    tail[0] = 0;

    /// Send packet
    L2CIF_SendData(lcid, sendPacket, NULL);

    PacketFree(packet);
    packet = NULL;
    PacketFree(sendPacket);
    sendPacket = NULL;
}

static Packet *SdpBuildAttributeFragmentResponse(
    SdpPduId pduId, uint16_t transactionId, size_t size, Packet *fragmentPacket)
{
    Packet *sendPacket = NULL;
    uint16_t length = 0;
    uint16_t offset = 0;
    uint8_t *header = NULL;
    uint8_t *tail = NULL;

    /// AttributeByteCount
    uint16_t attributeByteCount = (uint16_t)PacketSize(fragmentPacket);

    /// ContinuationState
    if (size == 0) {
        sendPacket =
            PacketInheritMalloc(fragmentPacket, SDP_PDU_HEADER_LENGTH + SDP_UINT16_LENGTH, SDP_PACKET_TAIL_ONE_BYTE);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x00;
        length = attributeByteCount + SDP_UINT16_LENGTH + SDP_PACKET_TAIL_ONE_BYTE;
    } else if (size <= 0xFF) {
        sendPacket =
            PacketInheritMalloc(fragmentPacket, SDP_PDU_HEADER_LENGTH + SDP_UINT16_LENGTH, SDP_PACKET_TAIL_TWO_BYTE);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x01;
        tail[1] = size & 0xFF;
        length = attributeByteCount + SDP_UINT16_LENGTH + SDP_PACKET_TAIL_TWO_BYTE;
    } else if (size <= 0xFFFF) {
        sendPacket =
            PacketInheritMalloc(fragmentPacket, SDP_PDU_HEADER_LENGTH + SDP_UINT16_LENGTH, SDP_PACKET_TAIL_THREE_BYTE);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x02;
        *(uint16_t *)(tail + 1) = H2BE_16((uint16_t)size);
        length = attributeByteCount + SDP_UINT16_LENGTH + SDP_PACKET_TAIL_THREE_BYTE;
    } else {
        LOG_ERROR("[%{public}s][%{public}d] Invalid continuation length [%zu]", __FUNCTION__, __LINE__, size);
        return NULL;
    }

    header = BufferPtr(PacketHead(sendPacket));
    /// PduID
    header[offset] = pduId;
    offset++;
    /// Transaction ID
    *(uint16_t *)(header + offset) = H2BE_16(transactionId);
    offset += SDP_UINT16_LENGTH;
    /// ParameterLength
    *(uint16_t *)(header + offset) = H2BE_16(length);
    offset += SDP_UINT16_LENGTH;
    /// AttributeListByteCount
    *(uint16_t *)(header + offset) = H2BE_16(attributeByteCount);

    return sendPacket;
}

void SdpSendAttributeFragmentResponse(
    uint16_t lcid, SdpPduId pduId, uint16_t transactionId, uint16_t maxCount, const Packet *attributePacket)
{
    SdpConnectInfo *connect = NULL;
    Packet *packet = NULL;
    Packet *fragmentPacket = NULL;
    Packet *sendPacket = NULL;
    size_t size;

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        return;
    }

    if (attributePacket == NULL) {
        if (connect->packet == NULL) {
            return;
        }
        packet = connect->packet;
    } else {
        packet = PacketRefMalloc(attributePacket);
    }

    fragmentPacket = PacketMalloc(0, 0, 0);
    size = PacketFragment(packet, fragmentPacket, maxCount);

    sendPacket = SdpBuildAttributeFragmentResponse(pduId, transactionId, size, fragmentPacket);
    if (sendPacket == NULL) {
        SdpSendErrorResponse(lcid, transactionId, SDP_INVALID_CONT_STATE);
        return;
    }

    /// Send data
    L2CIF_SendData(lcid, sendPacket, NULL);

    if (size != 0) {
        connect = SdpFindConnectByCid(lcid);
        if (connect == NULL) {
            LOG_ERROR("point to NULL");
            return;
        }
        /// store remain packet
        connect->packet = packet;
    } else {
        PacketFree(packet);
        packet = NULL;
        connect->packet = NULL;
    }

    PacketFree(fragmentPacket);
    fragmentPacket = NULL;
    PacketFree(sendPacket);
    sendPacket = NULL;
}

void SdpSendAttributeResponse(
    uint16_t lcid, uint16_t transactionId, SdpPduId pduId, uint16_t maxCount, const Packet *packet)
{
    SdpConnectInfo *connect = NULL;
    Packet *sendPacket = NULL;
    uint16_t length;
    uint8_t *header = NULL;
    uint8_t *tail = NULL;

    connect = SdpFindConnectByCid(lcid);
    if (connect == NULL) {
        LOG_ERROR("[%{public}s][%{public}d] There is no connect with cid [%hu]", __FUNCTION__, __LINE__, lcid);
        return;
    }

    if (maxCount > (connect->mtu - SDP_PACKET_HEADER_AND_TAIL_LENGTH)) {
        maxCount = connect->mtu - SDP_PACKET_HEADER_AND_TAIL_LENGTH;
    }

    length = PacketSize(packet);
    if (length > maxCount) {
        /// Fragment packet
        SdpSendAttributeFragmentResponse(lcid, pduId, transactionId, maxCount, packet);
        return;
    } else {
        /// Single packet
        uint16_t offset = 0;
        sendPacket = PacketInheritMalloc(packet, SDP_PDU_HEADER_LENGTH + SDP_UINT16_LENGTH, SDP_PACKET_TAIL_ONE_BYTE);
        header = BufferPtr(PacketHead(sendPacket));
        tail = BufferPtr(PacketTail(sendPacket));
        /// PduID
        header[offset] = pduId;
        offset++;
        /// Transaction ID
        *(uint16_t *)(header + offset) = H2BE_16(transactionId);
        offset += SDP_UINT16_LENGTH;
        /// ParameterLength
        *(uint16_t *)(header + offset) = H2BE_16(length + SDP_UINT16_LENGTH + SDP_PACKET_TAIL_ONE_BYTE);
        offset += SDP_UINT16_LENGTH;
        /// AttributeListByteCount
        *(uint16_t *)(header + offset) = H2BE_16(length);
        /// ContinuationState
        tail[0] = 0;

        /// Send packet
        L2CIF_SendData(lcid, sendPacket, NULL);
        PacketFree(sendPacket);
        sendPacket = NULL;
    }
}

void SdpSendRequest(uint16_t lcid, uint16_t transactionId, uint8_t continuationStateLen,
    const uint8_t *continuationState, Packet *packet)
{
    SdpClientRequest *request = NULL;
    Packet *sendPacket = NULL;
    uint8_t *header = NULL;
    uint8_t *tail = NULL;

    uint16_t length = PacketPayloadSize(packet);
    /// ContinuationState
    if (continuationStateLen == 0) {
        sendPacket = PacketInheritMalloc(packet, SDP_PDU_HEADER_LENGTH, 1);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x00;
        length += 1;
    } else {
        sendPacket = PacketInheritMalloc(packet, SDP_PDU_HEADER_LENGTH, continuationStateLen + 1);
        tail = BufferPtr(PacketTail(sendPacket));
        tail[0] = 0x01;
        (void)memcpy_s(tail, continuationStateLen + 1, continuationState, continuationStateLen + 1);
        length = length + continuationStateLen + 1;
    }

    header = (uint8_t *)BufferPtr(PacketHead(sendPacket));
    /// PduID
    request = SdpFindRequestByTransactionId(transactionId);
    if (request == NULL) {
        return;
    }
    header[0] = request->pduId;
    /// Transaction ID
    *(uint16_t *)(header + 1) = H2BE_16(transactionId);
    /// ParameterLength
    *(uint16_t *)(header + SDP_UINT16_LENGTH + 1) = H2BE_16(length);

    /// Send packet
    L2CIF_SendData(lcid, sendPacket, NULL);

    PacketFree(sendPacket);
    sendPacket = NULL;
}

int SdpClientConnect(SdpClientRequest *request)
{
    SdpConnectInfo *connect = NULL;

    if (g_connectList == NULL) {
        return BT_OPERATION_FAILED;
    }
    connect = SdpFindConnectByAddress(&request->addr);
    if (connect == NULL) {
        /// Create new channel
        SdpAddRequest(request);
        SdpSendConnectRequest(&request->addr);
    } else {
        /// Use existed channel
        if ((connect->inConnState == SDP_STATE_CONNECTED) && (connect->outConnState == SDP_STATE_CONNECTED)) {
            /// Channel idle and send packet
            if (connect->timer != NULL) {
                AlarmCancel(connect->timer);
            }
            request->packetState = SDP_PACKET_SEND;
            SdpAddRequest(request);
            SdpSendRequest(connect->lcid, request->transactionId, 0, NULL, request->packet);
        } else if ((connect->inConnState == SDP_STATE_DISCONNECT) && (connect->outConnState == SDP_STATE_DISCONNECT)) {
            /// Create new channel
            SdpAddRequest(request);
            SdpSendConnectRequest(&request->addr);
        } else {
            SdpAddRequest(request);
        }
    }

    return BT_SUCCESS;
}