Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f864dc75 authored by Himanshu Rawat's avatar Himanshu Rawat
Browse files

Transmit pending L2CAP fixed channel packets before closing

SMP closes L2CAP channel immediately after sending its last packet.
L2CAP may not have transmitted all the SMP packets by the time it
receives remove request. As a result, it may end up flushing the pending
SMP packets. This causes the SMP pairing to fail on the remote device
due to timeout.
The change ensures that pending packages in L2CAP fixed channels are
transmitted when the channel is closed locally.

Test: mmm packages/modules/Bluetooth
Test: Manual | Pair with LE-only devices. Pair with dual mode devices
over BR/EDR
Flag: com.android.bluetooth.flags.transmit_smp_packets_before_release
Bug: 314417619
Bug: 357106628

Change-Id: I7e474f14ce5e8195e2f67e3ac5af8e2c8c2cb10f
parent a233a7dd
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -364,9 +364,8 @@ uint16_t L2CA_SendFixedChnlData(uint16_t fixed_cid, const RawAddress& rem_bda, B
 *
 *  Parameters:     Fixed CID
 *                  BD Address of remote
 *                  Idle timeout to use (or 0xFFFF if don't care)
 *
 *  Return value:   true if channel removed
 *  Return value:   true if channel removed or marked for removal
 *
 ******************************************************************************/
bool L2CA_RemoveFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda);
+1 −2
Original line number Diff line number Diff line
@@ -794,9 +794,8 @@ struct tL2CAP_FIXED_CHNL_REG {
 *
 *  Parameters:     Fixed CID
 *                  BD Address of remote
 *                  Idle timeout to use (or 0xFFFF if don't care)
 *
 *  Return value:   true if channel removed
 *  Return value:   true if channel removed or marked for removal
 *
 ******************************************************************************/
[[nodiscard]] bool L2CA_RemoveFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda);
+35 −17
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@
#include <base/location.h>
#include <base/strings/stringprintf.h>
#include <bluetooth/log.h>
#include <com_android_bluetooth_flags.h>

#include <cstdint>
#include <string>
@@ -1214,6 +1215,9 @@ bool L2CA_ConnectFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda) {
      return true;
    }

    // Restore the fixed channel if it was suspended
    l2cu_fixed_channel_restore(p_lcb, fixed_cid);

    (*l2cb.fixed_reg[fixed_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedConn_Cb)(
            fixed_cid, p_lcb->remote_bd_addr, true, 0, p_lcb->transport);
    return true;
@@ -1307,28 +1311,29 @@ tL2CAP_DW_RESULT L2CA_SendFixedChnlData(uint16_t fixed_cid, const RawAddress& re
  p_buf->event = 0;
  p_buf->layer_specific = L2CAP_FLUSHABLE_CH_BASED;

  if (!p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]) {
  tL2C_CCB* p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];

  if (p_ccb == nullptr) {
    if (!l2cu_initialize_fixed_ccb(p_lcb, fixed_cid)) {
      log::warn("No channel control block found for CID: 0x{:4x}", fixed_cid);
      osi_free(p_buf);
      return tL2CAP_DW_RESULT::FAILED;
    }
    p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];
  }

  if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) {
    log::warn(
            "Unable to send data due to congestion CID: 0x{:04x} "
            "xmit_hold_q.count: {} buff_quota: {}",
            fixed_cid,
            fixed_queue_length(
                    p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->xmit_hold_q),
            p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->buff_quota);
  // Sending packets over fixed channel reinstates them
  l2cu_fixed_channel_restore(p_lcb, fixed_cid);

  if (p_ccb->cong_sent) {
    log::warn("Link congestion CID: 0x{:04x} xmit_hold_q.count: {} buff_quota: {}", fixed_cid,
              fixed_queue_length(p_ccb->xmit_hold_q), p_ccb->buff_quota);
    osi_free(p_buf);
    return tL2CAP_DW_RESULT::FAILED;
  }

  log::debug("Enqueued data for CID: 0x{:04x} len:{}", fixed_cid, p_buf->len);
  l2c_enqueue_peer_data(p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL], p_buf);
  l2c_enqueue_peer_data(p_ccb, p_buf);

  l2c_link_check_send_pkts(p_lcb, 0, NULL);

