/******************************************************************************
 *
 *  Copyright 2006-2012 Broadcom Corporation
 *
 *  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.
 *
 ******************************************************************************/

/******************************************************************************
 *
 *  This file contains action functions for BTA JV APIs.
 *
 ******************************************************************************/

#define LOG_TAG "bluetooth"

#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>

#include <cstdint>
#include <unordered_set>

#include "bta/include/bta_jv_co.h"
#include "bta/include/bta_rfcomm_scn.h"
#include "bta/jv/bta_jv_int.h"
#include "bta/sys/bta_sys.h"
#include "internal_include/bt_target.h"
#include "osi/include/allocator.h"
#include "osi/include/properties.h"
#include "stack/btm/btm_sec.h"
#include "stack/include/bt_hdr.h"
#include "stack/include/bt_psm_types.h"
#include "stack/include/bt_types.h"
#include "stack/include/bt_uuid16.h"
#include "stack/include/btm_client_interface.h"
#include "stack/include/gap_api.h"
#include "stack/include/l2cap_interface.h"
#include "stack/include/l2cdefs.h"
#include "stack/include/port_api.h"
#include "stack/include/rfcdefs.h"
#include "stack/include/sdp_api.h"
#include "types/bluetooth/uuid.h"
#include "types/raw_address.h"

// TODO(b/369381361) Enfore -Wmissing-prototypes
#pragma GCC diagnostic ignored "-Wmissing-prototypes"

using namespace bluetooth::legacy::stack::sdp;
using namespace bluetooth;

tBTA_JV_CB bta_jv_cb;
std::unordered_set<uint16_t> used_l2cap_classic_dynamic_psm;

static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb_open);
static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(uint32_t jv_handle);
static void bta_jv_pm_conn_busy(tBTA_JV_PM_CB* p_cb);
static void bta_jv_pm_conn_idle(tBTA_JV_PM_CB* p_cb);
static void bta_jv_pm_state_change(tBTA_JV_PM_CB* p_cb, const tBTA_JV_CONN_STATE state);
static void bta_jv_reset_sniff_timer(tBTA_JV_PM_CB* p_cb);

#ifndef BTA_JV_SDP_DB_SIZE
#define BTA_JV_SDP_DB_SIZE 4500
#endif

#ifndef BTA_JV_SDP_RAW_DATA_SIZE
#define BTA_JV_SDP_RAW_DATA_SIZE 1800
#endif

static uint8_t bta_jv_sdp_raw_data[BTA_JV_SDP_RAW_DATA_SIZE];
static tSDP_DISCOVERY_DB bta_jv_sdp_db_data[BTA_JV_SDP_DB_SIZE / sizeof(tSDP_DISCOVERY_DB)];

/* JV configuration structure */
struct tBTA_JV_CFG {
  uint16_t sdp_raw_size;       /* The size of p_sdp_raw_data */
  uint16_t sdp_db_size;        /* The size of p_sdp_db */
  uint8_t* p_sdp_raw_data;     /* The data buffer to keep raw data */
  tSDP_DISCOVERY_DB* p_sdp_db; /* The data buffer to keep SDP database */
} bta_jv_cfg = {
        BTA_JV_SDP_RAW_DATA_SIZE, /* The size of p_sdp_raw_data */
        (BTA_JV_SDP_DB_SIZE / sizeof(tSDP_DISCOVERY_DB)) *
                sizeof(tSDP_DISCOVERY_DB), /* The size of p_sdp_db_data */
        bta_jv_sdp_raw_data,               /* The data buffer to keep raw data */
        bta_jv_sdp_db_data                 /* The data buffer to keep SDP database */
};

tBTA_JV_CFG* p_bta_jv_cfg = &bta_jv_cfg;

/*******************************************************************************
 *
 * Function     bta_jv_alloc_sec_id
 *
 * Description  allocate a security id
 *
 * Returns
 *
 ******************************************************************************/
uint8_t bta_jv_alloc_sec_id(void) {
  uint8_t ret = 0;
  int i;
  for (i = 0; i < BTA_JV_NUM_SERVICE_ID; i++) {
    if (0 == bta_jv_cb.sec_id[i]) {
      bta_jv_cb.sec_id[i] = BTA_JV_FIRST_SERVICE_ID + i;
      ret = bta_jv_cb.sec_id[i];
      break;
    }
  }
  return ret;
}
static int get_sec_id_used(void) {
  int i;
  int used = 0;
  for (i = 0; i < BTA_JV_NUM_SERVICE_ID; i++) {
    if (bta_jv_cb.sec_id[i]) {
      used++;
    }
  }
  if (used == BTA_JV_NUM_SERVICE_ID) {
    log::error("sec id exceeds the limit={}", BTA_JV_NUM_SERVICE_ID);
  }
  return used;
}
static int get_rfc_cb_used(void) {
  int i;
  int used = 0;
  for (i = 0; i < BTA_JV_MAX_RFC_CONN; i++) {
    if (bta_jv_cb.rfc_cb[i].handle) {
      used++;
    }
  }
  if (used == BTA_JV_MAX_RFC_CONN) {
    log::error("rfc ctrl block exceeds the limit={}", BTA_JV_MAX_RFC_CONN);
  }
  return used;
}

/*******************************************************************************
 *
 * Function     bta_jv_free_sec_id
 *
 * Description  free the given security id
 *
 * Returns
 *
 ******************************************************************************/
