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

Commit e778518d authored by Łukasz Rymanowski's avatar Łukasz Rymanowski
Browse files

gatt: Fix handling MTU Exchange request

With this patch Android will always do only one MTU Exchange request as
per specification. For this reason Android always uses
GATT_MAX_MTU_SIZE as this is what easly GATT can handle in the Bluetooth
Stack.

This is done so the single application request cannot block other
applications from using bigger MTU than the one requested by the first
application.

For backward compatibility, the for Data Length Extension the MTU from
the User is used and not the one which was internally requested by the GATT

Bug: 257250188
Bug: 239776509
Test: atest BluetoothInstrumentationTests
Test: manual testing
Tag: #feature
Change-Id: I157d40040b53cd4595403d99e58e105709099684
parent a8bac58b
Loading
Loading
Loading
Loading
+58 −14
Original line number Diff line number Diff line
@@ -62,10 +62,6 @@ static void bta_gattc_conn_cback(tGATT_IF gattc_if, const RawAddress& bda,
static void bta_gattc_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
                                 tGATT_STATUS status,
                                 tGATT_CL_COMPLETE* p_data);
static void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                                   tGATT_STATUS status,
                                   tGATT_CL_COMPLETE* p_data);

static void bta_gattc_deregister_cmpl(tBTA_GATTC_RCB* p_clreg);
static void bta_gattc_enc_cmpl_cback(tGATT_IF gattc_if, const RawAddress& bda);
static void bta_gattc_cong_cback(uint16_t conn_id, bool congested);
@@ -742,6 +738,42 @@ void bta_gattc_restart_discover(tBTA_GATTC_CLCB* p_clcb,

/** Configure MTU size on the GATT connection */
void bta_gattc_cfg_mtu(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
  uint16_t current_mtu = 0;
  auto result = GATTC_TryMtuRequest(p_clcb->bda, p_clcb->transport,
                                    p_clcb->bta_conn_id, &current_mtu);
  switch (result) {
    case MTU_EXCHANGE_DEVICE_DISCONNECTED:
      LOG_INFO("Device %s disconnected", ADDRESS_TO_LOGGABLE_CSTR(p_clcb->bda));
      bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                             GATT_NO_RESOURCES, NULL);
      bta_gattc_continue(p_clcb);
      return;
    case MTU_EXCHANGE_NOT_ALLOWED:
      LOG_INFO("Not allowed for BR/EDR devices %s",
               ADDRESS_TO_LOGGABLE_CSTR(p_clcb->bda));
      bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                             GATT_ERR_UNLIKELY, NULL);
      bta_gattc_continue(p_clcb);
      return;
    case MTU_EXCHANGE_ALREADY_DONE:
      /* Check if MTU is not already set, if so, just report it back to the user
       * and continue with other requests.
       */
      GATTC_UpdateUserAttMtuIfNeeded(p_clcb->bda, p_clcb->transport,
                                     p_data->api_mtu.mtu);
      bta_gattc_send_mtu_response(p_clcb, p_data, current_mtu);
      return;
    case MTU_EXCHANGE_IN_PROGRESS:
      LOG_INFO("Enqueue MTU Request  - waiting for response on p_clcb %p",
               p_clcb);
      bta_gattc_enqueue(p_clcb, p_data);
      return;

    case MTU_EXCHANGE_NOT_DONE_YET:
      /* OK to proceed */
      break;
  }

  if (bta_gattc_enqueue(p_clcb, p_data) == ENQUEUED_FOR_LATER) return;

  tGATT_STATUS status =
