/******************************************************************************
 *
 *  Copyright (C) 2002-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 module contains API of the audio/video distribution transport
 *  protocol.
 *
 ******************************************************************************/

#include "avdt_api.h"
#include <string.h>
#include "avdt_int.h"
#include "avdtc_api.h"
#include "bt_target.h"
#include "bt_types.h"
#include "btm_api.h"
#include "btu.h"
#include "l2c_api.h"
#include "stack/include/a2dp_codec_api.h"

/* Control block for AVDT */
tAVDT_CB avdt_cb;

void avdt_ccb_idle_ccb_timer_timeout(void* data) {
  tAVDT_CCB* p_ccb = (tAVDT_CCB*)data;
  uint8_t avdt_event = AVDT_CCB_IDLE_TOUT_EVT;
  uint8_t err_code = AVDT_ERR_TIMEOUT;

  tAVDT_CCB_EVT avdt_ccb_evt;
  avdt_ccb_evt.err_code = err_code;
  avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt);
}

void avdt_ccb_ret_ccb_timer_timeout(void* data) {
  tAVDT_CCB* p_ccb = (tAVDT_CCB*)data;
  uint8_t avdt_event = AVDT_CCB_RET_TOUT_EVT;
  uint8_t err_code = AVDT_ERR_TIMEOUT;

  tAVDT_CCB_EVT avdt_ccb_evt;
  avdt_ccb_evt.err_code = err_code;
  avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt);
}

void avdt_ccb_rsp_ccb_timer_timeout(void* data) {
  tAVDT_CCB* p_ccb = (tAVDT_CCB*)data;
  uint8_t avdt_event = AVDT_CCB_RSP_TOUT_EVT;
  uint8_t err_code = AVDT_ERR_TIMEOUT;

  tAVDT_CCB_EVT avdt_ccb_evt;
  avdt_ccb_evt.err_code = err_code;
  avdt_ccb_event(p_ccb, avdt_event, &avdt_ccb_evt);
}

void avdt_scb_transport_channel_timer_timeout(void* data) {
  tAVDT_SCB* p_scb = (tAVDT_SCB*)data;
  uint8_t avdt_event = AVDT_SCB_TC_TOUT_EVT;

  avdt_scb_event(p_scb, avdt_event, NULL);
}

/*******************************************************************************
 *
 * Function         AVDT_Register
 *
 * Description      This is the system level registration function for the
 *                  AVDTP protocol.  This function initializes AVDTP and
 *                  prepares the protocol stack for its use.  This function
 *                  must be called once by the system or platform using AVDTP
 *                  before the other functions of the API an be used.
 *
 *
 * Returns          void
 *
 ******************************************************************************/
void AVDT_Register(tAVDT_REG* p_reg, tAVDT_CTRL_CBACK* p_cback) {
  /* register PSM with L2CAP */
  L2CA_Register(AVDT_PSM, (tL2CAP_APPL_INFO*)&avdt_l2c_appl);

  /* set security level */
  BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
                       AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);
  BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVDTP, p_reg->sec_mask,
                       AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_SIG);

  /* do not use security on the media channel */
  BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
                       AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA);
  BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
                       AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_MEDIA);

#if (AVDT_REPORTING == TRUE)
  /* do not use security on the reporting channel */
  BTM_SetSecurityLevel(true, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
                       AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT);
  BTM_SetSecurityLevel(false, "", BTM_SEC_SERVICE_AVDTP_NOSEC, BTM_SEC_NONE,
                       AVDT_PSM, BTM_SEC_PROTO_AVDT, AVDT_CHAN_REPORT);
#endif

  /* initialize AVDTP data structures */
  avdt_scb_init();
  avdt_ccb_init();
  avdt_ad_init();

  /* copy registration struct */
  memcpy(&avdt_cb.rcb, p_reg, sizeof(tAVDT_REG));
  avdt_cb.p_conn_cback = p_cback;
}

/*******************************************************************************
 *
 * Function         AVDT_Deregister
 *
 * Description      This function is called to deregister use AVDTP protocol.
 *                  It is called when AVDTP is no longer being used by any
 *                  application in the system.  Before this function can be
 *                  called, all streams must be removed with
 *                  AVDT_RemoveStream().
 *
 *
 * Returns          void
 *
 ******************************************************************************/
void AVDT_Deregister(void) {
  /* deregister PSM with L2CAP */
  L2CA_Deregister(AVDT_PSM);
}

void AVDT_AbortReq(uint8_t handle) {
  AVDT_TRACE_WARNING("%s: handle=%d", __func__, handle);

  tAVDT_SCB* p_scb = avdt_scb_by_hdl(handle);
  if (p_scb != NULL) {
    avdt_scb_event(p_scb, AVDT_SCB_API_ABORT_REQ_EVT, NULL);
  } else {
    AVDT_TRACE_ERROR("%s Improper SCB, can not abort the stream", __func__);
  }
}