static void bta_jv_free_sec_id(uint8_t* p_sec_id) {
  uint8_t sec_id = *p_sec_id;
  *p_sec_id = 0;
  if (sec_id >= BTA_JV_FIRST_SERVICE_ID && sec_id <= BTA_JV_LAST_SERVICE_ID) {
    BTM_SecClrService(sec_id);
    bta_jv_cb.sec_id[sec_id - BTA_JV_FIRST_SERVICE_ID] = 0;
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_from_gap_l2cap_err
 *
 * Description  Convert the L2CAP error result propagated from GAP to BTA JV
 *              L2CAP close reason code.
 *
 * Params      l2cap_result: The L2CAP result propagated from GAP error.
 *
 * Returns     Appropriate l2cap error reason value
 *             or BTA_JV_L2CAP_REASON_UNKNOWN if reason isn't defined yet.
 *
 ******************************************************************************/
static tBTA_JV_L2CAP_REASON bta_jv_from_gap_l2cap_err(const tL2CAP_CONN& l2cap_result) {
  switch (l2cap_result) {
    case tL2CAP_CONN::L2CAP_CONN_ACL_CONNECTION_FAILED:
      return BTA_JV_L2CAP_REASON_ACL_FAILURE;
    case tL2CAP_CONN::L2CAP_CONN_CLIENT_SECURITY_CLEARANCE_FAILED:
      return BTA_JV_L2CAP_REASON_CL_SEC_FAILURE;
    case tL2CAP_CONN::L2CAP_CONN_INSUFFICIENT_AUTHENTICATION:
      return BTA_JV_L2CAP_REASON_INSUFFICIENT_AUTHENTICATION;
    case tL2CAP_CONN::L2CAP_CONN_INSUFFICIENT_AUTHORIZATION:
      return BTA_JV_L2CAP_REASON_INSUFFICIENT_AUTHORIZATION;
    case tL2CAP_CONN::L2CAP_CONN_INSUFFICIENT_ENCRYP_KEY_SIZE:
      return BTA_JV_L2CAP_REASON_INSUFFICIENT_ENCRYP_KEY_SIZE;
    case tL2CAP_CONN::L2CAP_CONN_INSUFFICIENT_ENCRYP:
      return BTA_JV_L2CAP_REASON_INSUFFICIENT_ENCRYP;
    case tL2CAP_CONN::L2CAP_CONN_INVALID_SOURCE_CID:
      return BTA_JV_L2CAP_REASON_INVALID_SOURCE_CID;
    case tL2CAP_CONN::L2CAP_CONN_SOURCE_CID_ALREADY_ALLOCATED:
      return BTA_JV_L2CAP_REASON_SOURCE_CID_ALREADY_ALLOCATED;
    case tL2CAP_CONN::L2CAP_CONN_UNACCEPTABLE_PARAMETERS:
      return BTA_JV_L2CAP_REASON_UNACCEPTABLE_PARAMETERS;
    case tL2CAP_CONN::L2CAP_CONN_INVALID_PARAMETERS:
      return BTA_JV_L2CAP_REASON_INVALID_PARAMETERS;
    case tL2CAP_CONN::L2CAP_CONN_NO_RESOURCES:
      return BTA_JV_L2CAP_REASON_NO_RESOURCES;
    case tL2CAP_CONN::L2CAP_CONN_NO_PSM:
      return BTA_JV_L2CAP_REASON_NO_PSM;
    case tL2CAP_CONN::L2CAP_CONN_TIMEOUT:
      return BTA_JV_L2CAP_REASON_TIMEOUT;
    default:
      return BTA_JV_L2CAP_REASON_UNKNOWN;
  }
}
/******************************************************************************/

/*******************************************************************************
 *
 * Function     bta_jv_alloc_rfc_cb
 *
 * Description  allocate a control block for the given port handle
 *
 * Returns
 *
 ******************************************************************************/
tBTA_JV_RFC_CB* bta_jv_alloc_rfc_cb(uint16_t port_handle, tBTA_JV_PCB** pp_pcb) {
  tBTA_JV_RFC_CB* p_cb = NULL;
  tBTA_JV_PCB* p_pcb;
  int i, j;
  for (i = 0; i < BTA_JV_MAX_RFC_CONN; i++) {
    if (0 == bta_jv_cb.rfc_cb[i].handle) {
      p_cb = &bta_jv_cb.rfc_cb[i];
      /* mask handle to distinguish it with L2CAP handle */
      p_cb->handle = (i + 1) | BTA_JV_RFCOMM_MASK;

      p_cb->max_sess = 1;
      p_cb->curr_sess = 1;
      for (j = 0; j < BTA_JV_MAX_RFC_SR_SESSION; j++) {
        p_cb->rfc_hdl[j] = 0;
      }
      p_cb->rfc_hdl[0] = port_handle;
      log::verbose("port_handle={}, handle=0x{:x}", port_handle, p_cb->handle);

      p_pcb = &bta_jv_cb.port_cb[port_handle - 1];
      p_pcb->handle = p_cb->handle;
      p_pcb->port_handle = port_handle;
      p_pcb->p_pm_cb = NULL;
      *pp_pcb = p_pcb;
      break;
    }
  }
  if (p_cb == NULL) {
    log::error("port_handle={} ctrl block exceeds limit:{}", port_handle, BTA_JV_MAX_RFC_CONN);
  }
  return p_cb;
}

/*******************************************************************************
 *
 * Function     bta_jv_rfc_port_to_pcb
 *
 * Description  find the port control block associated with the given port
 *              handle
 *
 * Returns
 *
 ******************************************************************************/
tBTA_JV_PCB* bta_jv_rfc_port_to_pcb(uint16_t port_handle) {
  tBTA_JV_PCB* p_pcb = NULL;

  if ((port_handle > 0) && (port_handle <= MAX_RFC_PORTS) &&
      bta_jv_cb.port_cb[port_handle - 1].handle) {
    p_pcb = &bta_jv_cb.port_cb[port_handle - 1];
  }

  return p_pcb;
}

/*******************************************************************************
 *
 * Function     bta_jv_rfc_port_to_cb
 *
 * Description  find the RFCOMM control block associated with the given port
 *              handle
 *
 * Returns
 *
 ******************************************************************************/
tBTA_JV_RFC_CB* bta_jv_rfc_port_to_cb(uint16_t port_handle) {
  tBTA_JV_RFC_CB* p_cb = NULL;
  uint32_t handle;

  if ((port_handle > 0) && (port_handle <= MAX_RFC_PORTS) &&
      bta_jv_cb.port_cb[port_handle - 1].handle) {
    handle = bta_jv_cb.port_cb[port_handle - 1].handle;
    handle &= BTA_JV_RFC_HDL_MASK;
    handle &= ~BTA_JV_RFCOMM_MASK;
    if (handle) {
      p_cb = &bta_jv_cb.rfc_cb[handle - 1];
    }
  } else {
    log::warn("jv handle not found port_handle:{}", port_handle);
  }
  return p_cb;
}

static tBTA_JV_STATUS bta_jv_free_rfc_cb(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb) {
  tBTA_JV_STATUS status = tBTA_JV_STATUS::SUCCESS;
  bool remove_server = false;
  int close_pending = 0;

  if (!p_cb || !p_pcb) {
    log::error("p_cb or p_pcb cannot be null");
    return tBTA_JV_STATUS::FAILURE;
  }
  log::verbose("max_sess={}, curr_sess={}, p_pcb={}, user={}, state={}, jv handle=0x{:x}",
               p_cb->max_sess, p_cb->curr_sess, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id,
               p_pcb->state, p_pcb->handle);

  if (p_cb->curr_sess <= 0) {
    return tBTA_JV_STATUS::SUCCESS;
  }

  switch (p_pcb->state) {
    case BTA_JV_ST_CL_CLOSING:
    case BTA_JV_ST_SR_CLOSING:
      log::warn("return on closing, port state={}, scn={}, p_pcb={}, user_data={}", p_pcb->state,
                p_cb->scn, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id);
      status = tBTA_JV_STATUS::FAILURE;
      return status;
    case BTA_JV_ST_CL_OPEN:
    case BTA_JV_ST_CL_OPENING:
      log::verbose("state={}, scn={}, user_data={}", p_pcb->state, p_cb->scn,
                   p_pcb->rfcomm_slot_id);
      p_pcb->state = BTA_JV_ST_CL_CLOSING;
      break;
    case BTA_JV_ST_SR_LISTEN:
      p_pcb->state = BTA_JV_ST_SR_CLOSING;
      remove_server = true;
      log::verbose("state: BTA_JV_ST_SR_LISTEN, scn={}, user_data={}", p_cb->scn,
                   p_pcb->rfcomm_slot_id);
      break;
    case BTA_JV_ST_SR_OPEN:
      p_pcb->state = BTA_JV_ST_SR_CLOSING;
      log::verbose(": state: BTA_JV_ST_SR_OPEN, scn={} user_data={}", p_cb->scn,
                   p_pcb->rfcomm_slot_id);
      break;
    default:
      log::warn(
              "failed, ignore port state= {}, scn={}, p_pcb= {}, jv handle=0x{:x}, "
              "port_handle={}, user_data={}",
              p_pcb->state, p_cb->scn, std::format_ptr(p_pcb), p_pcb->handle, p_pcb->port_handle,
              p_pcb->rfcomm_slot_id);
      status = tBTA_JV_STATUS::FAILURE;
      break;
  }
  if (tBTA_JV_STATUS::SUCCESS == status) {
    int port_status;

    if (!remove_server) {
      port_status = RFCOMM_RemoveConnection(p_pcb->port_handle);
    } else {
      port_status = RFCOMM_RemoveServer(p_pcb->port_handle);
    }
    if (port_status != PORT_SUCCESS) {
      status = tBTA_JV_STATUS::FAILURE;
      log::warn(
              "Remove jv handle=0x{:x}, state={}, port_status={}, port_handle={}, close_pending={}",
              p_pcb->handle, p_pcb->state, port_status, p_pcb->port_handle, close_pending);
    }
  }
  if (!close_pending) {
    p_pcb->port_handle = 0;
    p_pcb->state = BTA_JV_ST_NONE;
    bta_jv_free_set_pm_profile_cb(p_pcb->handle);

    // Initialize congestion flags
    p_pcb->cong = false;
    p_pcb->rfcomm_slot_id = 0;
    int si = BTA_JV_RFC_HDL_TO_SIDX(p_pcb->handle);
    if (0 <= si && si < BTA_JV_MAX_RFC_SR_SESSION) {
      p_cb->rfc_hdl[si] = 0;
    }
    p_pcb->handle = 0;
    p_cb->curr_sess--;
    if (p_cb->curr_sess == 0) {
      p_cb->scn = 0;
      p_cb->p_cback = NULL;
      p_cb->handle = 0;
      p_cb->curr_sess = -1;
    }
  }
  return status;
}

/*******************************************************************************
 *
 * Function     bta_jv_free_l2c_cb
 *
 * Description  free the given L2CAP control block
 *
 * Returns
 *
 ******************************************************************************/
tBTA_JV_STATUS bta_jv_free_l2c_cb(tBTA_JV_L2C_CB* p_cb) {
  tBTA_JV_STATUS status = tBTA_JV_STATUS::SUCCESS;

  if (BTA_JV_ST_NONE != p_cb->state) {
    bta_jv_free_set_pm_profile_cb((uint32_t)p_cb->handle);
    if (GAP_ConnClose(p_cb->handle) != BT_PASS) {
      status = tBTA_JV_STATUS::FAILURE;
    }
  }
  p_cb->psm = 0;
  p_cb->state = BTA_JV_ST_NONE;
  p_cb->cong = false;
  bta_jv_free_sec_id(&p_cb->sec_id);
  p_cb->p_cback = NULL;
  p_cb->handle = 0;
  p_cb->l2cap_socket_id = 0;
  return status;
}

/*******************************************************************************
 *
 *
 * Function    bta_jv_clear_pm_cb
 *
 * Description clears jv pm control block and optionally calls
 *             bta_sys_conn_close()
 *             In general close_conn should be set to true to remove registering
 *             with dm pm!
 *
 * WARNING:    Make sure to clear pointer form port or l2c to this control block
 *             too!
 *
 ******************************************************************************/
static void bta_jv_clear_pm_cb(tBTA_JV_PM_CB* p_pm_cb, bool close_conn) {
  /* needs to be called if registered with bta pm, otherwise we may run out of
   * dm pm slots! */
  if (close_conn) {
    bta_sys_conn_close(BTA_ID_JV, p_pm_cb->app_id, p_pm_cb->peer_bd_addr);
  }
  p_pm_cb->state = BTA_JV_PM_FREE_ST;
  p_pm_cb->app_id = BTA_JV_PM_ALL;
  p_pm_cb->handle = BTA_JV_PM_HANDLE_CLEAR;
  p_pm_cb->peer_bd_addr = RawAddress::kEmpty;
}

/*******************************************************************************
 *
 * Function     bta_jv_free_set_pm_profile_cb
 *
 * Description  free pm profile control block
 *
 * Returns     tBTA_JV_STATUS::SUCCESS if cb has been freed correctly,
 *             tBTA_JV_STATUS::FAILURE in case of no profile has been registered
 *                                     or already freed
 *
 ******************************************************************************/
static tBTA_JV_STATUS bta_jv_free_set_pm_profile_cb(uint32_t jv_handle) {
  tBTA_JV_STATUS status = tBTA_JV_STATUS::FAILURE;
  tBTA_JV_PM_CB** p_cb;
  int i, j, bd_counter = 0, appid_counter = 0;

  for (i = 0; i < BTA_JV_PM_MAX_NUM; i++) {
    p_cb = NULL;
    if ((bta_jv_cb.pm_cb[i].state != BTA_JV_PM_FREE_ST) &&
        (jv_handle == bta_jv_cb.pm_cb[i].handle)) {
      for (j = 0; j < BTA_JV_PM_MAX_NUM; j++) {
        if (bta_jv_cb.pm_cb[j].peer_bd_addr == bta_jv_cb.pm_cb[i].peer_bd_addr) {
          bd_counter++;
        }
        if (bta_jv_cb.pm_cb[j].app_id == bta_jv_cb.pm_cb[i].app_id) {
          appid_counter++;
        }
      }

      log::verbose("jv_handle=0x{:x}, idx={}app_id={}, bd_counter={}, appid_counter={}", jv_handle,
                   i, bta_jv_cb.pm_cb[i].app_id, bd_counter, appid_counter);
      if (bd_counter > 1) {
        bta_jv_pm_conn_idle(&bta_jv_cb.pm_cb[i]);
      }

      if (bd_counter <= 1 || (appid_counter <= 1)) {
        bta_jv_clear_pm_cb(&bta_jv_cb.pm_cb[i], true);
      } else {
        bta_jv_clear_pm_cb(&bta_jv_cb.pm_cb[i], false);
      }

      if (BTA_JV_RFCOMM_MASK & jv_handle) {
        uint32_t hi = ((jv_handle & BTA_JV_RFC_HDL_MASK) & ~BTA_JV_RFCOMM_MASK) - 1;
        uint32_t si = BTA_JV_RFC_HDL_TO_SIDX(jv_handle);
        if (hi < BTA_JV_MAX_RFC_CONN && bta_jv_cb.rfc_cb[hi].p_cback &&
            si < BTA_JV_MAX_RFC_SR_SESSION && bta_jv_cb.rfc_cb[hi].rfc_hdl[si]) {
          tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(bta_jv_cb.rfc_cb[hi].rfc_hdl[si]);
          if (p_pcb) {
            if (NULL == p_pcb->p_pm_cb) {
              log::warn("jv_handle=0x{:x}, port_handle={}, i={}, no link to pm_cb?", jv_handle,
                        p_pcb->port_handle, i);
            }
            p_cb = &p_pcb->p_pm_cb;
          }
        }
      } else {
        if (jv_handle < BTA_JV_MAX_L2C_CONN) {
          tBTA_JV_L2C_CB* p_l2c_cb = &bta_jv_cb.l2c_cb[jv_handle];
          if (NULL == p_l2c_cb->p_pm_cb) {
            log::warn("jv_handle=0x{:x}, i={} no link to pm_cb?", jv_handle, i);
          }
          p_cb = &p_l2c_cb->p_pm_cb;
        }
      }
      if (p_cb) {
        *p_cb = NULL;
        status = tBTA_JV_STATUS::SUCCESS;
      }
    }
  }
  return status;
}

/*******************************************************************************
 *
 * Function    bta_jv_alloc_set_pm_profile_cb
 *
 * Description set PM profile control block
 *
 * Returns     pointer to allocated cb or NULL in case of failure
 *
 ******************************************************************************/
static tBTA_JV_PM_CB* bta_jv_alloc_set_pm_profile_cb(uint32_t jv_handle, tBTA_JV_PM_ID app_id) {
  bool bRfcHandle = (jv_handle & BTA_JV_RFCOMM_MASK) != 0;
  RawAddress peer_bd_addr = RawAddress::kEmpty;
  int i, j;
  tBTA_JV_PM_CB** pp_cb;

  for (i = 0; i < BTA_JV_PM_MAX_NUM; i++) {
    pp_cb = NULL;
    if (bta_jv_cb.pm_cb[i].state == BTA_JV_PM_FREE_ST) {
      /* rfc handle bd addr retrieval requires core stack handle */
      if (bRfcHandle) {
        for (j = 0; j < BTA_JV_MAX_RFC_CONN; j++) {
          if (jv_handle == bta_jv_cb.port_cb[j].handle) {
            pp_cb = &bta_jv_cb.port_cb[j].p_pm_cb;
            if (PORT_SUCCESS !=
                PORT_CheckConnection(bta_jv_cb.port_cb[j].port_handle, &peer_bd_addr, NULL)) {
              i = BTA_JV_PM_MAX_NUM;
            }
            break;
          }
        }
      } else {
        /* use jv handle for l2cap bd address retrieval */
        for (j = 0; j < BTA_JV_MAX_L2C_CONN; j++) {
          if (jv_handle == bta_jv_cb.l2c_cb[j].handle) {
            pp_cb = &bta_jv_cb.l2c_cb[j].p_pm_cb;
            const RawAddress* p_bd_addr = GAP_ConnGetRemoteAddr((uint16_t)jv_handle);
            if (p_bd_addr) {
              peer_bd_addr = *p_bd_addr;
            } else {
              i = BTA_JV_PM_MAX_NUM;
            }
            break;
          }
        }
      }
      log::verbose("handle=0x{:x}, app_id={}, idx={}, BTA_JV_PM_MAX_NUM={}, pp_cb={}", jv_handle,
                   app_id, i, BTA_JV_PM_MAX_NUM, std::format_ptr(pp_cb));
      break;
    }
  }

  if ((i != BTA_JV_PM_MAX_NUM) && (NULL != pp_cb)) {
    *pp_cb = &bta_jv_cb.pm_cb[i];
    bta_jv_cb.pm_cb[i].handle = jv_handle;
    bta_jv_cb.pm_cb[i].app_id = app_id;
    bta_jv_cb.pm_cb[i].peer_bd_addr = peer_bd_addr;
    bta_jv_cb.pm_cb[i].state = BTA_JV_PM_IDLE_ST;
    return &bta_jv_cb.pm_cb[i];
  }
  log::warn("handle=0x{:x}, app_id={}, return NULL", jv_handle, app_id);
  return NULL;
}

/*******************************************************************************
 *
 * Function     bta_jv_check_psm
 *
 * Description  for now use only the legal PSM per JSR82 spec
 *
 * Returns      true, if allowed
 *
 ******************************************************************************/
bool bta_jv_check_psm(uint16_t psm) {
  bool ret = false;

  if (L2C_IS_VALID_PSM(psm)) {
    if (psm < 0x1001) {
      /* see if this is defined by spec */
      switch (psm) {
        case BT_PSM_SDP:
        case BT_PSM_RFCOMM: /* 3 */
          /* do not allow java app to use these 2 PSMs */
          break;

        case BT_PSM_TCS:
        case BT_PSM_CTP:
          if (!bta_sys_is_register(BTA_ID_CT) && !bta_sys_is_register(BTA_ID_CG)) {
            ret = true;
          }
          break;

        case BT_PSM_BNEP: /* F */
          if (!bta_sys_is_register(BTA_ID_PAN)) {
            ret = true;
          }
          break;

        case BT_PSM_HIDC:
        case BT_PSM_HIDI:
          // FIX: allow HID Device and HID Host to coexist
          if (!bta_sys_is_register(BTA_ID_HD) || !bta_sys_is_register(BTA_ID_HH)) {
            ret = true;
          }
          break;

        case BT_PSM_AVCTP: /* 0x17 */
        case BT_PSM_AVDTP: /* 0x19 */
          if (!bta_sys_is_register(BTA_ID_AV)) {
            ret = true;
          }
          break;

        default:
          ret = true;
          break;
      }
    } else {
      ret = true;
    }
  }
  return ret;
}

/* Initialises the JAVA I/F */
void bta_jv_enable(tBTA_JV_DM_CBACK* p_cback) {
  bta_jv_cb.p_dm_cback = p_cback;
  if (bta_jv_cb.p_dm_cback) {
    tBTA_JV bta_jv = {
            .status = tBTA_JV_STATUS::SUCCESS,
    };
    bta_jv_cb.p_dm_cback(BTA_JV_ENABLE_EVT, &bta_jv, 0);
  }
  memset(bta_jv_cb.free_psm_list, 0, sizeof(bta_jv_cb.free_psm_list));
  memset(bta_jv_cb.scn_in_use, 0, sizeof(bta_jv_cb.scn_in_use));
  bta_jv_cb.scn_search_index = 1;
}

/** Disables the BT device manager free the resources used by java */
void bta_jv_disable() { log::info(""); }

/**
 * We keep a list of PSM's that have been freed from JAVA, for reuse.
 * This function will return a free PSM, and delete it from the free
 * list.
 * If no free PSMs exist, 0 will be returned.
 */
static uint16_t bta_jv_get_free_psm() {
  const int cnt = sizeof(bta_jv_cb.free_psm_list) / sizeof(bta_jv_cb.free_psm_list[0]);
  for (int i = 0; i < cnt; i++) {
    uint16_t psm = bta_jv_cb.free_psm_list[i];
    if (psm != 0) {
      log::verbose("Reusing PSM=0x{:x}", psm);
      bta_jv_cb.free_psm_list[i] = 0;
      return psm;
    }
  }
  return 0;
}

static void bta_jv_set_free_psm(uint16_t psm) {
  int free_index = -1;
  const int cnt = sizeof(bta_jv_cb.free_psm_list) / sizeof(bta_jv_cb.free_psm_list[0]);
  for (int i = 0; i < cnt; i++) {
    if (bta_jv_cb.free_psm_list[i] == 0) {
      free_index = i;
    } else if (psm == bta_jv_cb.free_psm_list[i]) {
      return;  // PSM already freed?
    }
  }
  if (free_index != -1) {
    bta_jv_cb.free_psm_list[free_index] = psm;
    log::verbose("Recycling PSM=0x{:x}", psm);
  } else {
    log::error("unable to free psm=0x{:x} no more free slots", psm);
  }
}

static uint16_t bta_jv_allocate_l2cap_classic_psm() {
  bool done = false;
  uint16_t psm = bta_jv_cb.dyn_psm;

  while (!done) {
    psm += 2;
    if (psm > 0xfeff) {
      psm = 0x1001;
    } else if (psm & 0x0100) {
      /* the upper byte must be even */
      psm += 0x0100;
    }

    /* if psm is in range of reserved BRCM Aware features */
    if ((BRCM_RESERVED_PSM_START <= psm) && (psm <= BRCM_RESERVED_PSM_END)) {
      continue;
    }

    /* make sure the newlly allocated psm is not used right now */
    if (used_l2cap_classic_dynamic_psm.count(psm) == 0) {
      done = true;
    }
  }
  bta_jv_cb.dyn_psm = psm;

  return psm;
}

/** Obtain a free SCN (Server Channel Number) (RFCOMM channel or L2CAP PSM) */
void bta_jv_get_channel_id(tBTA_JV_CONN_TYPE type /* One of BTA_JV_CONN_TYPE_ */,
                           int32_t channel /* optionally request a specific channel */,
                           uint32_t l2cap_socket_id, uint32_t rfcomm_slot_id) {
  uint16_t psm = 0;

  switch (type) {
    case tBTA_JV_CONN_TYPE::RFCOMM: {
      uint8_t scn = 0;
      if (channel > 0) {
        if (BTA_TryAllocateSCN(channel)) {
          scn = static_cast<uint8_t>(channel);
        } else {
          log::error("rfc channel {} already in use or invalid", channel);
        }
      } else {
        scn = BTA_AllocateSCN();
        if (scn == 0) {
          log::error("out of rfc channels");
        }
      }
      if (bta_jv_cb.p_dm_cback) {
        tBTA_JV bta_jv;
        bta_jv.scn = scn;
        bta_jv_cb.p_dm_cback(BTA_JV_GET_SCN_EVT, &bta_jv, rfcomm_slot_id);
      }
      return;
    }
    case tBTA_JV_CONN_TYPE::L2CAP:
      psm = bta_jv_get_free_psm();
      if (psm == 0) {
        psm = bta_jv_allocate_l2cap_classic_psm();
        log::verbose("returned PSM=0x{:x}", psm);
      }
      break;
    case tBTA_JV_CONN_TYPE::L2CAP_LE:
      psm = stack::l2cap::get_interface().L2CA_AllocateLePSM();
      if (psm == 0) {
        log::error("Error: No free LE PSM available");
      }
      break;
    default:
      break;
  }

  if (bta_jv_cb.p_dm_cback) {
    tBTA_JV bta_jv;
    bta_jv.psm = psm;
    bta_jv_cb.p_dm_cback(BTA_JV_GET_PSM_EVT, &bta_jv, l2cap_socket_id);
  }
}

/** free a SCN */
void bta_jv_free_scn(tBTA_JV_CONN_TYPE type /* One of BTA_JV_CONN_TYPE_ */, uint16_t scn) {
  switch (type) {
    case tBTA_JV_CONN_TYPE::RFCOMM:
      BTA_FreeSCN(scn);
      break;
    case tBTA_JV_CONN_TYPE::L2CAP:
      bta_jv_set_free_psm(scn);
      break;
    case tBTA_JV_CONN_TYPE::L2CAP_LE:
      log::verbose("type=BTA_JV_CONN_TYPE::L2CAP_LE. psm={}", scn);
      stack::l2cap::get_interface().L2CA_FreeLePSM(scn);
      break;
    default:
      break;
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_start_discovery_cback
 *
 * Description  Callback for Start Discovery
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_start_discovery_cback(uint32_t rfcomm_slot_id, const RawAddress& bd_addr,
                                         tSDP_RESULT result) {
  if (!bta_jv_cb.sdp_cb.sdp_active) {
    log::warn("Received unexpected service discovery callback bd_addr:{} result:{}", bd_addr,
              sdp_result_text(result), bta_jv_cb.sdp_cb.sdp_active);
  }
  if (bta_jv_cb.sdp_cb.bd_addr != bta_jv_cb.sdp_cb.bd_addr) {
    log::warn(
            "Received incorrect service discovery callback expected_bd_addr:{} "
            "actual_bd_addr:{} result:{}",
            bta_jv_cb.sdp_cb.bd_addr, bd_addr, sdp_result_text(result),
            bta_jv_cb.sdp_cb.sdp_active);
  }

  if (bta_jv_cb.p_dm_cback) {
    tBTA_JV bta_jv = {
            .disc_comp =
                    {
                            .status = tBTA_JV_STATUS::FAILURE,
                            .scn = 0,
                    },
    };
    if (result == tSDP_STATUS::SDP_SUCCESS || result == tSDP_STATUS::SDP_DB_FULL) {
      log::info("Received service discovery callback success bd_addr:{} result:{}", bd_addr,
                sdp_result_text(result));
      tSDP_PROTOCOL_ELEM pe;
      tSDP_DISC_REC* p_sdp_rec = NULL;
      p_sdp_rec = get_legacy_stack_sdp_api()->db.SDP_FindServiceUUIDInDb(
              p_bta_jv_cfg->p_sdp_db, bta_jv_cb.sdp_cb.uuid, p_sdp_rec);
      log::verbose("bta_jv_cb.uuid={} p_sdp_rec={}", bta_jv_cb.sdp_cb.uuid,
                   std::format_ptr(p_sdp_rec));
      if (p_sdp_rec && get_legacy_stack_sdp_api()->record.SDP_FindProtocolListElemInRec(
                               p_sdp_rec, UUID_PROTOCOL_RFCOMM, &pe)) {
        bta_jv = {
                .disc_comp =
                        {
                                .status = tBTA_JV_STATUS::SUCCESS,
                                .scn = (uint8_t)pe.params[0],
                        },
        };
      }
    } else {
      log::warn("Received service discovery callback failed bd_addr:{} result:{}", bd_addr,
                sdp_result_text(result));
    }
    log::info("Issuing service discovery complete callback bd_addr:{} result:{} status:{} scn:{}",
              bd_addr, sdp_result_text(result), bta_jv_status_text(bta_jv.disc_comp.status),
              bta_jv.disc_comp.scn);
    bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, &bta_jv, rfcomm_slot_id);
  } else {
    log::warn("Received service discovery callback when disabled bd_addr:{} result:{}", bd_addr,
              sdp_result_text(result));
  }
  bta_jv_cb.sdp_cb = {};
}

/* Discovers services on a remote device */
void bta_jv_start_discovery(const RawAddress& bd_addr, uint16_t num_uuid,
                            bluetooth::Uuid* uuid_list, uint32_t rfcomm_slot_id) {
  log::assert_that(uuid_list != nullptr, "assert failed: uuid_list != nullptr");
  if (bta_jv_cb.sdp_cb.sdp_active) {
    log::warn(
            "Unable to start discovery as already in progress active_bd_addr{} "
            "request_bd_addr:{} num:uuid:{} rfcomm_slot_id:{}",
            bta_jv_cb.sdp_cb.bd_addr, bd_addr, num_uuid, rfcomm_slot_id);
    if (bta_jv_cb.p_dm_cback) {
      tBTA_JV bta_jv = {
              .status = tBTA_JV_STATUS::BUSY,
      };
      bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, &bta_jv, rfcomm_slot_id);
    } else {
      log::warn(
              "bta::jv module DISABLED so unable to inform caller service discovery is "
              "unavailable");
    }
    return;
  }

  /* init the database/set up the filter */
  if (!get_legacy_stack_sdp_api()->service.SDP_InitDiscoveryDb(
              p_bta_jv_cfg->p_sdp_db, p_bta_jv_cfg->sdp_db_size, num_uuid, uuid_list, 0, NULL)) {
    log::warn("Unable to initialize service discovery db bd_addr:{} num:uuid:{} rfcomm_slot_id:{}",
              bd_addr, num_uuid, rfcomm_slot_id);
  }

  /* tell SDP to keep the raw data */
  p_bta_jv_cfg->p_sdp_db->raw_data = p_bta_jv_cfg->p_sdp_raw_data;
  p_bta_jv_cfg->p_sdp_db->raw_size = p_bta_jv_cfg->sdp_raw_size;

  // Optimistically set this as active
  bta_jv_cb.sdp_cb = {
          .sdp_active = true,
          .bd_addr = bd_addr,
          .uuid = uuid_list[0],
  };

  if (!get_legacy_stack_sdp_api()->service.SDP_ServiceSearchAttributeRequest2(
              bd_addr, p_bta_jv_cfg->p_sdp_db,
              base::BindRepeating(&bta_jv_start_discovery_cback, rfcomm_slot_id))) {
    bta_jv_cb.sdp_cb = {};
    log::warn("Unable to original service discovery bd_addr:{} num:uuid:{} rfcomm_slot_id:{}",
              bd_addr, num_uuid, rfcomm_slot_id);
    /* failed to start SDP. report the failure right away */
    if (bta_jv_cb.p_dm_cback) {
      tBTA_JV bta_jv = {
              .status = tBTA_JV_STATUS::FAILURE,
      };
      bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, &bta_jv, rfcomm_slot_id);
    } else {
      log::warn("No callback set for discovery complete event");
    }
  } else {
    log::info("Started service discovery bd_addr:{} num_uuid:{} rfcomm_slot_id:{}", bd_addr,
              num_uuid, rfcomm_slot_id);
  }
}

void bta_jv_cancel_discovery(uint32_t rfcomm_slot_id) {
  if (!bta_jv_cb.sdp_cb.sdp_active) {
    log::error("Canceling discovery but discovery is not active");
    return;
  }
  if (!get_legacy_stack_sdp_api()->service.SDP_CancelServiceSearch(p_bta_jv_cfg->p_sdp_db)) {
    log::error("Failed to cancel discovery, clean up the control block anyway");
    bta_jv_cb.sdp_cb = {};
    /* Send complete event right away as we might not receive callback from stack */
    if (bta_jv_cb.p_dm_cback) {
      tBTA_JV bta_jv = {
              .status = tBTA_JV_STATUS::FAILURE,
      };
      bta_jv_cb.p_dm_cback(BTA_JV_DISCOVERY_COMP_EVT, &bta_jv, rfcomm_slot_id);
    } else {
      log::warn("No callback set for discovery complete event");
    }
  } else {
    log::info("Canceled discovery");
  }
}

/* Create an SDP record with the given attributes */
void bta_jv_create_record(uint32_t rfcomm_slot_id) {
  tBTA_JV_CREATE_RECORD evt_data;
  evt_data.status = tBTA_JV_STATUS::SUCCESS;
  if (bta_jv_cb.p_dm_cback) {
    // callback immediately to create the sdp record in stack thread context
    tBTA_JV bta_jv;
    bta_jv.create_rec = evt_data;
    bta_jv_cb.p_dm_cback(BTA_JV_CREATE_RECORD_EVT, &bta_jv, rfcomm_slot_id);
  }
}

/* Delete an SDP record */
void bta_jv_delete_record(uint32_t handle) {
  if (handle) {
    /* this is a record created by btif layer*/
    if (!get_legacy_stack_sdp_api()->handle.SDP_DeleteRecord(handle)) {
      log::warn("Unable to delete  SDP record handle:{}", handle);
    }
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_l2cap_client_cback
 *
 * Description  handles the l2cap client events
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_l2cap_client_cback(uint16_t gap_handle, uint16_t event, tGAP_CB_DATA* data) {
  tBTA_JV_L2C_CB* p_cb = &bta_jv_cb.l2c_cb[gap_handle];
  tBTA_JV evt_data;

  if (gap_handle >= BTA_JV_MAX_L2C_CONN && !p_cb->p_cback) {
    return;
  }

  log::verbose("gap_handle={}, evt=0x{:x}", gap_handle, event);
  evt_data.l2c_open.status = tBTA_JV_STATUS::SUCCESS;
  evt_data.l2c_open.handle = gap_handle;

  switch (event) {
    case GAP_EVT_CONN_OPENED:
      evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
      evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
      if (data != nullptr) {
        evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid;
        evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid;
      }
      p_cb->state = BTA_JV_ST_CL_OPEN;
      p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->l2cap_socket_id);
      break;

    case GAP_EVT_CONN_CLOSED:
      p_cb->state = BTA_JV_ST_NONE;
      bta_jv_free_sec_id(&p_cb->sec_id);
      evt_data.l2c_close.async = true;
      evt_data.l2c_close.reason = data != nullptr ? bta_jv_from_gap_l2cap_err(data->l2cap_result)
                                                  : BTA_JV_L2CAP_REASON_EMPTY;
      p_cb->p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt_data, p_cb->l2cap_socket_id);
      p_cb->p_cback = NULL;
      break;

    case GAP_EVT_CONN_DATA_AVAIL:
      evt_data.data_ind.handle = gap_handle;
      /* Reset idle timer to avoid requesting sniff mode while receiving data */
      bta_jv_pm_conn_busy(p_cb->p_pm_cb);
      p_cb->p_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, p_cb->l2cap_socket_id);
      bta_jv_pm_conn_idle(p_cb->p_pm_cb);
      break;

    case GAP_EVT_TX_EMPTY:
      bta_jv_pm_conn_idle(p_cb->p_pm_cb);
      break;

    case GAP_EVT_CONN_CONGESTED:
    case GAP_EVT_CONN_UNCONGESTED:
      p_cb->cong = (event == GAP_EVT_CONN_CONGESTED) ? true : false;
      evt_data.l2c_cong.cong = p_cb->cong;
      p_cb->p_cback(BTA_JV_L2CAP_CONG_EVT, &evt_data, p_cb->l2cap_socket_id);
      break;

    default:
      break;
  }
}