@@ -1171,18 +1203,31 @@ void bta_gattc_op_cmpl(tBTA_GATTC_CLCB* p_clcb, const tBTA_GATTC_DATA* p_data) {
  }

  /* service handle change void the response, discard it */
  if (op == GATTC_OPTYPE_READ)
  if (op == GATTC_OPTYPE_READ) {
    bta_gattc_read_cmpl(p_clcb, &p_data->op_cmpl);

  else if (op == GATTC_OPTYPE_WRITE)
  } else if (op == GATTC_OPTYPE_WRITE) {
    bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl);

  else if (op == GATTC_OPTYPE_EXE_WRITE)
  } else if (op == GATTC_OPTYPE_EXE_WRITE) {
    bta_gattc_exec_cmpl(p_clcb, &p_data->op_cmpl);

  else if (op == GATTC_OPTYPE_CONFIG)
  } else if (op == GATTC_OPTYPE_CONFIG) {
    bta_gattc_cfg_mtu_cmpl(p_clcb, &p_data->op_cmpl);

    /* If there are more clients waiting for the MTU results on the same device,
     * lets trigger them now.
     */

    auto outstanding_conn_ids =
        GATTC_GetAndRemoveListOfConnIdsWaitingForMtuRequest(p_clcb->bda);
    for (auto conn_id : outstanding_conn_ids) {
      tBTA_GATTC_CLCB* p_clcb = bta_gattc_find_clcb_by_conn_id(conn_id);
      LOG_DEBUG("Continue MTU request clcb %p", p_clcb);
      if (p_clcb) {
        LOG_DEBUG("Continue MTU request for client conn_id=0x%04x", conn_id);
        bta_gattc_continue(p_clcb);
      }
    }
  }

  // If receive DATABASE_OUT_OF_SYNC error code, bta_gattc should start service
  // discovery immediately
  if (bta_gattc_is_robust_caching_enabled() &&
@@ -1542,9 +1587,8 @@ static void bta_gattc_cmpl_cback(uint16_t conn_id, tGATTC_OPTYPE op,
}

/** client operation complete send message */
static void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                                   tGATT_STATUS status,
                                   tGATT_CL_COMPLETE* p_data) {
void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                            tGATT_STATUS status, tGATT_CL_COMPLETE* p_data) {
  const size_t len = sizeof(tBTA_GATTC_OP_CMPL) + sizeof(tGATT_CL_COMPLETE);
  tBTA_GATTC_OP_CMPL* p_buf = (tBTA_GATTC_OP_CMPL*)osi_calloc(len);

+6 −0
Original line number Diff line number Diff line
@@ -445,6 +445,12 @@ extern BtaEnqueuedResult_t bta_gattc_enqueue(tBTA_GATTC_CLCB* p_clcb,
extern bool bta_gattc_is_data_queued(tBTA_GATTC_CLCB* p_clcb,
                                     const tBTA_GATTC_DATA* p_data);
extern void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb);
extern void bta_gattc_send_mtu_response(tBTA_GATTC_CLCB* p_clcb,
                                        const tBTA_GATTC_DATA* p_data,
                                        uint16_t current_mtu);
extern void bta_gattc_cmpl_sendmsg(uint16_t conn_id, tGATTC_OPTYPE op,
                                   tGATT_STATUS status,
                                   tGATT_CL_COMPLETE* p_data);

extern bool bta_gattc_check_notif_registry(tBTA_GATTC_RCB* p_clreg,
                                           tBTA_GATTC_SERV* p_srcb,
+63 −7
Original line number Diff line number Diff line
@@ -359,6 +359,27 @@ tBTA_GATTC_SERV* bta_gattc_srcb_alloc(const RawAddress& bda) {
  return p_tcb;
}

void bta_gattc_send_mtu_response(tBTA_GATTC_CLCB* p_clcb,
                                 const tBTA_GATTC_DATA* p_data,
                                 uint16_t current_mtu) {
  GATT_CONFIGURE_MTU_OP_CB cb = p_data->api_mtu.mtu_cb;
  if (cb) {
    void* my_cb_data = p_data->api_mtu.mtu_cb_data;
    cb(p_clcb->bta_conn_id, GATT_SUCCESS, my_cb_data);
  }

  tBTA_GATTC cb_data;
  p_clcb->status = GATT_SUCCESS;
  cb_data.cfg_mtu.conn_id = p_clcb->bta_conn_id;
  cb_data.cfg_mtu.status = GATT_SUCCESS;

  cb_data.cfg_mtu.mtu = current_mtu;

  if (p_clcb->p_rcb) {
    (*p_clcb->p_rcb->p_cback)(BTA_GATTC_CFG_MTU_EVT, &cb_data);
  }
}

void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb) {
  if (p_clcb->p_q_cmd != NULL) {
    LOG_INFO("Already scheduled another request for conn_id = 0x%04x",
@@ -366,14 +387,49 @@ void bta_gattc_continue(tBTA_GATTC_CLCB* p_clcb) {
    return;
  }

  if (p_clcb->p_q_cmd_queue.empty()) {
    LOG_INFO("Nothing to do for conn_id = 0x%04x", p_clcb->bta_conn_id);
  while (!p_clcb->p_q_cmd_queue.empty()) {
    const tBTA_GATTC_DATA* p_q_cmd = p_clcb->p_q_cmd_queue.front();
    if (p_q_cmd->hdr.event != BTA_GATTC_API_CFG_MTU_EVT) {
      p_clcb->p_q_cmd_queue.pop_front();
      bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
      return;
    }

  const tBTA_GATTC_DATA* p_q_cmd = p_clcb->p_q_cmd_queue.front();
    /* The p_q_cmd is the MTU Request event. */
    uint16_t current_mtu = 0;
    auto result = GATTC_TryMtuRequest(p_clcb->bda, p_clcb->transport,
                                      p_clcb->bta_conn_id, &current_mtu);
    switch (result) {
      case MTU_EXCHANGE_DEVICE_DISCONNECTED:
        bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                               GATT_NO_RESOURCES, NULL);
        /* Handled, free command below and continue with a p_q_cmd_queue */
        break;
      case MTU_EXCHANGE_NOT_ALLOWED:
        bta_gattc_cmpl_sendmsg(p_clcb->bta_conn_id, GATTC_OPTYPE_CONFIG,
                               GATT_ERR_UNLIKELY, NULL);
        /* Handled, free command below and continue with a p_q_cmd_queue */
        break;
      case MTU_EXCHANGE_ALREADY_DONE:
        bta_gattc_send_mtu_response(p_clcb, p_q_cmd, current_mtu);
        /* Handled, free command below and continue with a p_q_cmd_queue */
        break;
      case MTU_EXCHANGE_IN_PROGRESS:
        LOG_WARN("Waiting p_clcb %p", p_clcb);
        return;
      case MTU_EXCHANGE_NOT_DONE_YET:
        p_clcb->p_q_cmd_queue.pop_front();
        bta_gattc_sm_execute(p_clcb, p_q_cmd->hdr.event, p_q_cmd);
        return;
    }

    /* p_q_cmd was the MTU request and it was handled.
     * If MTU request was handled without actually ATT request,
     * it is ok to take another message from the queue and proceed.
     */
    p_clcb->p_q_cmd_queue.pop_front();
    osi_free_and_reset((void**)&p_q_cmd);
  }
}

bool bta_gattc_is_data_queued(tBTA_GATTC_CLCB* p_clcb,
+133 −4
Original line number Diff line number Diff line
@@ -713,15 +713,144 @@ tGATT_STATUS GATTC_ConfigureMTU(uint16_t conn_id, uint16_t mtu) {

  /* For this request only ATT CID is valid */
  p_clcb->cid = L2CAP_ATT_CID;
  p_clcb->p_tcb->payload_size = mtu;
  p_clcb->operation = GATTC_OPTYPE_CONFIG;
  tGATT_CL_MSG gatt_cl_msg;
  gatt_cl_msg.mtu = mtu;
  LOG_DEBUG("Configuring ATT mtu size conn_id:%hu mtu:%hu", conn_id, mtu);

  bluetooth::shim::arbiter::GetArbiter().OnOutgoingMtuReq(tcb_idx);

  return attp_send_cl_msg(*p_clcb->p_tcb, p_clcb, GATT_REQ_MTU, &gatt_cl_msg);
  /* Since GATT MTU Exchange can be done only once, and it is impossible to
   * predict what MTU will be requested by other applications, let's use
   * max possible MTU in the request. */
  gatt_cl_msg.mtu = GATT_MAX_MTU_SIZE;

  LOG_INFO("Configuring ATT mtu size conn_id:%hu mtu:%hu user mtu %hu", conn_id,
           gatt_cl_msg.mtu, mtu);

  auto result =
      attp_send_cl_msg(*p_clcb->p_tcb, p_clcb, GATT_REQ_MTU, &gatt_cl_msg);
  if (result == GATT_SUCCESS) {
    p_clcb->p_tcb->pending_user_mtu_exchange_value = mtu;
  }
  return result;
}

/******************************************************************************
 *
 * Function         GATTC_TryMtuRequest
 *
 * Description      This function shall be called before calling
 *                  GATTC_ConfgureMTU in order to check if operation is
 *                  available to do.
 *
 * Parameters        remote_bda : peer device address. (input)
 *                   transport  : physical transport of the GATT connection
 *                                 (BR/EDR or LE) (input)
 *                   conn_id    : connection id  (input)
 *                   current_mtu: current mtu on the link (output)
 *
 * Returns          tGATTC_TryMtuRequestResult:
 *                  - MTU_EXCHANGE_NOT_DONE_YET: There was no MTU Exchange
 *                      procedure on the link. User can call GATTC_ConfigureMTU
 *                      now.
 *                  - MTU_EXCHANGE_NOT_ALLOWED : Not allowed for BR/EDR or if
 *                      link does not exist
 *                  - MTU_EXCHANGE_ALREADY_DONE: MTU Exchange is done. MTU
 *                      should be taken from current_mtu
 *                  - MTU_EXCHANGE_IN_PROGRESS : Other use is doing MTU
 *                      Exchange. Conn_id is stored for result.
 *
 ******************************************************************************/
tGATTC_TryMtuRequestResult GATTC_TryMtuRequest(const RawAddress& remote_bda,
                                               tBT_TRANSPORT transport,
                                               uint16_t conn_id,
                                               uint16_t* current_mtu) {
  LOG_INFO("%s conn_id=0x%04x", remote_bda.ToString().c_str(), conn_id);
  *current_mtu = GATT_DEF_BLE_MTU_SIZE;

  if (transport == BT_TRANSPORT_BR_EDR) {
    LOG_ERROR("Device %s connected over BR/EDR",
              ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return MTU_EXCHANGE_NOT_ALLOWED;
  }

  tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(remote_bda, transport);
  if (!p_tcb) {
    LOG_ERROR("Device %s is not connected ",
              ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return MTU_EXCHANGE_DEVICE_DISCONNECTED;
  }

  if (gatt_is_pending_mtu_exchange(p_tcb)) {
    LOG_DEBUG("Continue MTU pending for other client.");
    /* MTU Exchange is in progress, started by other GATT Client.
     * Wait until it is completed.
     */
    gatt_set_conn_id_waiting_for_mtu_exchange(p_tcb, conn_id);
    return MTU_EXCHANGE_IN_PROGRESS;
  }

  uint16_t mtu = gatt_get_mtu(remote_bda, transport);
  if (mtu == GATT_DEF_BLE_MTU_SIZE || mtu == 0) {
    LOG_DEBUG("MTU not yet updated for %s",
              ADDRESS_TO_LOGGABLE_CSTR(remote_bda));
    return MTU_EXCHANGE_NOT_DONE_YET;
  }

  *current_mtu = mtu;
  return MTU_EXCHANGE_ALREADY_DONE;
}

/*******************************************************************************
 * Function         GATTC_UpdateUserAttMtuIfNeeded
 *
 * Description      This function to be called when user requested MTU after
 *                  MTU Exchange has been already done. This will update data
 *                  length in the controller.
 *
 * Parameters        remote_bda : peer device address. (input)
 *                   transport  : physical transport of the GATT connection
 *                                 (BR/EDR or LE) (input)
 *                   user_mtu: user request mtu
 *
 ******************************************************************************/
void GATTC_UpdateUserAttMtuIfNeeded(const RawAddress& remote_bda,
                                    tBT_TRANSPORT transport,
                                    uint16_t user_mtu) {
  LOG_INFO("%s, mtu=%hu", ADDRESS_TO_LOGGABLE_CSTR(remote_bda), user_mtu);
  tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(remote_bda, transport);
  if (!p_tcb) {
    LOG_WARN("Transport control block not found");
    return;
  }

  LOG_INFO("%s, current mtu: %d, max_user_mtu:%d, user_mtu: %d",
           ADDRESS_TO_LOGGABLE_CSTR(remote_bda), p_tcb->payload_size,
           p_tcb->max_user_mtu, user_mtu);

  if (p_tcb->payload_size < user_mtu) {
    LOG_INFO("User requested more than what GATT can handle. Trim it.");
    user_mtu = p_tcb->payload_size;
  }

  if (p_tcb->max_user_mtu >= user_mtu) {
    return;
  }

  p_tcb->max_user_mtu = user_mtu;
  BTM_SetBleDataLength(remote_bda, user_mtu);
}

std::list<uint16_t> GATTC_GetAndRemoveListOfConnIdsWaitingForMtuRequest(
    const RawAddress& remote_bda) {
  std::list result = std::list<uint16_t>();

  tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(remote_bda, BT_TRANSPORT_LE);
  if (!p_tcb || p_tcb->conn_ids_waiting_for_mtu_exchange.empty()) {
    return result;
  }

  result.swap(p_tcb->conn_ids_waiting_for_mtu_exchange);
  return result;
}

/*******************************************************************************
+24 −6
Original line number Diff line number Diff line
@@ -1099,14 +1099,32 @@ void gatt_process_mtu_rsp(tGATT_TCB& tcb, tGATT_CLCB* p_clcb, uint16_t len,
  } else {
    STREAM_TO_UINT16(mtu, p_data);

    if (mtu < tcb.payload_size && mtu >= GATT_DEF_BLE_MTU_SIZE)
      tcb.payload_size = mtu;
    LOG_INFO("Local pending MTU %d, Remote (%s) MTU %d",
             tcb.pending_user_mtu_exchange_value,
             tcb.peer_bda.ToString().c_str(), mtu);

    /* Aim for MAX as we did in the request */
    if (mtu < GATT_DEF_BLE_MTU_SIZE) {
      tcb.payload_size = GATT_DEF_BLE_MTU_SIZE;
    } else {
      tcb.payload_size = std::min(mtu, (uint16_t)(GATT_MAX_MTU_SIZE));
    }

    bluetooth::shim::arbiter::GetArbiter().OnIncomingMtuResp(tcb.tcb_idx,
                                                             tcb.payload_size);

  BTM_SetBleDataLength(tcb.peer_bda, tcb.payload_size + L2CAP_PKT_OVERHEAD);
    /* This is just to track the biggest MTU requested by the user.
     * This value will be used in the BTM_SetBleDataLength */
    if (tcb.pending_user_mtu_exchange_value > tcb.max_user_mtu) {
      tcb.max_user_mtu =
          std::min(tcb.pending_user_mtu_exchange_value, tcb.payload_size);
    }
    tcb.pending_user_mtu_exchange_value = 0;

    LOG_INFO("MTU Exchange resulted in: %d", tcb.payload_size);

    BTM_SetBleDataLength(tcb.peer_bda, tcb.max_user_mtu + L2CAP_PKT_OVERHEAD);
  }

  gatt_end_operation(p_clcb, status, NULL);
}
Loading