This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Unwanted Authorization request event on open link

Hello Nordic Community, 

I've stumble upon an issue with a characteristic configured to not require authorization but still does on certain occasions.

Consider a service with a characteristic defined as below housed on a peripheral device:

static uint32_t char_add(ble_id_t *p_id)
{
    ble_gatts_char_md_t char_md;
    ble_gatts_attr_t    attr_char_value;
    ble_uuid_t          ble_uuid;
    ble_gatts_attr_md_t attr_md;

    memset(&char_md, 0, sizeof(char_md));

    char_md.char_props.read  = 0;
    char_md.char_props.write = 1;
    char_md.char_props.notify = 0;
    char_md.p_char_user_desc = NULL;
    char_md.p_char_pf        = NULL;
    char_md.p_user_desc_md   = NULL;
    char_md.p_cccd_md        = NULL;
    char_md.p_sccd_md        = NULL;

    ble_uuid.type = p_id->uuid_type;
    ble_uuid.uuid = UUID_CHAR;

    memset(&attr_md, 0, sizeof(attr_md));

    BLE_GAP_CONN_SEC_MODE_SET_NO_ACCESS(&attr_md.read_perm);
    BLE_GAP_CONN_SEC_MODE_SET_OPEN(&attr_md.write_perm);

    attr_md.vloc            = BLE_GATTS_VLOC_STACK;
    attr_md.rd_auth         = 0;
    attr_md.wr_auth         = 0;
    attr_md.vlen            = 0;

    memset(&attr_char_value, 0, sizeof(attr_char_value));

    attr_char_value.p_uuid    = &ble_uuid;
    attr_char_value.p_attr_md = &attr_md;
    attr_char_value.init_len  = sizeof(uint8_t);
    attr_char_value.init_offs = 0;
    attr_char_value.max_len   = sizeof(uint8_t) * 250; 
    attr_char_value.p_value   = NULL;
    
    return sd_ble_gatts_characteristic_add(p_id->service_handle,
                                           &char_md,
                                           &attr_char_value,
                                           &p_id->id_char_handle);
}

As far as I understand this characteristic should only support writes and should not require authorization right? 

It works fine to write to this characteristic as far as the amount of bytes written are less than the ATT MTU size. 

When writing a bigger chunk of data than the ATT MTU size the following happens:

1. Receive a BLE_EVT_USER_MEM_REQUEST which is handled like this within the ble event handler:

case BLE_EVT_USER_MEM_REQUEST:
    err_code = sd_ble_user_mem_reply(p_ble_evt->evt.gattc_evt.conn_handle, NULL);
    APP_ERROR_CHECK(err_code);
    break;


2. Receive a BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST of type BLE_GATTS_AUTHORIZE_TYPE_WRITE.

First time this event come it a BLE_GATTS_OP_PREP_WRITE_REQ and the second time its a BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL.

Hence this result in that the write is not performed at all.

I've implemented the following non-working code to try to deal with the authorization:

static void on_authorize_request(ble_evt_t const * p_ble_evt)
{
  ret_code_t                            err_code;
  ble_gatts_evt_rw_authorize_request_t  req;
  ble_gatts_rw_authorize_reply_params_t auth_reply;

  req = p_ble_evt->evt.gatts_evt.params.authorize_request;

  NRF_LOG_INFO("on_authorize_request - type: %x", req.type);

  if (req.type == BLE_GATTS_AUTHORIZE_TYPE_WRITE) 
  {
    NRF_LOG_INFO("on_authorize_request - op: %x", req.request.write.op);
    NRF_LOG_INFO("on_authorize_request - len: %x", req.request.write.len);

    if ((req.request.write.op == BLE_GATTS_OP_PREP_WRITE_REQ)     ||
        (req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_NOW)) // ||
        //(req.request.write.op == BLE_GATTS_OP_EXEC_WRITE_REQ_CANCEL))
    {
      auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE; 
      //auth_reply.params.write.update = 1;
      //auth_reply.params.write.offset = 0;
      //auth_reply.params.write.len    = req.request.write.len;
      //auth_reply.params.write.p_data = req.request.write.data;
      auth_reply.params.write.gatt_status = BLE_GATT_STATUS_ATTERR_APP_BEGIN + 2;
    }
    else if (req.request.write.op == BLE_GATTS_OP_WRITE_REQ) {
      auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_WRITE; 
      auth_reply.params.write.update = 1;
      auth_reply.params.write.offset = 0;
      auth_reply.params.write.len    = req.request.write.len;
      auth_reply.params.write.p_data = req.request.write.data;
      auth_reply.params.write.gatt_status = BLE_GATT_STATUS_SUCCESS;
    }

    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                               &auth_reply);
    APP_ERROR_CHECK(err_code);
  } 
  else if (req.type == BLE_GATTS_AUTHORIZE_TYPE_READ) 
  {
    auth_reply.type = BLE_GATTS_AUTHORIZE_TYPE_READ;
    auth_reply.params.read.update = 1;
    auth_reply.params.read.offset = 0;
    auth_reply.params.read.len    = 0;
    auth_reply.params.read.p_data = NULL;
    auth_reply.params.read.gatt_status = BLE_GATT_STATUS_SUCCESS;

    err_code = sd_ble_gatts_rw_authorize_reply(p_ble_evt->evt.gatts_evt.conn_handle,
                                               &auth_reply);
    APP_ERROR_CHECK(err_code);
  } 
  else 
  {
    // Invalid authorization type
    NRF_LOG_WARNING("Invalid authorization type");
  }
}

I feel that I need some enlightenment in what I'm doing wrong here:

Why is an authorization request triggered for data chunks larger than ATT MTU?

If this is expected behavior, how can I handle the authorization request to allow the write?

The following code is running on a nRF52840 chip using SDK 15.0.0 alongside with SoftDevice 6.0.0.

I've tested this on both iOS and Android devices with the same issue present on both platforms. E.g iOS have an ATT MTU of 185 and sending 182 bytes works (ATT MTU - 3), however sending 183 trigger the above behavior.

Thanks for any help!

Related