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

Bonding lost on Android 8.1 when reconnecting from the central

Hi,

I'm developping an application for nRF52840 as a BLE central. It is supposed to connect to an Android device (BLE peripheral) using a bond (encrypted connection with keys stored on both sides). The Android device implements a GATT service with readable and writable characteristics. The board has no IO capability so that it uses the Just Works pairing method.

When debugging, I'm continuously checking what method BluetoothAdapter.getBondedDevices() returns.

The first time the board connects to the Android device, everything goes well: it generates a pop up asking for user confirmation on the Android device, and once it is confirmed, the event BluetoothDevice.BOND_BONDED is received from the Android BLE stack and method BluetoothAdapter.getBondedDevices() starts returning the central is bonded. The central can write on the GATT server characteristics and be notified of any changes.

To simulate a second connection, I turn the BT off on the Android device, wait for a few seconds (until the timeout expires and the board notices the peripheral has been disconnected), and turn BT on again. Here, method BluetoothAdapter.getBondedDevices() still returns the central is bonded.

The board receives the peripheral's advertising packets, connects to the device, and an event BluetoothDevice.BOND_NONE is triggered by Android. At that moment, the central has been removed from the list of bonded devices (returned by method BluetoothAdapter.getBondedDevices()). The central can still be notified of any changes on read-permissions characteristics but cannot write on the GATT server characteristics.

So I suppose I'm using the SoftDevice API wrongly but I cannot find how to fix that.

Here is how I connect to the peripheral:

I define the pairing parameters:

ble_gap_sec_params_t sec_param;
ret_code_t err_code; 

memset(&sec_param, 0, sizeof(ble_gap_sec_params_t)); 
sec_param.bond           = true;                 // need to bond (encrypt the connexion and keep the device's key to direcly encrypt the next connexion)
sec_param.mitm           = false;                // no man-in-the-middle protection
sec_param.lesc           = 0;                    // LE secure connection disabled
sec_param.keypress       = 0;                    // no keypress notifications
sec_param.io_caps        = BLE_GAP_IO_CAPS_NONE; // the central has no keyboard/display capability
sec_param.oob            = false;                // no OOB
sec_param.min_key_size   = 7; 
sec_param.max_key_size   = 16; 
sec_param.kdist_own.enc  = 1; 
sec_param.kdist_own.id   = 1; 
sec_param.kdist_peer.enc = 1; 
sec_param.kdist_peer.id  = 1; 

err_code = pm_sec_params_set(&sec_param); 
APP_ERROR_CHECK(err_code);

When receiving an advertising report from the peripheral, I connect to it:

static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
{
   ret_code_t err_code;
   err_code = sd_ble_gap_connect(&p_adv_report->peer_addr, &m_scan_param, &m_connection_param, APP_BLE_CONN_CFG_TAG);
   APP_ERROR_CHECK(err_code);     
}

My BLE events handler receives a CONNECTED event and starts discovering the services:


static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
{
    ret_code_t err_code;
    ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
        // launch the service discovery process
        err_code = ble_db_discovery_start(&m_db_disc, p_gap_evt->conn_handle);
        APP_ERROR_CHECK(err_code);
        break;
    }
}

My service events handler receives an event after the service discovery has been completed and connect to the peripheral:

static void service_c_evt_handler(service_c_t* p_service_c, service_c_evt_t * p_service_c_evt)
{
     switch (p_service_c_evt->evt_type)
     {
           case SERVICE_C_EVT_DISCOVERY_COMPLETE:
           {
                ret_code_t err_code;
                // last parameter false means there is no need to force repairing if the bond with the device already exists
                err_code = pm_conn_secure(p_service_c_evt->conn_handle, false);
                APP_ERROR_CHECK(err_code);
           } break;
     }
}

My peer events handler:

static void pm_evt_handler(pm_evt_t const * p_evt)
{
     ret_code_t err_code; 
     switch (p_evt->evt_id) 
     { 
          case PM_EVT_BONDED_PEER_CONNECTED: 
          { 
               // connected to an already paired device 
          } break; 

          case PM_EVT_CONN_SEC_SUCCEEDED: 
          { 
               // connected to an newly paired device 
          } break; 
     }
}