/* makes an l2cap client connection */
void bta_jv_l2cap_connect(tBTA_JV_CONN_TYPE type, tBTA_SEC sec_mask, uint16_t remote_psm,
                          uint16_t rx_mtu, const RawAddress& peer_bd_addr,
                          std::unique_ptr<tL2CAP_CFG_INFO> cfg_param,
                          std::unique_ptr<tL2CAP_ERTM_INFO> ertm_info, tBTA_JV_L2CAP_CBACK* p_cback,
                          uint32_t l2cap_socket_id) {
  uint16_t handle = GAP_INVALID_HANDLE;

  tL2CAP_CFG_INFO cfg;
  memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
  if (cfg_param) {
    cfg = *cfg_param;
  }

  /* We need to use this value for MTU to be able to handle cases where cfg is
   * not set in req. */
  cfg.mtu_present = true;
  cfg.mtu = rx_mtu;

  uint8_t sec_id = bta_jv_alloc_sec_id();
  tBTA_JV_L2CAP_CL_INIT evt_data;
  evt_data.sec_id = sec_id;
  evt_data.status = tBTA_JV_STATUS::FAILURE;

  if (sec_id) {
    /* PSM checking is not required for LE COC */
    if ((type != tBTA_JV_CONN_TYPE::L2CAP) || (bta_jv_check_psm(remote_psm))) /* allowed */
    {
      // Given a client socket type
      // return the associated transport
      const tBT_TRANSPORT transport = [](tBTA_JV_CONN_TYPE type) -> tBT_TRANSPORT {
        switch (type) {
          case tBTA_JV_CONN_TYPE::L2CAP:
            return BT_TRANSPORT_BR_EDR;
          case tBTA_JV_CONN_TYPE::L2CAP_LE:
            return BT_TRANSPORT_LE;
          case tBTA_JV_CONN_TYPE::RFCOMM:
          default:
            break;
        }
        log::warn("Unexpected socket type:{}", bta_jv_conn_type_text(type));
        return BT_TRANSPORT_AUTO;
      }(type);

      uint16_t max_mps = 0xffff;  // Let GAP_ConnOpen set the max_mps.
      handle = GAP_ConnOpen("", sec_id, 0, &peer_bd_addr, remote_psm, max_mps, &cfg,
                            ertm_info.get(), sec_mask, bta_jv_l2cap_client_cback, transport);
      if (handle != GAP_INVALID_HANDLE) {
        evt_data.status = tBTA_JV_STATUS::SUCCESS;
      }
    }
  }

  if (evt_data.status == tBTA_JV_STATUS::SUCCESS) {
    tBTA_JV_L2C_CB* p_cb;
    p_cb = &bta_jv_cb.l2c_cb[handle];
    p_cb->handle = handle;
    p_cb->p_cback = p_cback;
    p_cb->l2cap_socket_id = l2cap_socket_id;
    p_cb->psm = 0; /* not a server */
    p_cb->sec_id = sec_id;
    p_cb->state = BTA_JV_ST_CL_OPENING;
  } else {
    bta_jv_free_sec_id(&sec_id);
  }

  evt_data.handle = handle;
  if (p_cback) {
    tBTA_JV bta_jv;
    bta_jv.l2c_cl_init = evt_data;
    p_cback(BTA_JV_L2CAP_CL_INIT_EVT, &bta_jv, l2cap_socket_id);
  }
}