@@ -1338,7 +1343,7 @@ tL2CAP_DW_RESULT L2CA_SendFixedChnlData(uint16_t fixed_cid, const RawAddress& re
    l2cu_no_dynamic_ccbs(p_lcb);
  }

  if (p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL]->cong_sent) {
  if (p_ccb->cong_sent) {
    log::debug("Link congested for CID: 0x{:04x}", fixed_cid);
    return tL2CAP_DW_RESULT::CONGESTED;
  }
@@ -1354,13 +1359,11 @@ tL2CAP_DW_RESULT L2CA_SendFixedChnlData(uint16_t fixed_cid, const RawAddress& re
 *
 *  Parameters:     Fixed CID
 *                  BD Address of remote
 *                  Idle timeout to use (or 0xFFFF if don't care)
 *
 *  Return value:   true if channel removed
 *  Return value:   true if channel removed or marked for removal
 *
 ******************************************************************************/
bool L2CA_RemoveFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda) {
  tL2C_CCB* p_ccb;
  tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;

  /* Check CID is valid and registered */
@@ -1382,11 +1385,22 @@ bool L2CA_RemoveFixedChnl(uint16_t fixed_cid, const RawAddress& rem_bda) {
    return false;
  }

  log::verbose("BDA: {} CID: 0x{:04x}", rem_bda, fixed_cid);

  /* Release the CCB, starting an inactivity timeout on the LCB if no other CCBs
   * exist */
  p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];
  tL2C_CCB* p_ccb = p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL];

  if (com::android::bluetooth::flags::transmit_smp_packets_before_release() && p_ccb->in_use &&
      !fixed_queue_is_empty(p_ccb->xmit_hold_q)) {
    if (l2cu_fixed_channel_suspended(p_lcb, fixed_cid)) {
      log::warn("Removal of BDA: {} CID: 0x{:04x} already pending", rem_bda, fixed_cid);
    } else {
      p_lcb->suspended.push_back(fixed_cid);
      log::info("Waiting for transmit queue to clear, BDA: {} CID: 0x{:04x}", rem_bda, fixed_cid);
    }
    return true;
  }

  log::verbose("BDA: {} CID: 0x{:04x}", rem_bda, fixed_cid);

  p_lcb->p_fixed_ccbs[fixed_cid - L2CAP_FIRST_FIXED_CHNL] = NULL;
  p_lcb->SetDisconnectReason(HCI_ERR_CONN_CAUSE_LOCAL_HOST);
@@ -1744,6 +1758,10 @@ void L2CA_Dumpsys(int fd) {
                  ccb->in_use ? "true" : "false");
      ccb = ccb->p_next_ccb;
    }

    for (auto fixed_cid : lcb.suspended) {
      LOG_DUMPSYS(fd, "  pending removal fixed CID: 0x%04x", fixed_cid);
    }
  }
}
#undef DUMPSYS_TAG
+1 −2
Original line number Diff line number Diff line
@@ -1137,8 +1137,7 @@ static void l2c_csm_config(tL2C_CCB* p_ccb, tL2CEVT event, void* p_data) {
          if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb !=
              nullptr) {
            p_ccb->metrics.rx(static_cast<BT_HDR*>(p_data)->len);
            (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(
                    p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, (BT_HDR*)p_data);
            l2cu_fixed_channel_data_cb(p_lcb, p_ccb->local_cid, reinterpret_cast<BT_HDR*>(p_data));
          } else {
            if (p_data != nullptr) {
              osi_free_and_reset(&p_data);
+1 −2
Original line number Diff line number Diff line
@@ -1219,8 +1219,7 @@ static bool do_sar_reassembly(tL2C_CCB* p_ccb, BT_HDR* p_buf, uint16_t ctrl_word
    if (p_ccb->local_cid < L2CAP_BASE_APPL_CID &&
        (p_ccb->local_cid >= L2CAP_FIRST_FIXED_CHNL && p_ccb->local_cid <= L2CAP_LAST_FIXED_CHNL)) {
      if (l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb) {
        (*l2cb.fixed_reg[p_ccb->local_cid - L2CAP_FIRST_FIXED_CHNL].pL2CA_FixedData_Cb)(
                p_ccb->local_cid, p_ccb->p_lcb->remote_bd_addr, p_buf);
        l2cu_fixed_channel_data_cb(p_ccb->p_lcb, p_ccb->local_cid, p_buf);
      }
    } else {
      l2c_csm_execute(p_ccb, L2CEVT_L2CAP_DATA, p_buf);
Loading