The first time the central connects and pairs with the Android device, event PM_EVT_CONN_SEC_SUCCEEDED is triggered.
The next times, event PM_EVT_BONDED_PEER_CONNECTED is received, so that the bonding procedure has no been reinitialized.
Parameter force_repairing when calling function pm_conn_secure is false.

Thank you in advance for your help!

Let me know if something is unclear.

Cécile

  • I see. The reason I asked is because the phone will often act as a central in a BLE connection. FYI, it is possible to be central in one connection and peripheral in another. The reason the phone is often the central is because scanning draws a lot more current than advertising. But I guess if you are scanning for other peripherals as well, that wouldn't really make a huge difference.

    Appart from if it is actually a bug in the Bluetootoh FW on the tablet.

    Try out the ble_app_hrsc example, and let me know how it behaves.

    Best regards,

    Edvin

  • Hello Edvin,

    Could you please help me understand how I can modify the base UUID of the service the central is looking for with the example ble_app_hrsc ? I'd like to make as few modifications as possible to test with my tablet and being able to compare this method with the one I have now.

    Here is what I'm used to configure with the current implementation:

    #define PERIPHERAL_UUID_BASE {0x23, 0xD1, 0xBC, 0xEA, 0x5F, 0x78, 0x23, 0x15, 0xDE, 0xEF, 0x12, 0x12, 0x00, 0x00, 0x00, 0x00}
    
    ble_uuid128_t peripheral_uuid_base = {PERIPHERAL_UUID_BASE};
    
    err_code err_code = sd_ble_uuid_vs_add(&peripheral_uuid_base, &p_service_c->uuid_type);
    VERIFY_SUCCESS(err_code);

    If I'm not wrong, I need to change the peripheral's 128bits- UUID to match the one the central is looking for, or change the 128-bits UUID the central is looking for when discovering the services offered by the peripheral's GATT server with the one configured in the tablet.

    Thank you in advance for your help!

    Best regards,

    Cécile

  • That is correct, you must change one of these, so that the peripheral is advertising with the service that the central is looking for.

    However, it may be a bit more complex than that. The ble_app_hrs_c is looking for the Heart rate UUID, which is a short UUID (standardized by Bluetooth). 

    Perhaps it is actually easier to start with the ble_app_multilink_central example, which is looking for an advertisement name ("Nordic_Blinky").

    The other alternative is to don't care about the scan_filters, and find some specific bytes in the advertising packet yourself that you can check before connecting. You can add the BLE_GAP_EVT_ADV_REPORT event to your ble_evt_handler in main.c.

    BR,

    Edvin

  • Hello Edvin and thank you for you reply.

    Actually I can connect to the peripheral, I added a target name

    static char const m_target_periph_name[] = "N80"

    So that my central can contact to the peripheral (the tablet):

    static void on_adv_report(ble_gap_evt_adv_report_t const * p_adv_report)
    {
        if (strlen(m_target_periph_name) != 0)
        {
            if (ble_advdata_name_find(p_adv_report->data.p_data,
                                      p_adv_report->data.len,
                                      m_target_periph_name))
            {
                do_connect = true;
                printf("Name match send connect_request.\n");
            }
        }
    }

    I can connected and start discovering the services:

    static void ble_evt_handler(ble_evt_t const * p_ble_evt, void * p_context)
    {
        ret_code_t            err_code;
        ble_gap_evt_t const * p_gap_evt = &p_ble_evt->evt.gap_evt;
    
        switch (p_ble_evt->header.evt_id)
        {
            case BLE_GAP_EVT_CONNECTED:
            {
                printf("Connected.\n");
    
                // Discover peer's services.
                err_code = ble_db_discovery_start(&m_db_disc, p_ble_evt->evt.gap_evt.conn_handle);
                APP_ERROR_CHECK(err_code);
    
                err_code = bsp_indication_set(BSP_INDICATE_CONNECTED);
                APP_ERROR_CHECK(err_code);
    
                if (ble_conn_state_central_conn_count() < NRF_SDH_BLE_CENTRAL_LINK_COUNT)
                {
                    scan_start();
                }
            } break;
        }
    }
    

    But now I'd like to be able to discover the service offered by the peripheral to be able to test:

    * the securing of the connection

    * write and read to/from the secured characteristics of the peripheral's GATT service

    Thank you in advance for you help,

    Cécile

  • Look at how this is done in this example. You need to change the hrs_c_init() with your custom service_c. Also, update the discovery handler to handle your service.

Related