/** Close an L2CAP client connection */
void bta_jv_l2cap_close(uint32_t handle, tBTA_JV_L2C_CB* p_cb) {
  tBTA_JV_L2CAP_CLOSE evt_data;
  tBTA_JV_L2CAP_CBACK* p_cback = p_cb->p_cback;
  uint32_t l2cap_socket_id = p_cb->l2cap_socket_id;

  evt_data.handle = handle;
  evt_data.status = bta_jv_free_l2c_cb(p_cb);
  evt_data.async = false;

  if (p_cback) {
    tBTA_JV bta_jv;
    bta_jv.l2c_close = evt_data;
    p_cback(BTA_JV_L2CAP_CLOSE_EVT, &bta_jv, l2cap_socket_id);
  }
}

/*******************************************************************************
 *
 * Function         bta_jv_l2cap_server_cback
 *
 * Description      handles the l2cap server callback
 *
 * Returns          void
 *
 ******************************************************************************/
static void bta_jv_l2cap_server_cback(uint16_t gap_handle, uint16_t event, tGAP_CB_DATA* data) {
  tBTA_JV_L2C_CB* p_cb = &bta_jv_cb.l2c_cb[gap_handle];
  tBTA_JV evt_data;
  tBTA_JV_L2CAP_CBACK* p_cback;
  uint32_t socket_id;

  if (gap_handle >= BTA_JV_MAX_L2C_CONN && !p_cb->p_cback) {
    return;
  }

  log::verbose("gap_handle={}, evt=0x{:x}", gap_handle, event);
  evt_data.l2c_open.status = tBTA_JV_STATUS::SUCCESS;
  evt_data.l2c_open.handle = gap_handle;

  switch (event) {
    case GAP_EVT_CONN_OPENED:
      evt_data.l2c_open.rem_bda = *GAP_ConnGetRemoteAddr(gap_handle);
      evt_data.l2c_open.tx_mtu = GAP_ConnGetRemMtuSize(gap_handle);
      if (data != nullptr) {
        evt_data.l2c_open.local_cid = data->l2cap_cids.local_cid;
        evt_data.l2c_open.remote_cid = data->l2cap_cids.remote_cid;
      }
      p_cb->state = BTA_JV_ST_SR_OPEN;
      p_cb->p_cback(BTA_JV_L2CAP_OPEN_EVT, &evt_data, p_cb->l2cap_socket_id);
      break;

    case GAP_EVT_CONN_CLOSED:
      evt_data.l2c_close.async = true;
      evt_data.l2c_close.handle = p_cb->handle;
      p_cback = p_cb->p_cback;
      socket_id = p_cb->l2cap_socket_id;
      evt_data.l2c_close.status = bta_jv_free_l2c_cb(p_cb);
      p_cback(BTA_JV_L2CAP_CLOSE_EVT, &evt_data, socket_id);
      break;

    case GAP_EVT_CONN_DATA_AVAIL:
      evt_data.data_ind.handle = gap_handle;
      /* Reset idle timer to avoid requesting sniff mode while receiving data */
      bta_jv_pm_conn_busy(p_cb->p_pm_cb);
      p_cb->p_cback(BTA_JV_L2CAP_DATA_IND_EVT, &evt_data, p_cb->l2cap_socket_id);
      bta_jv_pm_conn_idle(p_cb->p_pm_cb);
      break;

    case GAP_EVT_TX_EMPTY:
      bta_jv_pm_conn_idle(p_cb->p_pm_cb);
      break;

    case GAP_EVT_CONN_CONGESTED:
    case GAP_EVT_CONN_UNCONGESTED:
      p_cb->cong = (event == GAP_EVT_CONN_CONGESTED) ? true : false;
      evt_data.l2c_cong.cong = p_cb->cong;
      p_cb->p_cback(BTA_JV_L2CAP_CONG_EVT, &evt_data, p_cb->l2cap_socket_id);
      break;

    default:
      break;
  }
}

