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

Parents
  • Hello Cécile,

    Have you tried to do this with other phones, or just one? 

    And have you tried other examples from the SDK? One of them that should work out of the box is the example found in SDK\examples\ble_peripheral\ble_app_gls. This is an example that uses pairing and bonding, and you should get the 6-digit numerical pairing key on the first connect, and not on the second (because they are bonded). 

    So can you try the following:

    Another Android/iPhone with your project. What is the behavior like?

    Another example from the SDK (ble_app_gls) with the same phone you are currently using.

    Do any of them behave differently?

    I assume that you have added the peer manager into your project? Or was it there by default (did you start with any of the examples from the SDK)?

    It looks like, from your snippets and the description that you give, that things are set up correctly from the nRF project. Is it possible to do a sniffer trace? Do you have a spare Development Kit that you can use as the sniffer?

    The nRF Sniffer is a quite useful tool that you can use to sniff BLE connections. I just wanted to check whether there is anything suspicious in the packets over the air from any of the devices.

    As you say, since the Android API tells you that the device is bonded, everything should be ok, but perhaps the phone is reacting to something suspicious from the nRF side.

    Also, what phone model do you use? And what Android OS version?

    And by the way, when you are trying to sniff the connection, set sec_params.lesc back to 0, or else the link is not possible to sniff.

    Best regards,

    Edvin

  • Hi Edvin,
    Many thanks for your reply and recommendations!

    The Android device I'm using is a Fieldbook N80 rugged tablet, running Android 8.1. I've also tried using a Nexus 6P but there were many issues (the phone gets disconnected just after the board connects to it for instance) and from what I read online, the BLE behavior can be quite different depending on the manufacturer so I decided to focus on the N80, which is the only target for my project.

    I looked at several examples including the ble_app_gls but I didn't run them since in my case the Android device is a peripheral (I thought it would be too different to bring something interesting to the debate). You think I should try anyway?

    I setup the nRF Sniffer and captured the BLE trafic including the following steps:

    * peripheral (Android tablet) advertising

    * central connection request

    * pairing/bonding negociation

    * bonding finished

    * after a while, reboot of the board

    * peripheral (Android tablet) advertising

    * central connection request

    I'm joining the capture file in case you see something interesting.

    What I saw is that during the second connection (the devices are supposed to be bonded already), the slave (the tablet) sends a LL_REJECT_IND during the securing of the connection, with error code 0x06, "PIN or key missing". I don't understand this error because with the "Just works" pairing method, no PIN/key shall be needed.

    3541	105.973607	Master_0xb927e11f	Slave_0xb927e11f	LE LL	49	Control Opcode: LL_ENC_REQ
    3544	105.981600	Slave_0xb927e11f	Master_0xb927e11f	LE LL	39	Control Opcode: LL_ENC_RSP
    3546	105.988594	Slave_0xb927e11f	Master_0xb927e11f	LE LL	28	Control Opcode: LL_REJECT_IND
    3551	106.018568	Master_0xb927e11f	Slave_0xb927e11f	LE LL	38	Control Opcode: LL_CONNECTION_UPDATE_REQ

    Do you see something in the ble exchange that would let the Android device 'think' the pairing method implies a key?

    Thank you in advance!

    Cécile

    full_exhange_bonding_plus_reconnection.pcapng

  • Hello Cécile,

    It looks like like in packet 390 that the master doesn't support pairing.

    Can you please try one of the other examples in the SDK that supports pairing? You can test the ble_app_hrs_c. Does the phone behave similar there?

    It may be a bug in the android OS. The phone being a central is not a common use case, so it may not have been discovered in that particular phone. Is there any particular reason for why the phone is the peripheral/slave, and not the other way around?

    BR,

    Edvin

  • Hello Edvin,

    I'm about to test this example as soon as possible and let you know how it behaves.

    I'm afraid I did not understand the second part of your response. The Android device (Fieldbook tablet) is a peripheral here and the nRF52840 is the central.

    It has been designed that way because other devices can connect to this nRF52840. Several peripherals (the tablet and other boards that embed a nRF52840 chip) can connect to the master board (central).

    Thank you,

    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

Reply
  • 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

Children
  • 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