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!

Parents
  • Hi Kristian, 

    You can have a look at these two messages sequence chart, this one and this one. 

    When you are writing with payload bigger than MTU size, the phone automatically change it to queued write (long write). And it depends on how you choose to handle the queued write, either by application or by the stack when you sending sd_ble_user_mem_reply() the process will either go to the first or the second sequence.

    If you choose to handle by application, you will receive a  BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST .

    Please follow what we have in the sequence chart to execute the write, if you still have problem, please let me know. 

Reply
  • Hi Kristian, 

    You can have a look at these two messages sequence chart, this one and this one. 

    When you are writing with payload bigger than MTU size, the phone automatically change it to queued write (long write). And it depends on how you choose to handle the queued write, either by application or by the stack when you sending sd_ble_user_mem_reply() the process will either go to the first or the second sequence.

    If you choose to handle by application, you will receive a  BLE_GATTS_EVT_RW_AUTHORIZE_REQUEST .

    Please follow what we have in the sequence chart to execute the write, if you still have problem, please let me know. 

Children
Related