/** starts an L2CAP server */
void bta_jv_l2cap_start_server(tBTA_JV_CONN_TYPE type, tBTA_SEC sec_mask, uint16_t local_psm,
                               uint16_t rx_mtu, std::unique_ptr<tL2CAP_CFG_INFO> cfg_param,
                               std::unique_ptr<tL2CAP_ERTM_INFO> ertm_info,
                               tBTA_JV_L2CAP_CBACK* p_cback, uint32_t l2cap_socket_id) {
  uint16_t handle;
  tBTA_JV_L2CAP_START evt_data;

  tL2CAP_CFG_INFO cfg;
  memset(&cfg, 0, sizeof(tL2CAP_CFG_INFO));
  if (cfg_param) {
    cfg = *cfg_param;
  }

  // FIX: MTU=0 means not present
  if (rx_mtu > 0) {
    cfg.mtu_present = true;
    cfg.mtu = rx_mtu;
  } else {
    cfg.mtu_present = false;
    cfg.mtu = 0;
  }

  uint8_t sec_id = bta_jv_alloc_sec_id();
  uint16_t max_mps = 0xffff;  // Let GAP_ConnOpen set the max_mps.
  /* PSM checking is not required for LE COC */

  // Given a server socket type
  // return the associated transport
  const tBT_TRANSPORT transport = [](tBTA_JV_CONN_TYPE type) -> tBT_TRANSPORT {
    switch (type) {
      case tBTA_JV_CONN_TYPE::L2CAP:
        return BT_TRANSPORT_BR_EDR;
      case tBTA_JV_CONN_TYPE::L2CAP_LE:
        return BT_TRANSPORT_LE;
      case tBTA_JV_CONN_TYPE::RFCOMM:
      default:
        break;
    }
    log::warn("Unexpected socket type:{}", bta_jv_conn_type_text(type));
    return BT_TRANSPORT_AUTO;
  }(type);

  if (0 == sec_id || ((type == tBTA_JV_CONN_TYPE::L2CAP) && (!bta_jv_check_psm(local_psm))) ||
      (handle = GAP_ConnOpen("JV L2CAP", sec_id, 1, nullptr, local_psm, max_mps, &cfg,
                             ertm_info.get(), sec_mask, bta_jv_l2cap_server_cback, transport)) ==
              GAP_INVALID_HANDLE) {
    bta_jv_free_sec_id(&sec_id);
    evt_data.status = tBTA_JV_STATUS::FAILURE;
  } else {
    tBTA_JV_L2C_CB* p_cb = &bta_jv_cb.l2c_cb[handle];
    evt_data.status = tBTA_JV_STATUS::SUCCESS;
    evt_data.handle = handle;
    evt_data.sec_id = sec_id;
    p_cb->p_cback = p_cback;
    p_cb->l2cap_socket_id = l2cap_socket_id;
    p_cb->handle = handle;
    p_cb->sec_id = sec_id;
    p_cb->state = BTA_JV_ST_SR_LISTEN;
    p_cb->psm = local_psm;
  }

  if (p_cback) {
    tBTA_JV bta_jv;
    bta_jv.l2c_start = evt_data;
    p_cback(BTA_JV_L2CAP_START_EVT, &bta_jv, l2cap_socket_id);
  }
}

/* stops an L2CAP server */
void bta_jv_l2cap_stop_server(uint16_t /* local_psm */, uint32_t l2cap_socket_id) {
  for (int i = 0; i < BTA_JV_MAX_L2C_CONN; i++) {
    if (bta_jv_cb.l2c_cb[i].l2cap_socket_id == l2cap_socket_id) {
      tBTA_JV_L2C_CB* p_cb = &bta_jv_cb.l2c_cb[i];
      tBTA_JV_L2CAP_CBACK* p_cback = p_cb->p_cback;
      tBTA_JV_L2CAP_CLOSE evt_data;
      evt_data.handle = p_cb->handle;
      evt_data.status = bta_jv_free_l2c_cb(p_cb);
      evt_data.async = false;
      if (p_cback) {
        tBTA_JV bta_jv;
        bta_jv.l2c_close = evt_data;
        p_cback(BTA_JV_L2CAP_CLOSE_EVT, &bta_jv, l2cap_socket_id);
      }
      break;
    }
  }
}