/*******************************************************************************
 *
 * Function         AVDT_CreateStream
 *
 * Description      Create a stream endpoint.  After a stream endpoint is
 *                  created an application can initiate a connection between
 *                  this endpoint and an endpoint on a peer device.  In
 *                  addition, a peer device can discover, get the capabilities,
 *                  and connect to this endpoint.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_CreateStream(uint8_t* p_handle, tAVDT_CS* p_cs) {
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB* p_scb;

  AVDT_TRACE_DEBUG("%s", __func__);

  /* Verify parameters; if invalid, return failure */
  if (((p_cs->cfg.psc_mask & (~AVDT_PSC)) != 0) ||
      (p_cs->p_ctrl_cback == NULL)) {
    result = AVDT_BAD_PARAMS;
  }
  /* Allocate scb; if no scbs, return failure */
  else {
    p_scb = avdt_scb_alloc(p_cs);
    if (p_scb == NULL) {
      result = AVDT_NO_RESOURCES;
    } else {
      *p_handle = avdt_scb_to_hdl(p_scb);
    }
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_RemoveStream
 *
 * Description      Remove a stream endpoint.  This function is called when
 *                  the application is no longer using a stream endpoint.
 *                  If this function is called when the endpoint is connected
 *                  the connection is closed and then the stream endpoint
 *                  is removed.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_RemoveStream(uint8_t handle) {
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB* p_scb;

  AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);

  /* look up scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  } else {
    /* send remove event to scb */
    avdt_scb_event(p_scb, AVDT_SCB_API_REMOVE_EVT, NULL);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_DiscoverReq
 *
 * Description      This function initiates a connection to the AVDTP service
 *                  on the peer device, if not already present, and discovers
 *                  the stream endpoints on the peer device.  (Please note
 *                  that AVDTP discovery is unrelated to SDP discovery).
 *                  This function can be called at any time regardless of
 *                  whether there is an AVDTP connection to the peer device.
 *
 *                  When discovery is complete, an AVDT_DISCOVER_CFM_EVT
 *                  is sent to the application via its callback function.
 *                  The application must not call AVDT_GetCapReq() or
 *                  AVDT_DiscoverReq() again to the same device until
 *                  discovery is complete.
 *
 *                  The memory addressed by sep_info is allocated by the
 *                  application.  This memory is written to by AVDTP as part
 *                  of the discovery procedure.  This memory must remain
 *                  accessible until the application receives the
 *                  AVDT_DISCOVER_CFM_EVT.
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_DiscoverReq(const RawAddress& bd_addr, tAVDT_SEP_INFO* p_sep_info,
                          uint8_t max_seps, tAVDT_CTRL_CBACK* p_cback) {
  tAVDT_CCB* p_ccb;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_CCB_EVT evt;

  AVDT_TRACE_DEBUG("%s", __func__);

  /* find channel control block for this bd addr; if none, allocate one */
  p_ccb = avdt_ccb_by_bd(bd_addr);
  if (p_ccb == NULL) {
    p_ccb = avdt_ccb_alloc(bd_addr);
    if (p_ccb == NULL) {
      /* could not allocate channel control block */
      result = AVDT_NO_RESOURCES;
    }
  }

  if (result == AVDT_SUCCESS) {
    /* make sure no discovery or get capabilities req already in progress */
    if (p_ccb->proc_busy) {
      result = AVDT_BUSY;
    }
    /* send event to ccb */
    else {
      evt.discover.p_sep_info = p_sep_info;
      evt.discover.num_seps = max_seps;
      evt.discover.p_cback = p_cback;
      avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCOVER_REQ_EVT, &evt);
    }
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         avdt_get_cap_req
 *
 * Description      internal function to serve both AVDT_GetCapReq and
 *                  AVDT_GetAllCapReq
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
static uint16_t avdt_get_cap_req(const RawAddress& bd_addr,
                                 tAVDT_CCB_API_GETCAP* p_evt) {
  tAVDT_CCB* p_ccb = NULL;
  uint16_t result = AVDT_SUCCESS;

  AVDT_TRACE_DEBUG("%s", __func__);

  /* verify SEID */
  if ((p_evt->single.seid < AVDT_SEID_MIN) ||
      (p_evt->single.seid > AVDT_SEID_MAX)) {
    AVDT_TRACE_ERROR("seid: %d", p_evt->single.seid);
    result = AVDT_BAD_PARAMS;
  }
  /* find channel control block for this bd addr; if none, allocate one */
  else {
    p_ccb = avdt_ccb_by_bd(bd_addr);
    if (p_ccb == NULL) {
      p_ccb = avdt_ccb_alloc(bd_addr);
      if (p_ccb == NULL) {
        /* could not allocate channel control block */
        result = AVDT_NO_RESOURCES;
      }
    }
  }

  if (result == AVDT_SUCCESS) {
    /* make sure no discovery or get capabilities req already in progress */
    if (p_ccb->proc_busy) {
      result = AVDT_BUSY;
    }
    /* send event to ccb */
    else {
      avdt_ccb_event(p_ccb, AVDT_CCB_API_GETCAP_REQ_EVT, (tAVDT_CCB_EVT*)p_evt);
    }
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_GetCapReq
 *
 * Description      This function initiates a connection to the AVDTP service
 *                  on the peer device, if not already present, and gets the
 *                  capabilities of a stream endpoint on the peer device.
 *                  This function can be called at any time regardless of
 *                  whether there is an AVDTP connection to the peer device.
 *
 *                  When the procedure is complete, an AVDT_GETCAP_CFM_EVT is
 *                  sent to the application via its callback function.  The
 *                  application must not call AVDT_GetCapReq() or
 *                  AVDT_DiscoverReq() again until the procedure is complete.
 *
 *                  The memory pointed to by p_cfg is allocated by the
 *                  application.  This memory is written to by AVDTP as part
 *                  of the get capabilities procedure.  This memory must
 *                  remain accessible until the application receives
 *                  the AVDT_GETCAP_CFM_EVT.
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_GetCapReq(const RawAddress& bd_addr, uint8_t seid,
                        tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback) {
  tAVDT_CCB_API_GETCAP getcap;
  uint16_t result = AVDT_SUCCESS;

  AVDT_TRACE_DEBUG("%s", __func__);

  getcap.single.seid = seid;
  getcap.single.sig_id = AVDT_SIG_GETCAP;
  getcap.p_cfg = p_cfg;
  getcap.p_cback = p_cback;
  result = avdt_get_cap_req(bd_addr, &getcap);

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_GetAllCapReq
 *
 * Description      This function initiates a connection to the AVDTP service
 *                  on the peer device, if not already present, and gets the
 *                  capabilities of a stream endpoint on the peer device.
 *                  This function can be called at any time regardless of
 *                  whether there is an AVDTP connection to the peer device.
 *
 *                  When the procedure is complete, an AVDT_GETCAP_CFM_EVT is
 *                  sent to the application via its callback function.  The
 *                  application must not call AVDT_GetCapReq() or
 *                  AVDT_DiscoverReq() again until the procedure is complete.
 *
 *                  The memory pointed to by p_cfg is allocated by the
 *                  application.  This memory is written to by AVDTP as part
 *                  of the get capabilities procedure.  This memory must
 *                  remain accessible until the application receives
 *                  the AVDT_GETCAP_CFM_EVT.
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_GetAllCapReq(const RawAddress& bd_addr, uint8_t seid,
                           tAVDT_CFG* p_cfg, tAVDT_CTRL_CBACK* p_cback) {
  tAVDT_CCB_API_GETCAP getcap;
  uint16_t result = AVDT_SUCCESS;

  AVDT_TRACE_DEBUG("%s", __func__);

  getcap.single.seid = seid;
  getcap.single.sig_id = AVDT_SIG_GET_ALLCAP;
  getcap.p_cfg = p_cfg;
  getcap.p_cback = p_cback;
  result = avdt_get_cap_req(bd_addr, &getcap);

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_DelayReport
 *
 * Description      This functions sends a Delay Report to the peer device
 *                  that is associated with a particular SEID.
 *                  This function is called by SNK device.
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_DelayReport(uint8_t handle, uint8_t seid, uint16_t delay) {
  tAVDT_SCB* p_scb;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB_EVT evt;

  AVDT_TRACE_DEBUG("%s: handle=%d ceid=%d delay=%d", __func__, handle, seid,
                   delay);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  } else
  /* send event to scb */
  {
    evt.apidelay.hdr.seid = seid;
    evt.apidelay.delay = delay;
    avdt_scb_event(p_scb, AVDT_SCB_API_DELAY_RPT_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_OpenReq
 *
 * Description      This function initiates a connection to the AVDTP service
 *                  on the peer device, if not already present, and connects
 *                  to a stream endpoint on a peer device.  When the connection
 *                  is completed, an AVDT_OPEN_CFM_EVT is sent to the
 *                  application via the control callback function for this
 *                  handle.
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_OpenReq(uint8_t handle, const RawAddress& bd_addr, uint8_t seid,
                      tAVDT_CFG* p_cfg) {
  tAVDT_CCB* p_ccb = NULL;
  tAVDT_SCB* p_scb = NULL;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB_EVT evt;

  AVDT_TRACE_DEBUG("%s: handle=%d seid=%d", __func__, handle, seid);

  /* verify SEID */
  if ((seid < AVDT_SEID_MIN) || (seid > AVDT_SEID_MAX)) {
    result = AVDT_BAD_PARAMS;
  }
  /* map handle to scb */
  else {
    p_scb = avdt_scb_by_hdl(handle);
    if (p_scb == NULL) {
      result = AVDT_BAD_HANDLE;
    }
    /* find channel control block for this bd addr; if none, allocate one */
    else {
      p_ccb = avdt_ccb_by_bd(bd_addr);
      if (p_ccb == NULL) {
        p_ccb = avdt_ccb_alloc(bd_addr);
        if (p_ccb == NULL) {
          /* could not allocate channel control block */
          result = AVDT_NO_RESOURCES;
        }
      }
    }
  }

  /* send event to scb */
  if (result == AVDT_SUCCESS) {
    A2DP_DumpCodecInfo(p_cfg->codec_info);

    evt.msg.config_cmd.hdr.seid = seid;
    evt.msg.config_cmd.hdr.ccb_idx = avdt_ccb_to_idx(p_ccb);
    evt.msg.config_cmd.int_seid = handle;
    evt.msg.config_cmd.p_cfg = p_cfg;
    avdt_scb_event(p_scb, AVDT_SCB_API_SETCONFIG_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_ConfigRsp
 *
 * Description      Respond to a configure request from the peer device.  This
 *                  function must be called if the application receives an
 *                  AVDT_CONFIG_IND_EVT through its control callback.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_ConfigRsp(uint8_t handle, uint8_t label, uint8_t error_code,
                        uint8_t category) {
  tAVDT_SCB* p_scb;
  tAVDT_SCB_EVT evt;
  uint16_t result = AVDT_SUCCESS;
  uint8_t event_code;

  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d category=%d", __func__,
                   handle, label, error_code, category);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  }
  /* handle special case when this function is called but peer has not send
  ** a configuration cmd; ignore and return error result
  */
  else if (!p_scb->in_use) {
    result = AVDT_BAD_HANDLE;
  }
  /* send event to scb */
  else {
    evt.msg.hdr.err_code = error_code;
    evt.msg.hdr.err_param = category;
    evt.msg.hdr.label = label;
    if (error_code == 0) {
      event_code = AVDT_SCB_API_SETCONFIG_RSP_EVT;
    } else {
      event_code = AVDT_SCB_API_SETCONFIG_REJ_EVT;
    }
    avdt_scb_event(p_scb, event_code, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_StartReq
 *
 * Description      Start one or more stream endpoints.  This initiates the
 *                  transfer of media packets for the streams.  All stream
 *                  endpoints must previously be opened.  When the streams
 *                  are started, an AVDT_START_CFM_EVT is sent to the
 *                  application via the control callback function for each
 *                  stream.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_StartReq(uint8_t* p_handles, uint8_t num_handles) {
  tAVDT_SCB* p_scb = NULL;
  tAVDT_CCB_EVT evt;
  uint16_t result = AVDT_SUCCESS;
  int i;

  AVDT_TRACE_DEBUG("%s: num_handles=%d", __func__, num_handles);

  if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
    result = AVDT_BAD_PARAMS;
  } else {
    /* verify handles */
    for (i = 0; i < num_handles; i++) {
      p_scb = avdt_scb_by_hdl(p_handles[i]);
      if (p_scb == NULL) {
        result = AVDT_BAD_HANDLE;
        break;
      }
    }
  }

  if (result == AVDT_SUCCESS) {
    if (p_scb->p_ccb == NULL) {
      result = AVDT_BAD_HANDLE;
    } else {
      /* send event to ccb */
      memcpy(evt.msg.multi.seid_list, p_handles, num_handles);
      evt.msg.multi.num_seps = num_handles;
      avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_START_REQ_EVT, &evt);
    }
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_SuspendReq
 *
 * Description      Suspend one or more stream endpoints. This suspends the
 *                  transfer of media packets for the streams.  All stream
 *                  endpoints must previously be open and started.  When the
 *                  streams are suspended, an AVDT_SUSPEND_CFM_EVT is sent to
 *                  the application via the control callback function for
 *                  each stream.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_SuspendReq(uint8_t* p_handles, uint8_t num_handles) {
  tAVDT_SCB* p_scb = NULL;
  tAVDT_CCB_EVT evt;
  uint16_t result = AVDT_SUCCESS;
  int i;

  AVDT_TRACE_DEBUG("%s: num_handles=%d", __func__, num_handles);

  if ((num_handles == 0) || (num_handles > AVDT_NUM_SEPS)) {
    result = AVDT_BAD_PARAMS;
  } else {
    /* verify handles */
    for (i = 0; i < num_handles; i++) {
      p_scb = avdt_scb_by_hdl(p_handles[i]);
      if (p_scb == NULL) {
        result = AVDT_BAD_HANDLE;
        break;
      }
    }
  }

  if (result == AVDT_SUCCESS) {
    if (p_scb->p_ccb == NULL) {
      result = AVDT_BAD_HANDLE;
    } else {
      /* send event to ccb */
      memcpy(evt.msg.multi.seid_list, p_handles, num_handles);
      evt.msg.multi.num_seps = num_handles;
      avdt_ccb_event(p_scb->p_ccb, AVDT_CCB_API_SUSPEND_REQ_EVT, &evt);
    }
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_CloseReq
 *
 * Description      Close a stream endpoint.  This stops the transfer of media
 *                  packets and closes the transport channel associated with
 *                  this stream endpoint.  When the stream is closed, an
 *                  AVDT_CLOSE_CFM_EVT is sent to the application via the
 *                  control callback function for this handle.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_CloseReq(uint8_t handle) {
  tAVDT_SCB* p_scb;
  uint16_t result = AVDT_SUCCESS;

  AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  } else
  /* send event to scb */
  {
    avdt_scb_event(p_scb, AVDT_SCB_API_CLOSE_REQ_EVT, NULL);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_ReconfigReq
 *
 * Description      Reconfigure a stream endpoint.  This allows the application
 *                  to change the codec or content protection capabilities of
 *                  a stream endpoint after it has been opened.  This function
 *                  can only be called if the stream is opened but not started
 *                  or if the stream has been suspended.  When the procedure
 *                  is completed, an AVDT_RECONFIG_CFM_EVT is sent to the
 *                  application via the control callback function for this
 *                  handle.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_ReconfigReq(uint8_t handle, tAVDT_CFG* p_cfg) {
  tAVDT_SCB* p_scb;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB_EVT evt;

  AVDT_TRACE_DEBUG("%s: handle=%d", __func__, handle);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  }
  /* send event to scb */
  else {
    /* force psc_mask to zero */
    p_cfg->psc_mask = 0;
    evt.msg.reconfig_cmd.p_cfg = p_cfg;
    avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_ReconfigRsp
 *
 * Description      Respond to a reconfigure request from the peer device.
 *                  This function must be called if the application receives
 *                  an AVDT_RECONFIG_IND_EVT through its control callback.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_ReconfigRsp(uint8_t handle, uint8_t label, uint8_t error_code,
                          uint8_t category) {
  tAVDT_SCB* p_scb;
  tAVDT_SCB_EVT evt;
  uint16_t result = AVDT_SUCCESS;

  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d category=%d", __func__,
                   handle, label, error_code, category);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  }
  /* send event to scb */
  else {
    evt.msg.hdr.err_code = error_code;
    evt.msg.hdr.err_param = category;
    evt.msg.hdr.label = label;
    avdt_scb_event(p_scb, AVDT_SCB_API_RECONFIG_RSP_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_SecurityReq
 *
 * Description      Send a security request to the peer device.  When the
 *                  security procedure is completed, an AVDT_SECURITY_CFM_EVT
 *                  is sent to the application via the control callback function
 *                  for this handle.  (Please note that AVDTP security
 *                  procedures are unrelated to Bluetooth link level security.)
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_SecurityReq(uint8_t handle, uint8_t* p_data, uint16_t len) {
  tAVDT_SCB* p_scb;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB_EVT evt;

  AVDT_TRACE_DEBUG("%s: handle=%d len=%d", __func__, handle, len);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  }
  /* send event to scb */
  else {
    evt.msg.security_rsp.p_data = p_data;
    evt.msg.security_rsp.len = len;
    avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_SecurityRsp
 *
 * Description      Respond to a security request from the peer device.
 *                  This function must be called if the application receives
 *                  an AVDT_SECURITY_IND_EVT through its control callback.
 *                  (Please note that AVDTP security procedures are unrelated
 *                  to Bluetooth link level security.)
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_SecurityRsp(uint8_t handle, uint8_t label, uint8_t error_code,
                          uint8_t* p_data, uint16_t len) {
  tAVDT_SCB* p_scb;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_SCB_EVT evt;

  AVDT_TRACE_DEBUG("%s: handle=%d label=%d error_code=%d len=%d", __func__,
                   handle, label, error_code, len);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  }
  /* send event to scb */
  else {
    evt.msg.security_rsp.hdr.err_code = error_code;
    evt.msg.security_rsp.hdr.label = label;
    evt.msg.security_rsp.p_data = p_data;
    evt.msg.security_rsp.len = len;
    avdt_scb_event(p_scb, AVDT_SCB_API_SECURITY_RSP_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_WriteReqOpt
 *
 * Description      Send a media packet to the peer device.  The stream must
 *                  be started before this function is called.  Also, this
 *                  function can only be called if the stream is a SRC.
 *
 *                  When AVDTP has sent the media packet and is ready for the
 *                  next packet, an AVDT_WRITE_CFM_EVT is sent to the
 *                  application via the control callback.  The application must
 *                  wait for the AVDT_WRITE_CFM_EVT before it makes the next
 *                  call to AVDT_WriteReq().  If the applications calls
 *                  AVDT_WriteReq() before it receives the event the packet
 *                  will not be sent.  The application may make its first call
 *                  to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT
 *                  or AVDT_START_IND_EVT.
 *
 *                  The application passes the packet using the BT_HDR
 *                  structure.
 *                  This structure is described in section 2.1.  The offset
 *                  field must be equal to or greater than AVDT_MEDIA_OFFSET
 *                  (if NO_RTP is specified, L2CAP_MIN_OFFSET can be used).
 *                  This allows enough space in the buffer for the L2CAP and
 *                  AVDTP headers.
 *
 *                  The memory pointed to by p_pkt must be a GKI buffer
 *                  allocated by the application.  This buffer will be freed
 *                  by the protocol stack; the application must not free
 *                  this buffer.
 *
 *                  The opt parameter allows passing specific options like:
 *                  - NO_RTP : do not add the RTP header to buffer
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_WriteReqOpt(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp,
                          uint8_t m_pt, tAVDT_DATA_OPT_MASK opt) {
  tAVDT_SCB* p_scb;
  tAVDT_SCB_EVT evt;
  uint16_t result = AVDT_SUCCESS;

  AVDT_TRACE_DEBUG("%s: handle=%d timestamp=%d m_pt=0x%x opt=0x%x", __func__,
                   handle, time_stamp, m_pt, opt);

  /* map handle to scb */
  p_scb = avdt_scb_by_hdl(handle);
  if (p_scb == NULL) {
    result = AVDT_BAD_HANDLE;
  } else {
    evt.apiwrite.p_buf = p_pkt;
    evt.apiwrite.time_stamp = time_stamp;
    evt.apiwrite.m_pt = m_pt;
    evt.apiwrite.opt = opt;
    avdt_scb_event(p_scb, AVDT_SCB_API_WRITE_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_WriteReq
 *
 * Description      Send a media packet to the peer device.  The stream must
 *                  be started before this function is called.  Also, this
 *                  function can only be called if the stream is a SRC.
 *
 *                  When AVDTP has sent the media packet and is ready for the
 *                  next packet, an AVDT_WRITE_CFM_EVT is sent to the
 *                  application via the control callback.  The application must
 *                  wait for the AVDT_WRITE_CFM_EVT before it makes the next
 *                  call to AVDT_WriteReq().  If the applications calls
 *                  AVDT_WriteReq() before it receives the event the packet
 *                  will not be sent.  The application may make its first call
 *                  to AVDT_WriteReq() after it receives an AVDT_START_CFM_EVT
 *                  or AVDT_START_IND_EVT.
 *
 *                  The application passes the packet using the BT_HDR
 *                  structure.
 *                  This structure is described in section 2.1.  The offset
 *                  field must be equal to or greater than AVDT_MEDIA_OFFSET.
 *                  This allows enough space in the buffer for the L2CAP and
 *                  AVDTP headers.
 *
 *                  The memory pointed to by p_pkt must be a GKI buffer
 *                  allocated by the application.  This buffer will be freed
 *                  by the protocol stack; the application must not free
 *                  this buffer.
 *
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_WriteReq(uint8_t handle, BT_HDR* p_pkt, uint32_t time_stamp,
                       uint8_t m_pt) {
  return AVDT_WriteReqOpt(handle, p_pkt, time_stamp, m_pt, AVDT_DATA_OPT_NONE);
}

/*******************************************************************************
 *
 * Function         AVDT_ConnectReq
 *
 * Description      This function initiates an AVDTP signaling connection
 *                  to the peer device.  When the connection is completed, an
 *                  AVDT_CONNECT_IND_EVT is sent to the application via its
 *                  control callback function.  If the connection attempt fails
 *                  an AVDT_DISCONNECT_IND_EVT is sent.  The security mask
 *                  parameter overrides the outgoing security mask set in
 *                  AVDT_Register().
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_ConnectReq(const RawAddress& bd_addr, uint8_t sec_mask,
                         tAVDT_CTRL_CBACK* p_cback) {
  tAVDT_CCB* p_ccb = NULL;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_CCB_EVT evt;

  AVDT_TRACE_DEBUG("%s: sec_mask=0x%x", __func__, sec_mask);

  /* find channel control block for this bd addr; if none, allocate one */
  p_ccb = avdt_ccb_by_bd(bd_addr);
  if (p_ccb == NULL) {
    p_ccb = avdt_ccb_alloc(bd_addr);
    if (p_ccb == NULL) {
      /* could not allocate channel control block */
      result = AVDT_NO_RESOURCES;
    }
  } else if (p_ccb->ll_opened == false) {
    AVDT_TRACE_WARNING("AVDT_ConnectReq: CCB LL is in the middle of opening");

    /* ccb was already allocated for the incoming signalling. */
    result = AVDT_BUSY;
  }

  if (result == AVDT_SUCCESS) {
    /* send event to ccb */
    evt.connect.p_cback = p_cback;
    evt.connect.sec_mask = sec_mask;
    avdt_ccb_event(p_ccb, AVDT_CCB_API_CONNECT_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_DisconnectReq
 *
 * Description      This function disconnect an AVDTP signaling connection
 *                  to the peer device.  When disconnected an
 *                  AVDT_DISCONNECT_IND_EVT is sent to the application via its
 *                  control callback function.
 *
 * Returns          AVDT_SUCCESS if successful, otherwise error.
 *
 ******************************************************************************/
uint16_t AVDT_DisconnectReq(const RawAddress& bd_addr,
                            tAVDT_CTRL_CBACK* p_cback) {
  tAVDT_CCB* p_ccb = NULL;
  uint16_t result = AVDT_SUCCESS;
  tAVDT_CCB_EVT evt;

  AVDT_TRACE_WARNING("%s: address=%s", __func__, bd_addr.ToString().c_str());

  /* find channel control block for this bd addr; if none, error */
  p_ccb = avdt_ccb_by_bd(bd_addr);
  if (p_ccb == NULL) {
    result = AVDT_BAD_PARAMS;
  }

  if (result == AVDT_SUCCESS) {
    /* send event to ccb */
    evt.disconnect.p_cback = p_cback;
    avdt_ccb_event(p_ccb, AVDT_CCB_API_DISCONNECT_REQ_EVT, &evt);
  }

  AVDT_TRACE_DEBUG("%s: address=%s result=%d", __func__,
                   bd_addr.ToString().c_str(), result);

  return result;
}

/*******************************************************************************
 *
 * Function         AVDT_GetL2CapChannel
 *
 * Description      Get the L2CAP CID used by the handle.
 *
 * Returns          CID if successful, otherwise 0.
 *
 ******************************************************************************/
uint16_t AVDT_GetL2CapChannel(uint8_t handle) {
  tAVDT_SCB* p_scb;
  tAVDT_CCB* p_ccb;
  uint8_t tcid;
  uint16_t lcid = 0;

  /* map handle to scb */
  if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) &&
      ((p_ccb = p_scb->p_ccb) != NULL)) {
    /* get tcid from type, scb */
    tcid = avdt_ad_type_to_tcid(AVDT_CHAN_MEDIA, p_scb);

    lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
  }

  return (lcid);
}

/*******************************************************************************
 *
 * Function         AVDT_GetSignalChannel
 *
 * Description      Get the L2CAP CID used by the signal channel of the given
 *                  handle.
 *
 * Returns          CID if successful, otherwise 0.
 *
 ******************************************************************************/
uint16_t AVDT_GetSignalChannel(uint8_t handle, const RawAddress& bd_addr) {
  tAVDT_SCB* p_scb;
  tAVDT_CCB* p_ccb;
  uint8_t tcid = 0; /* tcid is always 0 for signal channel */
  uint16_t lcid = 0;

  /* map handle to scb */
  if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) &&
      ((p_ccb = p_scb->p_ccb) != NULL)) {
    lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
  } else {
    p_ccb = avdt_ccb_by_bd(bd_addr);
    if (p_ccb != NULL) {
      lcid = avdt_cb.ad.rt_tbl[avdt_ccb_to_idx(p_ccb)][tcid].lcid;
    }
  }

  return (lcid);
}

#if (AVDT_REPORTING == TRUE)
/*******************************************************************************
 *
 * Function         AVDT_SendReport
 *
 * Description
 *
 *
 *
 * Returns
 *
 ******************************************************************************/
uint16_t AVDT_SendReport(uint8_t handle, AVDT_REPORT_TYPE type,
                         tAVDT_REPORT_DATA* p_data) {
  tAVDT_SCB* p_scb;
  uint16_t result = AVDT_BAD_PARAMS;
  tAVDT_TC_TBL* p_tbl;
  uint8_t *p, *plen, *pm1, *p_end;
  uint32_t ssrc;
  uint16_t len;

  AVDT_TRACE_DEBUG("%s: handle=%d type=%d", __func__, handle, type);

  /* map handle to scb && verify parameters */
  if (((p_scb = avdt_scb_by_hdl(handle)) != NULL) && (p_scb->p_ccb != NULL) &&
      (((type == AVDT_RTCP_PT_SR) && (p_scb->cs.tsep == AVDT_TSEP_SRC)) ||
       ((type == AVDT_RTCP_PT_RR) && (p_scb->cs.tsep == AVDT_TSEP_SNK)) ||
       (type == AVDT_RTCP_PT_SDES))) {
    result = AVDT_NO_RESOURCES;

    /* build SR - assume fit in one packet */
    p_tbl = avdt_ad_tc_tbl_by_type(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb);
    if (p_tbl->state == AVDT_AD_ST_OPEN) {
      BT_HDR* p_pkt = (BT_HDR*)osi_malloc(p_tbl->peer_mtu + sizeof(BT_HDR));

      p_pkt->offset = L2CAP_MIN_OFFSET;
      p = (uint8_t*)(p_pkt + 1) + p_pkt->offset;
      pm1 = p;
      *p++ = AVDT_MEDIA_OCTET1 | 1;
      *p++ = type;
      /* save the location for length */
      plen = p;
      p += 2;
      ssrc = avdt_scb_gen_ssrc(p_scb);
      UINT32_TO_BE_STREAM(p, ssrc);

      switch (type) {
        case AVDT_RTCP_PT_SR: /* Sender Report */
          *pm1 = AVDT_MEDIA_OCTET1;
          UINT32_TO_BE_STREAM(p, p_data->sr.ntp_sec);
          UINT32_TO_BE_STREAM(p, p_data->sr.ntp_frac);
          UINT32_TO_BE_STREAM(p, p_data->sr.rtp_time);
          UINT32_TO_BE_STREAM(p, p_data->sr.pkt_count);
          UINT32_TO_BE_STREAM(p, p_data->sr.octet_count);
          break;

        case AVDT_RTCP_PT_RR: /* Receiver Report */
          *p++ = p_data->rr.frag_lost;
          AVDT_TRACE_API("packet_lost: %d", p_data->rr.packet_lost);
          p_data->rr.packet_lost &= 0xFFFFFF;
          AVDT_TRACE_API("packet_lost: %d", p_data->rr.packet_lost);
          UINT24_TO_BE_STREAM(p, p_data->rr.packet_lost);
          UINT32_TO_BE_STREAM(p, p_data->rr.seq_num_rcvd);
          UINT32_TO_BE_STREAM(p, p_data->rr.jitter);
          UINT32_TO_BE_STREAM(p, p_data->rr.lsr);
          UINT32_TO_BE_STREAM(p, p_data->rr.dlsr);
          break;

        case AVDT_RTCP_PT_SDES: /* Source Description */
          *p++ = AVDT_RTCP_SDES_CNAME;
          len = strlen((char*)p_data->cname);
          if (len > AVDT_MAX_CNAME_SIZE) len = AVDT_MAX_CNAME_SIZE;
          *p++ = (uint8_t)len;
          strlcpy((char*)p, (char*)p_data->cname, len + 1);
          p += len;
          break;
      }
      p_end = p;
      len = p - pm1 - 1;
      UINT16_TO_BE_STREAM(plen, len);

      /* set the actual payload length */
      p_pkt->len = p_end - p;
      /* send the packet */
      if (L2CAP_DW_FAILED !=
          avdt_ad_write_req(AVDT_CHAN_REPORT, p_scb->p_ccb, p_scb, p_pkt))
        result = AVDT_SUCCESS;
    }
  }

  AVDT_TRACE_DEBUG("%s: result=%d", __func__, result);

  return result;
}
#endif

/******************************************************************************
 *
 * Function         AVDT_SetTraceLevel
 *
 * Description      Sets the trace level for AVDT. If 0xff is passed, the
 *                  current trace level is returned.
 *
 *                  Input Parameters:
 *                      new_level:  The level to set the AVDT tracing to:
 *                      0xff-returns the current setting.
 *                      0-turns off tracing.
 *                      >= 1-Errors.
 *                      >= 2-Warnings.
 *                      >= 3-APIs.
 *                      >= 4-Events.
 *                      >= 5-Debug.
 *
 * Returns          The new trace level or current trace level if
 *                  the input parameter is 0xff.
 *
 *****************************************************************************/
uint8_t AVDT_SetTraceLevel(uint8_t new_level) {
  if (new_level != 0xFF) avdt_cb.trace_level = new_level;

  return (avdt_cb.trace_level);
}