/* Write data to an L2CAP connection */
void bta_jv_l2cap_write(uint32_t handle, uint32_t req_id, BT_HDR* msg, uint32_t user_id,
                        tBTA_JV_L2C_CB* p_cb) {
  /* As we check this callback exists before the tBTA_JV_API_L2CAP_WRITE can be
   * send through the API this check should not be needed. But the API is not
   * designed to be used (safely at least) in a multi-threaded scheduler, hence
   * if the peer device disconnects the l2cap link after the API is called, but
   * before this message is handled, the ->p_cback will be cleared at this
   * point. At first glanch this seems highly unlikely, but for all
   * obex-profiles with two channels connected - e.g. MAP, this happens around 1
   * of 4 disconnects, as a disconnect on the server channel causes a disconnect
   * to be send on the client (notification) channel, but at the peer typically
   * disconnects both the OBEX disconnect request crosses the incoming l2cap
   * disconnect. If p_cback is cleared, we simply discard the data. RISK: The
   * caller must handle any cleanup based on another signal than
   * BTA_JV_L2CAP_WRITE_EVT, which is typically not possible, as the pointer to
   * the allocated buffer is stored in this message, and can therefore not be
   * freed, hence we have a mem-leak-by-design.*/
  if (!p_cb->p_cback) {
    /* As this pointer is checked in the API function, this occurs only when the
     * channel is disconnected after the API function is called, but before the
     * message is handled. */
    log::error("p_cb->p_cback == NULL");
    osi_free(msg);
    return;
  }

  tBTA_JV_L2CAP_WRITE evt_data;
  evt_data.status = tBTA_JV_STATUS::FAILURE;
  evt_data.handle = handle;
  evt_data.req_id = req_id;
  evt_data.cong = p_cb->cong;
  evt_data.len = msg->len;

  bta_jv_pm_conn_busy(p_cb->p_pm_cb);

  // TODO: this was set only for non-fixed channel packets. Is that needed ?
  msg->event = BT_EVT_TO_BTU_SP_DATA;

  if (evt_data.cong) {
    osi_free(msg);
  } else {
    if (GAP_ConnWriteData(handle, msg) == BT_PASS) {
      evt_data.status = tBTA_JV_STATUS::SUCCESS;
    }
  }

  tBTA_JV bta_jv;
  bta_jv.l2c_write = evt_data;
  p_cb->p_cback(BTA_JV_L2CAP_WRITE_EVT, &bta_jv, user_id);
}

/*******************************************************************************
 *
 * Function     bta_jv_port_data_co_cback
 *
 * Description  port data callback function of rfcomm
 *              connections
 *
 * Returns      void
 *
 ******************************************************************************/
static int bta_jv_port_data_co_cback(uint16_t port_handle, uint8_t* buf, uint16_t len, int type) {
  tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
  tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
  log::verbose("p_cb={}, p_pcb={}, len={}, type={}", std::format_ptr(p_cb), std::format_ptr(p_pcb),
               len, type);
  if (p_pcb != NULL) {
    switch (type) {
      case DATA_CO_CALLBACK_TYPE_INCOMING:
        // Reset sniff timer when receiving data by sysproxy
        if (osi_property_get_bool("bluetooth.rfcomm.sysproxy.rx.exit_sniff", false)) {
          bta_jv_reset_sniff_timer(p_pcb->p_pm_cb);
        }
        return bta_co_rfc_data_incoming(p_pcb->rfcomm_slot_id, (BT_HDR*)buf);
      case DATA_CO_CALLBACK_TYPE_OUTGOING_SIZE:
        return bta_co_rfc_data_outgoing_size(p_pcb->rfcomm_slot_id, (int*)buf);
      case DATA_CO_CALLBACK_TYPE_OUTGOING:
        return bta_co_rfc_data_outgoing(p_pcb->rfcomm_slot_id, buf, len);
      default:
        log::error("unknown callout type={}", type);
        break;
    }
  }
  return 0;
}

/*******************************************************************************
 *
 * Function     bta_jv_port_mgmt_cl_cback
 *
 * Description  callback for port mamangement function of rfcomm
 *              client connections
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_port_mgmt_cl_cback(const tPORT_RESULT code, uint16_t port_handle) {
  tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
  tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
  RawAddress rem_bda = RawAddress::kEmpty;
  uint16_t lcid;
  tBTA_JV_RFCOMM_CBACK* p_cback; /* the callback function */

  if (p_cb == NULL) {
    log::warn("p_cb is NULL, code={}, port_handle={}", code, port_handle);
    return;
  } else if (p_cb->p_cback == NULL) {
    log::warn("p_cb->p_cback is null, code={}, port_handle={}", code, port_handle);
    return;
  }

  log::verbose("code={}, port_handle={}, handle={}", code, port_handle, p_cb->handle);

  if (PORT_CheckConnection(port_handle, &rem_bda, &lcid) != PORT_SUCCESS) {
    log::warn("Unable to check RFCOMM connection peer:{} handle:{}", rem_bda, port_handle);
  }

  if (code == PORT_SUCCESS) {
    tBTA_JV evt_data = {
            .rfc_open =
                    {
                            .status = tBTA_JV_STATUS::SUCCESS,
                            .handle = p_cb->handle,
                            .rem_bda = rem_bda,
                    },
    };
    p_pcb->state = BTA_JV_ST_CL_OPEN;
    p_cb->p_cback(BTA_JV_RFCOMM_OPEN_EVT, &evt_data, p_pcb->rfcomm_slot_id);
  } else {
    tBTA_JV evt_data = {
            .rfc_close =
                    {
                            .status = tBTA_JV_STATUS::FAILURE,
                            .port_status = code,
                            .handle = p_cb->handle,
                            .async = (p_pcb->state == BTA_JV_ST_CL_CLOSING) ? false : true,
                    },
    };
    // p_pcb->state = BTA_JV_ST_NONE;
    // p_pcb->cong = false;
    p_cback = p_cb->p_cback;
    p_cback(BTA_JV_RFCOMM_CLOSE_EVT, &evt_data, p_pcb->rfcomm_slot_id);
    // bta_jv_free_rfc_cb(p_cb, p_pcb);
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_port_event_cl_cback
 *
 * Description  Callback for RFCOMM client port events
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_port_event_cl_cback(uint32_t code, uint16_t port_handle) {
  tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
  tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
  tBTA_JV evt_data;

  log::verbose("port_handle={}", port_handle);
  if (NULL == p_cb || NULL == p_cb->p_cback) {
    return;
  }

  log::verbose("code=0x{:x}, port_handle={}, handle={}", code, port_handle, p_cb->handle);
  if (code & PORT_EV_RXCHAR) {
    evt_data.data_ind.handle = p_cb->handle;
    p_cb->p_cback(BTA_JV_RFCOMM_DATA_IND_EVT, &evt_data, p_pcb->rfcomm_slot_id);
  }

  if (code & PORT_EV_FC) {
    p_pcb->cong = (code & PORT_EV_FCS) ? false : true;
    evt_data.rfc_cong.cong = p_pcb->cong;
    evt_data.rfc_cong.handle = p_cb->handle;
    evt_data.rfc_cong.status = tBTA_JV_STATUS::SUCCESS;
    p_cb->p_cback(BTA_JV_RFCOMM_CONG_EVT, &evt_data, p_pcb->rfcomm_slot_id);
  }

  if (code & PORT_EV_TXEMPTY) {
    bta_jv_pm_conn_idle(p_pcb->p_pm_cb);
  }
}

/* Client initiates an RFCOMM connection */
void bta_jv_rfcomm_connect(tBTA_SEC sec_mask, uint8_t remote_scn, const RawAddress& peer_bd_addr,
                           tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id) {
  uint16_t handle = 0;
  uint32_t event_mask = BTA_JV_RFC_EV_MASK;
  PortSettings port_settings;

  tBTA_JV bta_jv = {
          .rfc_cl_init =
                  {
                          .status = tBTA_JV_STATUS::SUCCESS,
                          .handle = 0,
                          .sec_id = 0,
                          .use_co = false,
                  },
  };

  if (com::android::bluetooth::flags::rfcomm_always_use_mitm()) {
    // Update security service record for RFCOMM client so that
    // secure RFCOMM connection will be authenticated with MTIM protection
    // while creating the L2CAP connection.
    get_btm_client_interface().security.BTM_SetSecurityLevel(
            true, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, sec_mask, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM,
            0);
  }

  if (RFCOMM_CreateConnectionWithSecurity(UUID_SERVCLASS_SERIAL_PORT, remote_scn, false,
                                          BTA_JV_DEF_RFC_MTU, peer_bd_addr, &handle,
                                          bta_jv_port_mgmt_cl_cback, sec_mask) != PORT_SUCCESS) {
    log::error("RFCOMM_CreateConnection failed");
    bta_jv.rfc_cl_init.status = tBTA_JV_STATUS::FAILURE;
  } else {
    tBTA_JV_PCB* p_pcb;
    tBTA_JV_RFC_CB* p_cb = bta_jv_alloc_rfc_cb(handle, &p_pcb);
    if (p_cb) {
      p_cb->p_cback = p_cback;
      p_cb->scn = 0;
      p_pcb->state = BTA_JV_ST_CL_OPENING;
      p_pcb->rfcomm_slot_id = rfcomm_slot_id;
      bta_jv.rfc_cl_init.use_co = true;

      if (PORT_SetEventMaskAndCallback(handle, event_mask, bta_jv_port_event_cl_cback) !=
          PORT_SUCCESS) {
        log::warn("Unable to set RFCOMM client event mask and callback handle:{}", handle);
      }
      if (PORT_SetDataCOCallback(handle, bta_jv_port_data_co_cback) != PORT_SUCCESS) {
        log::warn("Unable to set RFCOMM client data callback handle:{}", handle);
      }
      if (PORT_GetSettings(handle, &port_settings) != PORT_SUCCESS) {
        log::warn("Unable to get RFCOMM client state handle:{}", handle);
      }

      port_settings.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT);

      if (PORT_SetSettings(handle, &port_settings) != PORT_SUCCESS) {
        log::warn("Unable to set RFCOMM client state handle:{}", handle);
      }

      bta_jv.rfc_cl_init.handle = p_cb->handle;
    } else {
      bta_jv.rfc_cl_init.status = tBTA_JV_STATUS::FAILURE;
      log::error("run out of rfc control block");
    }
  }

  p_cback(BTA_JV_RFCOMM_CL_INIT_EVT, &bta_jv, rfcomm_slot_id);
  if (bta_jv.rfc_cl_init.status == tBTA_JV_STATUS::FAILURE) {
    if (handle) {
      if (RFCOMM_RemoveConnection(handle) != PORT_SUCCESS) {
        log::warn("Unable to remove RFCOMM connection handle:{}", handle);
      }
    }
  }
}

static int find_rfc_pcb(uint32_t rfcomm_slot_id, tBTA_JV_RFC_CB** cb, tBTA_JV_PCB** pcb) {
  *cb = NULL;
  *pcb = NULL;
  int i;
  for (i = 0; i < MAX_RFC_PORTS; i++) {
    uint32_t rfc_handle = bta_jv_cb.port_cb[i].handle & BTA_JV_RFC_HDL_MASK;
    rfc_handle &= ~BTA_JV_RFCOMM_MASK;
    if (rfc_handle && bta_jv_cb.port_cb[i].rfcomm_slot_id == rfcomm_slot_id) {
      *pcb = &bta_jv_cb.port_cb[i];
      *cb = &bta_jv_cb.rfc_cb[rfc_handle - 1];
      log::verbose(
              "FOUND rfc_cb_handle=0x{:x}, port.jv_handle=0x{:x}, state={}, rfc_cb->handle=0x{:x}",
              rfc_handle, (*pcb)->handle, (*pcb)->state, (*cb)->handle);
      return 1;
    }
  }
  log::verbose("cannot find rfc_cb from user data:{}", rfcomm_slot_id);
  return 0;
}

/* Close an RFCOMM connection */
void bta_jv_rfcomm_close(uint32_t handle, uint32_t rfcomm_slot_id) {
  if (!handle) {
    log::error("rfc handle is null");
    return;
  }

  log::verbose("rfc handle={}", handle);

  tBTA_JV_RFC_CB* p_cb = NULL;
  tBTA_JV_PCB* p_pcb = NULL;

  if (!find_rfc_pcb(rfcomm_slot_id, &p_cb, &p_pcb)) {
    return;
  }
  bta_jv_free_rfc_cb(p_cb, p_pcb);
}

/*******************************************************************************
 *
 * Function     bta_jv_port_mgmt_sr_cback
 *
 * Description  callback for port mamangement function of rfcomm
 *              server connections
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_port_mgmt_sr_cback(const tPORT_RESULT code, uint16_t port_handle) {
  tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
  tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
  tBTA_JV evt_data;
  RawAddress rem_bda = RawAddress::kEmpty;
  uint16_t lcid;
  log::verbose("code={}, port_handle={}", code, port_handle);
  if (NULL == p_cb || NULL == p_cb->p_cback) {
    log::error("p_cb={}, p_cb->p_cback={}", std::format_ptr(p_cb),
               std::format_ptr(p_cb ? p_cb->p_cback : nullptr));
    return;
  }
  uint32_t rfcomm_slot_id = p_pcb->rfcomm_slot_id;
  log::verbose("code={}, port_handle=0x{:x}, handle=0x{:x}, p_pcb{}, user={}", code, port_handle,
               p_cb->handle, std::format_ptr(p_pcb), p_pcb->rfcomm_slot_id);

  int status = PORT_CheckConnection(port_handle, &rem_bda, &lcid);
  int failed = true;
  if (code == PORT_SUCCESS) {
    if (status != PORT_SUCCESS) {
      log::error("PORT_CheckConnection returned {}, although port is supposed to be connected",
                 status);
    }
    evt_data.rfc_srv_open.handle = p_pcb->handle;
    evt_data.rfc_srv_open.status = tBTA_JV_STATUS::SUCCESS;
    evt_data.rfc_srv_open.rem_bda = rem_bda;
    tBTA_JV_PCB* p_pcb_new_listen = bta_jv_add_rfc_port(p_cb, p_pcb);
    if (p_pcb_new_listen) {
      evt_data.rfc_srv_open.new_listen_handle = p_pcb_new_listen->handle;
      p_pcb_new_listen->rfcomm_slot_id =
              p_cb->p_cback(BTA_JV_RFCOMM_SRV_OPEN_EVT, &evt_data, rfcomm_slot_id);
      if (p_pcb_new_listen->rfcomm_slot_id == 0) {
        log::error("rfcomm_slot_id == {}", p_pcb_new_listen->rfcomm_slot_id);
      } else {
        log::verbose("curr_sess={}, max_sess={}", p_cb->curr_sess, p_cb->max_sess);
        failed = false;
      }
    } else {
      log::error("failed to create new listen port");
    }
  }
  if (failed) {
    evt_data.rfc_close.handle = p_cb->handle;
    evt_data.rfc_close.status = tBTA_JV_STATUS::FAILURE;
    evt_data.rfc_close.async = true;
    evt_data.rfc_close.port_status = code;
    p_pcb->cong = false;

    tBTA_JV_RFCOMM_CBACK* p_cback = p_cb->p_cback;
    log::verbose("PORT_CLOSED before BTA_JV_RFCOMM_CLOSE_EVT: curr_sess={}, max_sess={}",
                 p_cb->curr_sess, p_cb->max_sess);
    if (BTA_JV_ST_SR_CLOSING == p_pcb->state) {
      evt_data.rfc_close.async = false;
      evt_data.rfc_close.status = tBTA_JV_STATUS::SUCCESS;
    }
    // p_pcb->state = BTA_JV_ST_NONE;
    p_cback(BTA_JV_RFCOMM_CLOSE_EVT, &evt_data, rfcomm_slot_id);
    // bta_jv_free_rfc_cb(p_cb, p_pcb);

    log::verbose("PORT_CLOSED after BTA_JV_RFCOMM_CLOSE_EVT: curr_sess={}, max_sess={}",
                 p_cb->curr_sess, p_cb->max_sess);
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_port_event_sr_cback
 *
 * Description  Callback for RFCOMM server port events
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_port_event_sr_cback(uint32_t code, uint16_t port_handle) {
  tBTA_JV_PCB* p_pcb = bta_jv_rfc_port_to_pcb(port_handle);
  tBTA_JV_RFC_CB* p_cb = bta_jv_rfc_port_to_cb(port_handle);
  tBTA_JV evt_data;

  if (NULL == p_cb || NULL == p_cb->p_cback) {
    log::error("p_cb={}, p_cb->p_cback={}", std::format_ptr(p_cb),
               std::format_ptr(p_cb ? p_cb->p_cback : nullptr));
    return;
  }

  log::verbose("code=0x{:x}, port_handle={}, handle={}", code, port_handle, p_cb->handle);

  uint32_t user_data = p_pcb->rfcomm_slot_id;
  if (code & PORT_EV_RXCHAR) {
    evt_data.data_ind.handle = p_cb->handle;
    p_cb->p_cback(BTA_JV_RFCOMM_DATA_IND_EVT, &evt_data, user_data);
  }

  if (code & PORT_EV_FC) {
    p_pcb->cong = (code & PORT_EV_FCS) ? false : true;
    evt_data.rfc_cong.cong = p_pcb->cong;
    evt_data.rfc_cong.handle = p_cb->handle;
    evt_data.rfc_cong.status = tBTA_JV_STATUS::SUCCESS;
    p_cb->p_cback(BTA_JV_RFCOMM_CONG_EVT, &evt_data, user_data);
  }

  if (code & PORT_EV_TXEMPTY) {
    bta_jv_pm_conn_idle(p_pcb->p_pm_cb);
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_add_rfc_port
 *
 * Description  add a port for server when the existing posts is open
 *
 * Returns   return a pointer to tBTA_JV_PCB just added
 *
 ******************************************************************************/
static tBTA_JV_PCB* bta_jv_add_rfc_port(tBTA_JV_RFC_CB* p_cb, tBTA_JV_PCB* p_pcb_open) {
  uint8_t used = 0, i, listen = 0;
  uint32_t si = 0;
  PortSettings port_settings;
  uint32_t event_mask = BTA_JV_RFC_EV_MASK;
  tBTA_JV_PCB* p_pcb = NULL;
  tBTA_SEC sec_mask;
  if (p_cb->max_sess > 1) {
    for (i = 0; i < p_cb->max_sess; i++) {
      if (p_cb->rfc_hdl[i] != 0) {
        p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[i] - 1];
        if (p_pcb->state == BTA_JV_ST_SR_LISTEN) {
          listen++;
          if (p_pcb_open == p_pcb) {
            log::verbose("port_handle={}, change the listen port to open state",
                         p_pcb->port_handle);
            p_pcb->state = BTA_JV_ST_SR_OPEN;

          } else {
            log::error(
                    "open pcb not matching listen one, count={}, listen pcb handle={}, open pcb={}",
                    listen, p_pcb->port_handle, p_pcb_open->handle);
            return NULL;
          }
        }
        used++;
      } else if (si == 0) {
        si = i + 1;
      }
    }

    log::verbose("max_sess={}, used={}, curr_sess={}, listen={}, si={}", p_cb->max_sess, used,
                 p_cb->curr_sess, listen, si);
    if (used < p_cb->max_sess && listen == 1 && si) {
      si--;
      if (PORT_GetSecurityMask(p_pcb_open->port_handle, &sec_mask) != PORT_SUCCESS) {
        log::error("RFCOMM_CreateConnection failed: invalid port_handle");
      }

      if (RFCOMM_CreateConnectionWithSecurity(
                  p_cb->sec_id, p_cb->scn, true, BTA_JV_DEF_RFC_MTU, RawAddress::kAny,
                  &(p_cb->rfc_hdl[si]), bta_jv_port_mgmt_sr_cback, sec_mask) == PORT_SUCCESS) {
        p_cb->curr_sess++;
        p_pcb = &bta_jv_cb.port_cb[p_cb->rfc_hdl[si] - 1];
        p_pcb->state = BTA_JV_ST_SR_LISTEN;
        p_pcb->port_handle = p_cb->rfc_hdl[si];
        p_pcb->rfcomm_slot_id = p_pcb_open->rfcomm_slot_id;

        if (PORT_ClearKeepHandleFlag(p_pcb->port_handle) != PORT_SUCCESS) {
          log::warn("Unable to clear RFCOMM server keep handle flag handle:{}", p_pcb->port_handle);
        }
        if (PORT_SetEventMaskAndCallback(p_pcb->port_handle, event_mask,
                                         bta_jv_port_event_sr_cback) != PORT_SUCCESS) {
          log::warn("Unable to set RFCOMM server event mask and callback handle:{}",
                    p_pcb->port_handle);
        }
        if (PORT_SetDataCOCallback(p_pcb->port_handle, bta_jv_port_data_co_cback) != PORT_SUCCESS) {
          log::warn("Unable to set RFCOMM server data callback handle:{}", p_pcb->port_handle);
        }
        if (PORT_GetSettings(p_pcb->port_handle, &port_settings) != PORT_SUCCESS) {
          log::warn("Unable to get RFCOMM server state handle:{}", p_pcb->port_handle);
        }

        port_settings.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT);

        if (PORT_SetSettings(p_pcb->port_handle, &port_settings) != PORT_SUCCESS) {
          log::warn("Unable to set RFCOMM server state handle:{}", p_pcb->port_handle);
        }
        p_pcb->handle = BTA_JV_RFC_H_S_TO_HDL(p_cb->handle, si);
        log::verbose("p_pcb->handle=0x{:x}, curr_sess={}", p_pcb->handle, p_cb->curr_sess);
      } else {
        log::error("RFCOMM_CreateConnection failed");
        return NULL;
      }
    } else {
      log::error("cannot create new rfc listen port");
      return NULL;
    }
  }
  log::verbose("sec id in use={}, rfc_cb in use={}", get_sec_id_used(), get_rfc_cb_used());
  return p_pcb;
}

/* waits for an RFCOMM client to connect */
void bta_jv_rfcomm_start_server(tBTA_SEC sec_mask, uint8_t local_scn, uint8_t max_session,
                                tBTA_JV_RFCOMM_CBACK* p_cback, uint32_t rfcomm_slot_id) {
  uint16_t handle = 0;
  uint32_t event_mask = BTA_JV_RFC_EV_MASK;
  PortSettings port_settings;
  tBTA_JV_RFC_CB* p_cb = NULL;
  tBTA_JV_PCB* p_pcb;
  tBTA_JV_RFCOMM_START evt_data;

  memset(&evt_data, 0, sizeof(evt_data));
  evt_data.status = tBTA_JV_STATUS::FAILURE;

  do {
    if (RFCOMM_CreateConnectionWithSecurity(0, local_scn, true, BTA_JV_DEF_RFC_MTU,
                                            RawAddress::kAny, &handle, bta_jv_port_mgmt_sr_cback,
                                            sec_mask) != PORT_SUCCESS) {
      log::error("RFCOMM_CreateConnection failed");
      break;
    }

    p_cb = bta_jv_alloc_rfc_cb(handle, &p_pcb);
    if (!p_cb) {
      log::error("run out of rfc control block");
      break;
    }

    p_cb->max_sess = max_session;
    p_cb->p_cback = p_cback;
    p_cb->scn = local_scn;
    p_pcb->state = BTA_JV_ST_SR_LISTEN;
    p_pcb->rfcomm_slot_id = rfcomm_slot_id;
    evt_data.status = tBTA_JV_STATUS::SUCCESS;
    evt_data.handle = p_cb->handle;
    evt_data.use_co = true;

    if (PORT_ClearKeepHandleFlag(handle) != PORT_SUCCESS) {
      log::warn("Unable to clear RFCOMM server keep handle flag handle:{}", handle);
    }
    if (PORT_SetEventMaskAndCallback(handle, event_mask, bta_jv_port_event_sr_cback) !=
        PORT_SUCCESS) {
      log::warn("Unable to set RFCOMM server event mask and callback handle:{}", handle);
    }
    if (PORT_GetSettings(handle, &port_settings) != PORT_SUCCESS) {
      log::warn("Unable to get RFCOMM server state handle:{}", handle);
    }

    port_settings.fc_type = (PORT_FC_CTS_ON_INPUT | PORT_FC_CTS_ON_OUTPUT);

    if (PORT_SetSettings(handle, &port_settings) != PORT_SUCCESS) {
      log::warn("Unable to set RFCOMM port state handle:{}", handle);
    }
  } while (0);

  tBTA_JV bta_jv;
  bta_jv.rfc_start = evt_data;
  p_cback(BTA_JV_RFCOMM_START_EVT, &bta_jv, rfcomm_slot_id);
  if (bta_jv.rfc_start.status == tBTA_JV_STATUS::SUCCESS) {
    if (PORT_SetDataCOCallback(handle, bta_jv_port_data_co_cback) != PORT_SUCCESS) {
      log::error("Unable to set RFCOMM server data callback handle:{}", handle);
    }
  } else {
    if (handle) {
      if (RFCOMM_RemoveConnection(handle) != PORT_SUCCESS) {
        log::warn("Unable to remote RFCOMM server connection handle:{}", handle);
      }
    }
  }
}

/* stops an RFCOMM server */
void bta_jv_rfcomm_stop_server(uint32_t handle, uint32_t rfcomm_slot_id) {
  if (!handle) {
    log::error("jv handle is null");
    return;
  }

  log::verbose("");
  tBTA_JV_RFC_CB* p_cb = NULL;
  tBTA_JV_PCB* p_pcb = NULL;

  if (!find_rfc_pcb(rfcomm_slot_id, &p_cb, &p_pcb)) {
    return;
  }
  log::verbose("p_pcb={}, p_pcb->port_handle={}", std::format_ptr(p_pcb), p_pcb->port_handle);
  bta_jv_free_rfc_cb(p_cb, p_pcb);
}

/* write data to an RFCOMM connection */
void bta_jv_rfcomm_write(uint32_t handle, uint32_t req_id, tBTA_JV_RFC_CB* p_cb,
                         tBTA_JV_PCB* p_pcb) {
  if (p_pcb->state == BTA_JV_ST_NONE) {
    log::error("in state BTA_JV_ST_NONE - cannot write");
    return;
  }

  tBTA_JV_RFCOMM_WRITE evt_data;
  evt_data.status = tBTA_JV_STATUS::FAILURE;
  evt_data.handle = handle;
  evt_data.req_id = req_id;
  evt_data.cong = p_pcb->cong;
  evt_data.len = 0;

  bta_jv_pm_conn_busy(p_pcb->p_pm_cb);

  if (!evt_data.cong && PORT_WriteDataCO(p_pcb->port_handle, &evt_data.len) == PORT_SUCCESS) {
    evt_data.status = tBTA_JV_STATUS::SUCCESS;
  }

  // Update congestion flag
  evt_data.cong = p_pcb->cong;

  if (!p_cb->p_cback) {
    log::error("No JV callback set");
    return;
  }

  tBTA_JV bta_jv;
  bta_jv.rfc_write = evt_data;
  p_cb->p_cback(BTA_JV_RFCOMM_WRITE_EVT, &bta_jv, p_pcb->rfcomm_slot_id);
}

/* Set or free power mode profile for a JV application */
void bta_jv_set_pm_profile(uint32_t handle, tBTA_JV_PM_ID app_id, tBTA_JV_CONN_STATE init_st) {
  log::verbose("handle=0x{:x}, app_id={}, init_st={}", handle, app_id,
               bta_jv_conn_state_text(init_st));

  /* clear PM control block */
  if (app_id == BTA_JV_PM_ID_CLEAR) {
    tBTA_JV_STATUS status = bta_jv_free_set_pm_profile_cb(handle);
    if (status != tBTA_JV_STATUS::SUCCESS) {
      log::warn("Unable to free a power mode profile handle:0x:{:x} app_id:{} state:{} status:{}",
                handle, app_id, init_st, bta_jv_status_text(status));
    }
  } else { /* set PM control block */
    tBTA_JV_PM_CB* p_cb = bta_jv_alloc_set_pm_profile_cb(handle, app_id);
    if (p_cb) {
      bta_jv_pm_state_change(p_cb, init_st);
    } else {
      log::warn("Unable to allocate a power mode profile handle:0x:{:x} app_id:{} state:{}", handle,
                app_id, init_st);
    }
  }
}

/*******************************************************************************
 *
 * Function    bta_jv_pm_conn_busy
 *
 * Description set pm connection busy state (input param safe)
 *
 * Params      p_cb: pm control block of jv connection
 *
 * Returns     void
 *
 ******************************************************************************/
static void bta_jv_pm_conn_busy(tBTA_JV_PM_CB* p_cb) {
  if ((NULL != p_cb) && (BTA_JV_PM_IDLE_ST == p_cb->state)) {
    bta_jv_pm_state_change(p_cb, BTA_JV_CONN_BUSY);
  }
}

/*******************************************************************************
 *
 * Function    bta_jv_pm_conn_idle
 *
 * Description set pm connection idle state (input param safe)
 *
 * Params      p_cb: pm control block of jv connection
 *
 * Returns     void
 *
 ******************************************************************************/
static void bta_jv_pm_conn_idle(tBTA_JV_PM_CB* p_cb) {
  if ((NULL != p_cb) && (BTA_JV_PM_IDLE_ST != p_cb->state)) {
    bta_jv_pm_state_change(p_cb, BTA_JV_CONN_IDLE);
  }
}

/*******************************************************************************
 *
 * Function     bta_jv_pm_state_change
 *
 * Description  Notify power manager there is state change
 *
 * Params      p_cb: must be NONE NULL
 *
 * Returns      void
 *
 ******************************************************************************/
static void bta_jv_pm_state_change(tBTA_JV_PM_CB* p_cb, const tBTA_JV_CONN_STATE state) {
  log::verbose("p_cb={}, handle=0x{:x}, busy/idle_state={}, app_id={}, conn_state={}",
               std::format_ptr(p_cb), p_cb->handle, p_cb->state, p_cb->app_id,
               bta_jv_conn_state_text(state));

  switch (state) {
    case BTA_JV_CONN_OPEN:
      bta_sys_conn_open(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_CONN_CLOSE:
      bta_sys_conn_close(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_APP_OPEN:
      bta_sys_app_open(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_APP_CLOSE:
      bta_sys_app_close(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_SCO_OPEN:
      bta_sys_sco_open(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_SCO_CLOSE:
      bta_sys_sco_close(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_CONN_IDLE:
      p_cb->state = BTA_JV_PM_IDLE_ST;
      bta_sys_idle(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    case BTA_JV_CONN_BUSY:
      p_cb->state = BTA_JV_PM_BUSY_ST;
      bta_sys_busy(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
      break;

    default:
      log::warn("Invalid state={}", bta_jv_conn_state_text(state));
      break;
  }
}

/*******************************************************************************
 *
 * Function    bta_jv_reset_sniff_timer
 *
 * Description reset pm sniff timer state (input param safe)
 *
 * Params      p_cb: pm control block of jv connection
 *
 * Returns     void
 *
 ******************************************************************************/
static void bta_jv_reset_sniff_timer(tBTA_JV_PM_CB* p_cb) {
  if (NULL != p_cb) {
    p_cb->state = BTA_JV_PM_IDLE_ST;
    bta_sys_reset_sniff(BTA_ID_JV, p_cb->app_id, p_cb->peer_bd_addr);
  }
}
/******************************************************************************/

namespace bluetooth::legacy::testing {

void bta_jv_start_discovery_cback(uint32_t rfcomm_slot_id, const RawAddress& bd_addr,
                                  tSDP_RESULT result) {
  ::bta_jv_start_discovery_cback(rfcomm_slot_id, bd_addr, result);
}

}  // namespace bluetooth::legacy::testing
