/* Copyright (c) 2012 Nordic Semiconductor. All Rights Reserved.
 *
 * The information contained herein is property of Nordic Semiconductor ASA.
 * Terms and conditions of usage are described in detail in NORDIC
 * SEMICONDUCTOR STANDARD SOFTWARE LICENSE AGREEMENT.
 *
 * Licensees are granted free, non-transferable use of the information. NO
 * WARRANTY of ANY KIND is provided. This heading must NOT be removed from
 * the file.
 *
 */

#include "ble_bondmngr.h"
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include "nordic_common.h"
#include "nrf_error.h"
#include "ble_gap.h"
#include "ble_srv_common.h"
#include "app_util.h"
#include "nrf_assert.h"
#include "nrf.h"
#include "nrf51_bitfields.h"
#include "crc16.h"
#include "pstorage.h"
#include "ble_bondmngr_cfg.h"

#define CCCD_SIZE                    6                                                                   /**< Number of bytes needed for storing the state of one CCCD. */
#define CRC_SIZE                     2                                                                   /**< Size of CRC in sys_attribute data. */
#define SYS_ATTR_BUFFER_MAX_LEN      (((BLE_BONDMNGR_CCCD_COUNT + 1) * CCCD_SIZE) + CRC_SIZE)            /**< Size of sys_attribute data. */
#define MAX_NUM_CENTRAL_WHITE_LIST   MIN(BLE_BONDMNGR_MAX_BONDED_CENTRALS, 8)                            /**< Maximum number of whitelisted centrals supported.*/
#define MAX_BONDS_IN_FLASH           10                                                                  /**< Maximum number of bonds that can be stored in flash. */
#define BOND_MANAGER_DATA_SIGNATURE  0x53240000

/**@defgroup ble_bond_mngr_sec_access  Bond Manager Security Status Access Macros
 * @brief    The following group of macros abstract access to Security Status with a peer.
 * @{
 */

#define SEC_STATUS_INIT_VAL       0x00                                                                  /**< Initialization value for security status flags. */
#define ENC_STATUS_SET_VAL        0x01                                                                  /**< Bitmask for encryption status. */
#define BOND_IN_PROGRESS_SET_VAL  0x02                                                                  /**< Bitmask for 'bonding in progress'. */


/**@brief   Macro for setting the Encryption Status for current link.
 *
 * @details Macro for setting the Encryption Status for current link.
 */
#define ENCRYPTION_STATUS_SET()                                                                   \
        do                                                                                        \
        {                                                                                         \
             m_sec_con_status |= ENC_STATUS_SET_VAL;                                              \
        } while (0)

/**@brief   Macro for getting the Encryption Status for current link.
 *
 * @details Macro for getting the Encryption Status for current link.
 */
#define ENCRYPTION_STATUS_GET()                                                                   \
        (((m_sec_con_status & ENC_STATUS_SET_VAL) == 0) ? false : true)

/**@brief  Macro for resetting the Encryption Status for current link.
 *
 * @details Macro for resetting the Encryption Status for current link.
 */
#define ENCRYPTION_STATUS_RESET()                                                                 \
        do                                                                                        \
        {                                                                                         \
            m_sec_con_status &= (~ENC_STATUS_SET_VAL);                                            \
        } while (0)


/**@brief   Macro for resetting the Bonding In Progress status for current link.
 *
 * @details Macro for resetting the Bonding In Progress status for current link.
 */
#define BONDING_IN_PROGRESS_STATUS_SET()                                                          \
        do                                                                                        \
        {                                                                                         \
            m_sec_con_status |= BOND_IN_PROGRESS_SET_VAL;                                         \
        } while (0)

/**@brief   Macro for setting the Bonding In Progress status for current link.
 *
 * @details Macro for setting the Bonding In Progress status for current link.
 */
#define BONDING_IN_PROGRESS_STATUS_GET()                                                          \
         (((m_sec_con_status & BOND_IN_PROGRESS_SET_VAL) == 0) ? false: true)

/**@brief   Macro for resetting the Bonding In Progress status for current link.
 *
 * @details Macro for resetting the Bonding In Progress status for current link.
 */
#define BONDING_IN_PROGRESS_STATUS_RESET()                                                        \
        do                                                                                        \
        {                                                                                         \
            m_sec_con_status &= (~BOND_IN_PROGRESS_SET_VAL);                                      \
        } while (0)

/**@brief   Macro for resetting all security status flags for current link.
 *
 * @details Macro for resetting all security status flags for current link.
 */
#define SECURITY_STATUS_RESET()                                                                   \
        do                                                                                        \
        {                                                                                         \
            m_sec_con_status = SEC_STATUS_INIT_VAL;                                               \
        } while (0)

/** @} */

/**@brief    Verify module's initialization status.
 *
 * @details  Verify module's initialization status. Returns NRF_INVALID_STATE in case a module API
 *           is called without initializing the module.
 */
#define VERIFY_MODULE_INITIALIZED()                                                               \
        do                                                                                        \
        {                                                                                         \
            if (!m_is_bondmngr_initialized)                                                       \
            {                                                                                     \
                 return NRF_ERROR_INVALID_STATE;                                                  \
            }                                                                                     \
        } while(0)


/**@brief This structure contains the Bonding Information for one central.
 */
typedef struct
{
    int32_t                        central_handle;                           /**< Central's handle (NOTE: Size is 32 bits just to make struct size dividable by 4). */
    ble_gap_evt_auth_status_t      auth_status;                              /**< Central authentication data. */
    ble_gap_evt_sec_info_request_t central_id_info;                          /**< Central identification info. */
    ble_gap_addr_t                 central_addr;                             /**< Central's address. */
} central_bond_t;

STATIC_ASSERT(sizeof(central_bond_t) % 4 == 0);

/**@brief This structure contains the System Attributes information related to one central.
 */
typedef struct
{
    int32_t  central_handle;                                                 /**< Central's handle (NOTE: Size is 32 bits just to make struct size dividable by 4). */
    uint8_t  sys_attr[SYS_ATTR_BUFFER_MAX_LEN];                              /**< Central sys_attribute data. */
    uint32_t sys_attr_size;                                                  /**< Central sys_attribute data's size (NOTE: Size is 32 bits just to make struct size dividable by 4). */
} central_sys_attr_t;

STATIC_ASSERT(sizeof(central_sys_attr_t) % 4 == 0);

/**@brief This structure contains the Bonding Information and System Attributes related to one
 *        central.
 */
typedef struct
{
    central_bond_t     bond;                                                 /**< Bonding information. */
    central_sys_attr_t sys_attr;                                             /**< System attribute information. */
} central_t;

/**@brief This structure contains the whitelisted addresses.
 */
typedef struct
{
    int8_t           central_handle;                                        /**< Central's handle. */
    ble_gap_addr_t * p_addr;                                                /**< Pointer to the central's address if BLE_GAP_ADDR_TYPE_PUBLIC. */
} whitelist_addr_t;

/**@brief This structure contains the whitelisted IRKs.
 */
typedef struct
{
    int8_t           central_handle;                                        /**< Central's handle. */
    ble_gap_irk_t  * p_irk;                                                 /**< Pointer to the central's irk if available. */
} whitelist_irk_t;

static bool                m_is_bondmngr_initialized = false;               /**< Flag for checking if module has been initialized. */
static ble_bondmngr_init_t m_bondmngr_config;                               /**< Configuration as specified by the application. */
static uint16_t            m_conn_handle;                                   /**< Current connection handle. */
static central_t           m_central;                                       /**< Current central data. */
static central_t           m_centrals_db[BLE_BONDMNGR_MAX_BONDED_CENTRALS]; /**< Pointer to start of bonded centrals database. */
static uint8_t             m_centrals_in_db_count;                          /**< Number of bonded centrals. */
static whitelist_addr_t    m_whitelist_addr[MAX_NUM_CENTRAL_WHITE_LIST];    /**< List of central's addresses  for the whitelist. */
static whitelist_irk_t     m_whitelist_irk[MAX_NUM_CENTRAL_WHITE_LIST];     /**< List of central's IRKs  for the whitelist. */
static uint8_t             m_addr_count;                                    /**< Number of addresses in the whitelist. */
static uint8_t             m_irk_count;                                     /**< Number of IRKs in the whitelist. */
static uint16_t            m_crc_bond_info;                                 /**< Combined CRC for all Bonding Information currently stored in flash. */
static uint16_t            m_crc_sys_attr;                                  /**< Combined CRC for all System Attributes currently stored in flash. */
static pstorage_handle_t   mp_flash_bond_info;                              /**< Pointer to flash location to write next Bonding Information. */
static pstorage_handle_t   mp_flash_sys_attr;                               /**< Pointer to flash location to write next System Attribute information. */
static uint8_t             m_bond_info_in_flash_count;                      /**< Number of Bonding Information currently stored in flash. */
static uint8_t             m_sys_attr_in_flash_count;                       /**< Number of System Attributes currently stored in flash. */
static uint8_t             m_sec_con_status;                                /**< Variable to denote security status.*/
static bool                m_bond_loaded;                                   /**< Variable to indicate if the bonding information of the currently connected central is available in the RAM.*/
static bool                m_sys_attr_loaded;                               /**< Variable to indicate if the system attribute information of the currently connected central is loaded from the database and set in the S110 SoftDevice.*/
static uint32_t            m_bond_crc_array[BLE_BONDMNGR_MAX_BONDED_CENTRALS];
static uint32_t            m_sys_crc_array[BLE_BONDMNGR_MAX_BONDED_CENTRALS];

/**@brief      Function for extracting the CRC from an encoded 32 bit number that typical resides in
 *             the flash memory
 *
 * @param[in]  header   Header containing CRC and magic number.
 * @param[out] p_crc    Extracted CRC.
 *
 * @retval      NRF_SUCCESS              CRC successfully extracted.
 * @retval      NRF_ERROR_NOT_FOUND      Flash seems to be empty.
 * @retval      NRF_ERROR_INVALID_DATA   Header does not contain the magic number.
 */
static uint32_t crc_extract(uint32_t header, uint16_t * p_crc)
{
    if ((header & 0xFFFF0000U) == BOND_MANAGER_DATA_SIGNATURE)
    {
        *p_crc = (uint16_t)(header & 0x0000FFFFU);

        return NRF_SUCCESS;
    }
    else if (header == 0xFFFFFFFFU)
    {
        return NRF_ERROR_NOT_FOUND;
    }
    else
    {
        return NRF_ERROR_INVALID_DATA;
    }
}


/**@brief      Function for storing the Bonding Information of the specified central to the flash.
 *
 * @param[in]  p_bond   Bonding information to be stored.
 *
 * @return     NRF_SUCCESS on success, an error_code otherwise.
 */
static uint32_t bond_info_store(central_bond_t * p_bond)
{
    uint32_t err_code;
    pstorage_handle_t dest_block;

    // Check if flash is full
    if (m_bond_info_in_flash_count >= MAX_BONDS_IN_FLASH)
    {
        return NRF_ERROR_NO_MEM;
    }

    // Check if this is the first bond to be stored
    if (m_bond_info_in_flash_count == 0)
    {
        // Initialize CRC
        m_crc_bond_info = crc16_compute(NULL, 0, NULL);
    }

    // Get block pointer from base
    err_code = pstorage_block_identifier_get(&mp_flash_bond_info,m_bond_info_in_flash_count,&dest_block);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    // Get block pointer from base    
    err_code = pstorage_block_identifier_get(&mp_flash_bond_info,m_bond_info_in_flash_count,&dest_block);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Write Bonding Information
    err_code = pstorage_store(&dest_block,
                              (uint8_t *)p_bond,
                              sizeof(central_bond_t),
                              sizeof(uint32_t));

    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    m_crc_bond_info = crc16_compute((uint8_t *)p_bond,
                                     sizeof(central_bond_t),
                                     &m_crc_bond_info);

    // Write header
    m_bond_crc_array[m_bond_info_in_flash_count] = (BOND_MANAGER_DATA_SIGNATURE | m_crc_bond_info);

    err_code = pstorage_store (&dest_block, (uint8_t *)&m_bond_crc_array[m_bond_info_in_flash_count],sizeof(uint32_t),0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    m_bond_info_in_flash_count++;
    return NRF_SUCCESS;
}


/**@brief      Function for storing the System Attributes related to a specified central in flash.
 *
 * @param[in]  p_sys_attr   System Attributes to be stored.
 *
 * @return     NRF_SUCCESS on success, an error_code otherwise.
 */
static uint32_t sys_attr_store(central_sys_attr_t * p_sys_attr)
{
    uint32_t err_code;
    pstorage_handle_t dest_block;

    // Check if flash is full.
    if (m_sys_attr_in_flash_count >= MAX_BONDS_IN_FLASH)
    {
        return NRF_ERROR_NO_MEM;
    }

    // Check if this is the first time any System Attributes is stored.
    if (m_sys_attr_in_flash_count == 0)
    {
        // Initialize CRC
        m_crc_sys_attr = crc16_compute(NULL, 0, NULL);
    }


    // Get block pointer from base
    err_code = pstorage_block_identifier_get(&mp_flash_sys_attr,m_sys_attr_in_flash_count,&dest_block);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    // Write System Attributes in flash.
    err_code = pstorage_store(&dest_block,
                              (uint8_t *)p_sys_attr,
                              sizeof(central_sys_attr_t),
                              sizeof(uint32_t));
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    m_crc_sys_attr = crc16_compute((uint8_t *)p_sys_attr,
                                             sizeof(central_sys_attr_t),
                                             &m_crc_sys_attr);

    // Write header.
    m_sys_crc_array[m_sys_attr_in_flash_count] = (BOND_MANAGER_DATA_SIGNATURE | m_crc_sys_attr);

    err_code = pstorage_store (&dest_block,
                               (uint8_t *)&m_sys_crc_array[m_sys_attr_in_flash_count],
                               sizeof(uint32_t),
                               0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    m_sys_attr_in_flash_count++;

    
    return NRF_SUCCESS;
}


/**@brief      Function for loading the Bonding Information of one central from flash.
 *
 * @param[out] p_bond   Loaded Bonding Information.
 *
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t bonding_info_load_from_flash(central_bond_t * p_bond)
{
    pstorage_handle_t source_block;
    uint32_t          err_code;
    uint32_t          crc;
    uint16_t          crc_header;
    
    // Check if this is the first bond to be loaded, in which case the
    // m_bond_info_in_flash_count variable would have the intial value 0.
    if (m_bond_info_in_flash_count == 0)
    {
        // Initialize CRC.
        m_crc_bond_info = crc16_compute(NULL, 0, NULL);
    }

    // Get block pointer from base
    err_code = pstorage_block_identifier_get(&mp_flash_bond_info,
                                             m_bond_info_in_flash_count,
                                             &source_block);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    err_code = pstorage_load((uint8_t *)&crc, &source_block, sizeof(uint32_t),0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    err_code = pstorage_load((uint8_t *)&crc, &source_block, sizeof(uint32_t), 0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Extract CRC from header.
    err_code = crc_extract(crc, &crc_header);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    err_code = pstorage_load((uint8_t *)p_bond,
                             &source_block,
                             sizeof(central_bond_t),
                             sizeof(uint32_t));
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Load central.
    err_code = pstorage_load((uint8_t *)p_bond,
                             &source_block,
                             sizeof(central_bond_t),
                             sizeof(uint32_t));
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Check CRC.
    m_crc_bond_info = crc16_compute((uint8_t *)p_bond,
                                    sizeof(central_bond_t),
                                    &m_crc_bond_info);
    if (m_crc_bond_info == crc_header)
    {
        m_bond_info_in_flash_count++;
        return NRF_SUCCESS;
    }
    else
    {
        return NRF_ERROR_INVALID_DATA;
    }
}



/**@brief      Function for loading the System Attributes related to one central from flash.
 *
 * @param[out] p_sys_attr   Loaded System Attributes.
 *
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t sys_attr_load_from_flash(central_sys_attr_t * p_sys_attr)
{
    pstorage_handle_t source_block;
    uint32_t err_code;
    uint32_t crc;
    uint16_t crc_header;

    // Check if this is the first time System Attributes is loaded from flash, in which case the
    // m_sys_attr_in_flash_count variable would have the initial value 0.
    if (m_sys_attr_in_flash_count == 0)
    {
        // Initialize CRC.
        m_crc_sys_attr = crc16_compute(NULL, 0, NULL);
    }

    // Get block pointer from base
    err_code = pstorage_block_identifier_get(&mp_flash_sys_attr,
                                             m_sys_attr_in_flash_count,
                                             &source_block);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    // Get block pointer from base    
    err_code = pstorage_block_identifier_get(&mp_flash_sys_attr,
                                             m_sys_attr_in_flash_count,
                                             &source_block);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    err_code = pstorage_load((uint8_t *)&crc, &source_block, sizeof(uint32_t), 0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    err_code = pstorage_load((uint8_t *)&crc, &source_block, sizeof(uint32_t), 0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Extract CRC from header.
    err_code = crc_extract(crc, &crc_header);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    err_code = pstorage_load((uint8_t *)p_sys_attr,
                             &source_block,
                             sizeof(central_sys_attr_t),
                             sizeof(uint32_t));
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    err_code = pstorage_load((uint8_t *)p_sys_attr,
                             &source_block,
                             sizeof(central_sys_attr_t),
                             sizeof(uint32_t));
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Check CRC.
    m_crc_sys_attr = crc16_compute((uint8_t *)p_sys_attr,
                                   sizeof(central_sys_attr_t),
                                   &m_crc_sys_attr);

    if (m_crc_sys_attr == crc_header)
    {
        m_sys_attr_in_flash_count++;
        return NRF_SUCCESS;
    }
    else
    {
        return NRF_ERROR_INVALID_DATA;
    }
}


/**@brief      Function for erasing the flash pages that contain Bonding Information and System
 *             Attributes.
 *
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t flash_pages_erase(void)
{
    uint32_t err_code;

    err_code = pstorage_clear(&mp_flash_bond_info, MAX_BONDS_IN_FLASH);

    if (err_code == NRF_SUCCESS)
    {
        err_code = pstorage_clear(&mp_flash_sys_attr, MAX_BONDS_IN_FLASH);
    }

    return err_code;
}


/**@brief      Function for checking if Bonding Information in RAM is different from that in
 *             flash.
 *
 * @return     TRUE if Bonding Information in flash and RAM are different, FALSE otherwise.
 */
static bool bond_info_changed(void)
{
    int      i;
    uint16_t crc = crc16_compute(NULL, 0, NULL);

    // Compute CRC for all bonds in database.
    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        crc = crc16_compute((uint8_t *)&m_centrals_db[i].bond,
                                      sizeof(central_bond_t),
                                      &crc);
    }

    // Compare the computed CRS to CRC stored in flash.
    return (crc != m_crc_bond_info);
}


/**@brief      Function for checking if System Attributes in RAM is different from that in flash.
 *
 * @return     TRUE if System Attributes in flash and RAM are different, FALSE otherwise.
 */
static bool sys_attr_changed(void)
{
    int      i;
    uint16_t crc = crc16_compute(NULL, 0, NULL);

    // Compute CRC for all System Attributes in database.
    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        crc = crc16_compute((uint8_t *)&m_centrals_db[i].sys_attr,
                                      sizeof(central_sys_attr_t),
                                      &crc);
    }

    // Compare the CRC of System Attributes in flash with that of the System Attributes in memory.
    return (crc != m_crc_sys_attr);
}


/**@brief      Function for setting the System Attributes for specified central to the SoftDevice, or
 *             clearing the System Attributes if central is a previously unknown.
 *
 * @param[in]  p_central   Central for which the System Attributes is to be set.
 *
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t central_sys_attr_set(central_t * p_central)
{
    uint8_t * p_sys_attr;

    if (m_central.sys_attr.sys_attr_size != 0)
    {
        if (m_central.sys_attr.sys_attr_size > SYS_ATTR_BUFFER_MAX_LEN)
        {
            return NRF_ERROR_INTERNAL;
        }

        p_sys_attr = m_central.sys_attr.sys_attr;
    }
    else
    {
        p_sys_attr = NULL;
    }

    return sd_ble_gatts_sys_attr_set(m_conn_handle, p_sys_attr, m_central.sys_attr.sys_attr_size);
}


/**@brief Function for updating the whitelist data structures.
 */
static void update_whitelist(void)
{
    int i;

    for (i = 0, m_addr_count = 0, m_irk_count = 0; i < m_centrals_in_db_count; i++)
    {
        central_bond_t * p_bond = &m_centrals_db[i].bond;

        if (p_bond->auth_status.central_kex.irk)
        {
            m_whitelist_irk[m_irk_count].central_handle = p_bond->central_handle;
            m_whitelist_irk[m_irk_count].p_irk          = &(p_bond->auth_status.central_keys.irk);

            m_irk_count++;
        }

        if (p_bond->central_addr.addr_type != BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE)
        {
            m_whitelist_addr[m_addr_count].central_handle = p_bond->central_handle;
            m_whitelist_addr[m_addr_count].p_addr         = &(p_bond->central_addr);

            m_addr_count++;
        }
    }
}


/**@brief      Function for handling the authentication status event related to a new central.
 *
 * @details    This function adds the new central to the database and stores the central's Bonding
 *             Information to flash. It also notifies the application when the new bond is created,
 *             and sets the System Attributes to prepare the stack for connection with the new
 *             central.
 *
 * @param[in]  p_auth_status   New authentication status.
 *
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t on_auth_status_from_new_central(ble_gap_evt_auth_status_t * p_auth_status)
{
    uint32_t err_code;

    if (m_centrals_in_db_count >= BLE_BONDMNGR_MAX_BONDED_CENTRALS)
    {
        return NRF_ERROR_NO_MEM;
    }

    // Update central.
    m_central.bond.auth_status         = *p_auth_status;
    m_central.bond.central_id_info.div = p_auth_status->periph_keys.enc_info.div;
    m_central.sys_attr.sys_attr_size   = 0;

    // Add new central to database.
    m_central.bond.central_handle           = m_centrals_in_db_count;
    m_centrals_db[m_centrals_in_db_count++] = m_central;

    update_whitelist();

    m_bond_loaded = true;

    // Clear System Attributes.
    err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0);
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Write new central's Bonding Information to flash.
    err_code = bond_info_store(&m_central.bond);
    if ((err_code == NRF_ERROR_NO_MEM) && (m_bondmngr_config.evt_handler != NULL))
    {
        ble_bondmngr_evt_t evt;

        evt.evt_type       = BLE_BONDMNGR_EVT_BOND_FLASH_FULL;
        evt.central_handle = m_central.bond.central_handle;
        evt.central_id     = m_central.bond.central_id_info.div;

        m_bondmngr_config.evt_handler(&evt);
    }
    else if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    // Pass the event to application.
    if (m_bondmngr_config.evt_handler != NULL)
    {
        ble_bondmngr_evt_t evt;

        evt.evt_type       = BLE_BONDMNGR_EVT_NEW_BOND;
        evt.central_handle = m_central.bond.central_handle;
        evt.central_id     = m_central.bond.central_id_info.div;

        m_bondmngr_config.evt_handler(&evt);
    }

    return NRF_SUCCESS;
}


/**@brief Function for updating the current central's entry in the database.
 */
static uint32_t central_update(void)
{
    uint32_t err_code;
    int32_t  central_handle = m_central.bond.central_handle;

    if ((central_handle >= 0) && (central_handle < m_centrals_in_db_count))
    {
        // Update the database based on whether the bond and system attributes have
        // been loaded or not to avoid overwriting information that was not used in the
        // connection session.
        if (m_bond_loaded)
        {
            m_centrals_db[central_handle].bond = m_central.bond;
        }

        if (m_sys_attr_loaded)
        {
            m_centrals_db[central_handle].sys_attr = m_central.sys_attr;
        }

        update_whitelist();

        err_code = NRF_SUCCESS;
    }
    else
    {
        err_code = NRF_ERROR_INTERNAL;
    }

    return err_code;
}


/**@brief      Function for searching for the central in the database of known centrals.
 *
 * @details    If the central is found, the variable m_central will be populated with all the
 *             information (Bonding Information and System Attributes) related to that central.
 *
 * @param[in]  central_id   Central Identifier.
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t central_find_in_db(uint16_t central_id)
{
    int i;

    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        if (central_id == m_centrals_db[i].bond.central_id_info.div)
        {
            m_central = m_centrals_db[i];
            return NRF_SUCCESS;
        }
    }

    return NRF_ERROR_NOT_FOUND;
}


/**@brief      Function for loading all Bonding Information and System Attributes from flash.
 *
 * @return     NRF_SUCCESS on success, otherwise an error code.
 */
static uint32_t load_all_from_flash(void)
{
    uint32_t err_code;
    int      i;

    m_centrals_in_db_count = 0;

    while (m_centrals_in_db_count < BLE_BONDMNGR_MAX_BONDED_CENTRALS)
    {
        central_bond_t central_bond_info;
        int           central_handle;

        // Load Bonding Information.
        err_code = bonding_info_load_from_flash(&central_bond_info);
        if (err_code == NRF_ERROR_NOT_FOUND)
        {
            // No more bonds in flash.
            break;
        }
        else if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        central_handle = central_bond_info.central_handle;
        if (central_handle > m_centrals_in_db_count)
        {
            // Central handle value(s) missing in flash. This should never happen.
            return NRF_ERROR_INVALID_DATA;
        }
        else
        {
            // Add/update Bonding Information in central array.
            m_centrals_db[central_handle].bond = central_bond_info;
            if (central_handle == m_centrals_in_db_count)
            {
                // New central handle, clear System Attributes.
                m_centrals_db[central_handle].sys_attr.sys_attr_size  = 0;
                m_centrals_db[central_handle].sys_attr.central_handle = INVALID_CENTRAL_HANDLE;
                m_centrals_in_db_count++;
            }
            else
            {
                // Entry was updated, do nothing.
            }
        }
    }

    // Load System Attributes for all previously known centrals.
    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        central_sys_attr_t central_sys_attr;

        // Load System Attributes.
        err_code = sys_attr_load_from_flash(&central_sys_attr);
        if (err_code == NRF_ERROR_NOT_FOUND)
        {
            // No more System Attributes in flash.
            break;
        }
        else if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        if (central_sys_attr.central_handle > m_centrals_in_db_count)
        {
            // Central handle value(s) missing in flash. This should never happen.
            return NRF_ERROR_INVALID_DATA;
        }
        else
        {
            // Add/update Bonding Information in central array.
            m_centrals_db[central_sys_attr.central_handle].sys_attr = central_sys_attr;
        }
    }

    // Initialize the remaining empty bond entries in the memory.
    for (i = m_centrals_in_db_count; i < BLE_BONDMNGR_MAX_BONDED_CENTRALS; i++)
    {
        m_centrals_db[i].bond.central_handle     = INVALID_CENTRAL_HANDLE;
        m_centrals_db[i].sys_attr.sys_attr_size  = 0;
        m_centrals_db[i].sys_attr.central_handle = INVALID_CENTRAL_HANDLE;
    }

    // Update whitelist data structures.
    update_whitelist();

    return NRF_SUCCESS;
}


/**@brief      Function for handling the connected event received from the BLE stack.
 *
 * @param[in]  p_ble_evt   Event received from the BLE stack.
 */
static void on_connect(ble_evt_t * p_ble_evt)
{
    m_conn_handle = p_ble_evt->evt.gap_evt.conn_handle;

    m_central.bond.central_handle     = INVALID_CENTRAL_HANDLE;
    m_central.bond.central_addr       = p_ble_evt->evt.gap_evt.params.connected.peer_addr;
    m_central.sys_attr.sys_attr_size  = 0;

    if (p_ble_evt->evt.gap_evt.params.connected.irk_match)
    {
        uint8_t irk_idx  = p_ble_evt->evt.gap_evt.params.connected.irk_match_idx;

        if ((irk_idx >= MAX_NUM_CENTRAL_WHITE_LIST) ||
            (m_whitelist_irk[irk_idx].central_handle >= BLE_BONDMNGR_MAX_BONDED_CENTRALS))
        {
            m_bondmngr_config.error_handler(NRF_ERROR_INTERNAL);
        }
        else
        {
            m_central = m_centrals_db[m_whitelist_irk[irk_idx].central_handle];
        }
    }
    else
    {
        int i;

        for (i = 0; i < m_addr_count; i++)
        {
            ble_gap_addr_t * p_cur_addr = m_whitelist_addr[i].p_addr;

            if (memcmp(p_cur_addr->addr, m_central.bond.central_addr.addr, BLE_GAP_ADDR_LEN) == 0)
            {
                m_central = m_centrals_db[m_whitelist_addr[i].central_handle];
                break;
            }
        }
    }

    if (m_central.bond.central_handle != INVALID_CENTRAL_HANDLE)
    {
        // Reset bond and system attributes loaded variables.
        m_bond_loaded     = false;
        m_sys_attr_loaded = false;

        // Do not set the system attributes of the central on connection.
        if (m_bondmngr_config.evt_handler != NULL)
        {
            ble_bondmngr_evt_t evt;

            evt.evt_type       = BLE_BONDMNGR_EVT_CONN_TO_BONDED_CENTRAL;
            evt.central_handle = m_central.bond.central_handle;
            evt.central_id     = m_central.bond.central_id_info.div;

            m_bondmngr_config.evt_handler(&evt);
        }
    }
}


/**@brief      Function for handling the 'System Attributes Missing' event received from the
 *             SoftDevice.
 *
 * @param[in]  p_ble_evt   Event received from the BLE stack.
 */
static void on_sys_attr_missing(ble_evt_t * p_ble_evt)
{
    uint32_t err_code;

    if (
        (m_central.bond.central_handle == INVALID_CENTRAL_HANDLE) ||
        !ENCRYPTION_STATUS_GET()                                  ||
        BONDING_IN_PROGRESS_STATUS_GET()
       )
    {
        err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0);
    }
    else
    {
        // Current central is valid, use its data. Set the corresponding sys_attr.
        err_code = central_sys_attr_set(&m_central);
        if (err_code == NRF_SUCCESS)
        {
            // Set System Attributes loaded status variable.
            m_sys_attr_loaded = true;
        }
    }

    if (err_code != NRF_SUCCESS)
    {
        m_bondmngr_config.error_handler(err_code);
    }
}


/**@brief      Function for handling the new authentication status event, received from the
 *             SoftDevice, related to an already bonded central.
 *
 * @details    This function also writes the updated Bonding Information to flash and notifies the
 *             application.
 *
 * @param[in]  p_auth_status   Updated authentication status.
 */
static void auth_status_update(ble_gap_evt_auth_status_t * p_auth_status)
{
    uint32_t err_code;

    // Authentication status changed, update Bonding Information.
    m_central.bond.auth_status         = *p_auth_status;
    m_central.bond.central_id_info.div = p_auth_status->periph_keys.enc_info.div;

    memset(&(m_centrals_db[m_central.bond.central_handle]), 0, sizeof(central_t));
    m_centrals_db[m_central.bond.central_handle] = m_central;

    // Write updated Bonding Information to flash.
    err_code = bond_info_store(&m_central.bond);
    if ((err_code == NRF_ERROR_NO_MEM) && (m_bondmngr_config.evt_handler != NULL))
    {
        ble_bondmngr_evt_t evt;

        evt.evt_type       = BLE_BONDMNGR_EVT_BOND_FLASH_FULL;
        evt.central_handle = m_central.bond.central_handle;
        evt.central_id     = m_central.bond.central_id_info.div;

        m_bondmngr_config.evt_handler(&evt);
    }
    else if (err_code != NRF_SUCCESS)
    {
        m_bondmngr_config.error_handler(err_code);
    }

   // Pass the event to the application.
   if (m_bondmngr_config.evt_handler != NULL)
   {
       ble_bondmngr_evt_t evt;

        evt.evt_type       = BLE_BONDMNGR_EVT_AUTH_STATUS_UPDATED;
        evt.central_handle = m_central.bond.central_handle;
        evt.central_id     = m_central.bond.central_id_info.div;

       m_bondmngr_config.evt_handler(&evt);
   }
}


/**@brief      Function for handling the Authentication Status event received from the BLE stack.
 *
 * @param[in]  p_ble_evt   Event received from the BLE stack.
 */
static void on_auth_status(ble_gap_evt_auth_status_t * p_auth_status)
{
    if (p_auth_status->auth_status != BLE_GAP_SEC_STATUS_SUCCESS)
    {
        return;
    }

    // Verify if its pairing and not bonding
    if (!ENCRYPTION_STATUS_GET())
    {
        return;
    }

    if (m_central.bond.central_handle == INVALID_CENTRAL_HANDLE)
        {
        uint32_t err_code = central_find_in_db(p_auth_status->periph_keys.enc_info.div);

        if (err_code == NRF_SUCCESS)
        {
            // Possible DIV Collision indicate error to application,
            // not storing the new LTK
            err_code = NRF_ERROR_FORBIDDEN;
        }
        else
        {
            // Add the new device to data base
            err_code = on_auth_status_from_new_central(p_auth_status);
        }

        if (err_code != NRF_SUCCESS)
        {
            m_bondmngr_config.error_handler(err_code);
        }
    }
    else
    {
        m_bond_loaded = true;

        // Receiving a auth status again when already in have existing information!
        auth_status_update(p_auth_status);
    }
}


/**@brief      Function for handling the Security Info Request event received from the BLE stack.
 *
 * @param[in]  p_ble_evt   Event received from the BLE stack.
 */
static void on_sec_info_request(ble_evt_t * p_ble_evt)
{
    uint32_t err_code;

    err_code = central_find_in_db(p_ble_evt->evt.gap_evt.params.sec_info_request.div);
    if (err_code == NRF_SUCCESS)
    {
        // Bond information has been found and loaded for security procedures. Reflect this in the
        // status variable
        m_bond_loaded = true;

        // Central found in the list of bonded central. Use the encryption info for this central.
        err_code = sd_ble_gap_sec_info_reply(m_conn_handle,
                                             &m_central.bond.auth_status.periph_keys.enc_info,
                                             NULL);
        if (err_code != NRF_SUCCESS)
        {
            m_bondmngr_config.error_handler(err_code);
        }

        // Do not set the sys_attr yet, should be set only when sec_update is successful.
    }
    else if (err_code == NRF_ERROR_NOT_FOUND)
    {
        m_central.bond.central_id_info = p_ble_evt->evt.gap_evt.params.sec_info_request;

        // New central.
        err_code = sd_ble_gap_sec_info_reply(m_conn_handle, NULL, NULL);
        if (err_code != NRF_SUCCESS)
        {
            m_bondmngr_config.error_handler(err_code);
        }

        // Initialize the sys_attr.
        err_code = sd_ble_gatts_sys_attr_set(m_conn_handle, NULL, 0);
    }

    if (err_code != NRF_SUCCESS)
    {
        m_bondmngr_config.error_handler(err_code);
    }
}


/**@brief      Function for handling the Connection Security Update event received from the BLE
 *             stack.
 *
 * @param[in]  p_ble_evt   Event received from the BLE stack.
 */
static void on_sec_update(ble_gap_evt_conn_sec_update_t * p_sec_update)
{
    uint8_t security_mode  = p_sec_update->conn_sec.sec_mode.sm;
    uint8_t security_level = p_sec_update->conn_sec.sec_mode.lv;

    if (((security_mode == 1) && (security_level > 1)) ||
        ((security_mode == 2) && (security_level != 0)))
    {
        ENCRYPTION_STATUS_SET();

        uint32_t err_code = central_sys_attr_set(&m_central);

        if (err_code != NRF_SUCCESS)
        {
            m_bondmngr_config.error_handler(err_code);
        }
        else
        {
            m_sys_attr_loaded = true;
        }

        if (m_bondmngr_config.evt_handler != NULL)
        {
            ble_bondmngr_evt_t evt;

            evt.evt_type       = BLE_BONDMNGR_EVT_ENCRYPTED;
            evt.central_handle = m_central.bond.central_handle;
            evt.central_id     = m_central.bond.central_id_info.div;

            m_bondmngr_config.evt_handler(&evt);
        }
    }
}


/**@brief      Function for handling the Connection Security Parameters Request event received from
 *             the BLE stack.
 *
 * @param[in]  p_ble_evt   Event received from the BLE stack.
 */
static void on_sec_param_request(ble_gap_evt_sec_params_request_t * p_sec_update)
{
    if (p_sec_update->peer_params.bond)
    {
        BONDING_IN_PROGRESS_STATUS_SET();

        if (m_central.bond.central_handle != INVALID_CENTRAL_HANDLE)
        {
            // Bonding request received from a bonded central, make all system attributes null
            m_central.sys_attr.sys_attr_size = 0;
            memset(m_central.sys_attr.sys_attr, 0, SYS_ATTR_BUFFER_MAX_LEN);
        }
    }
}


void ble_bondmngr_on_ble_evt(ble_evt_t * p_ble_evt)
{
    if (!m_is_bondmngr_initialized)
    {
        m_bondmngr_config.error_handler(NRF_ERROR_INVALID_STATE);
    }

    switch (p_ble_evt->header.evt_id)
    {
        case BLE_GAP_EVT_CONNECTED:
            on_connect(p_ble_evt);
            break;

        // NOTE: All actions to be taken on the Disconnected event are performed in
        //       ble_bondmngr_bonded_centrals_store(). This function must be called from the
        //       Disconnected handler of the application before advertising is restarted (to make
        //       sure the flash blocks are cleared while the radio is inactive).
        case BLE_GAP_EVT_DISCONNECTED:
            SECURITY_STATUS_RESET();
            break;

        case BLE_GATTS_EVT_SYS_ATTR_MISSING:
            on_sys_attr_missing(p_ble_evt);
            break;

        case BLE_GAP_EVT_AUTH_STATUS:
            on_auth_status(&p_ble_evt->evt.gap_evt.params.auth_status);
            break;

        case BLE_GAP_EVT_SEC_INFO_REQUEST:
            on_sec_info_request(p_ble_evt);
            break;

        case BLE_GAP_EVT_SEC_PARAMS_REQUEST:
            on_sec_param_request(&p_ble_evt->evt.gap_evt.params.sec_params_request);
            break;

        case BLE_GAP_EVT_CONN_SEC_UPDATE:
            on_sec_update(&p_ble_evt->evt.gap_evt.params.conn_sec_update);
            break;

        default:
            // No implementation needed.
            break;
    }
}


uint32_t ble_bondmngr_bonded_centrals_store(void)
{
    uint32_t err_code;
    int      i;

    VERIFY_MODULE_INITIALIZED();

    if (m_central.bond.central_handle != INVALID_CENTRAL_HANDLE)
    {
        // Fetch System Attributes from stack.
        uint16_t sys_attr_size = SYS_ATTR_BUFFER_MAX_LEN;

        err_code = sd_ble_gatts_sys_attr_get(m_conn_handle,
                                             m_central.sys_attr.sys_attr,
                                             &sys_attr_size);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        m_central.sys_attr.central_handle = m_central.bond.central_handle;
        m_central.sys_attr.sys_attr_size  = (uint16_t)sys_attr_size;

        // Update the current central.
        err_code = central_update();
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    // Save Bonding Information if changed.
    if (bond_info_changed())
    {
        // Erase flash page.
        err_code = pstorage_clear(&mp_flash_bond_info,MAX_BONDS_IN_FLASH);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        // Store bond information for all centrals.
        m_bond_info_in_flash_count = 0;
        for (i = 0; i < m_centrals_in_db_count; i++)
        {
            err_code = bond_info_store(&m_centrals_db[i].bond);
            if (err_code != NRF_SUCCESS)
            {
                return err_code;
            }
        }
    }

    // Save System Attributes, if changed.
    if (sys_attr_changed())
    {
        // Erase flash page.
        err_code = pstorage_clear(&mp_flash_sys_attr, MAX_BONDS_IN_FLASH);
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        // Store System Attributes for all centrals.
        m_sys_attr_in_flash_count = 0;
        for (i = 0; i < m_centrals_in_db_count; i++)
        {
            err_code = sys_attr_store(&m_centrals_db[i].sys_attr);
            if (err_code != NRF_SUCCESS)
            {
                return err_code;
            }
        }
    }

    m_conn_handle                     = BLE_CONN_HANDLE_INVALID;
    m_central.bond.central_handle     = INVALID_CENTRAL_HANDLE;
    m_central.sys_attr.central_handle = INVALID_CENTRAL_HANDLE;
    m_central.sys_attr.sys_attr_size  = 0;
    m_bond_loaded                     = false;
    m_sys_attr_loaded                 = false;

    return NRF_SUCCESS;
}


uint32_t ble_bondmngr_sys_attr_store(void)
{
    uint32_t err_code;

    if (m_central.sys_attr.sys_attr_size == 0)
    {
        // Connected to new central. So the flash block for System Attributes for this
        // central is empty. Hence no erase is needed.

        uint16_t sys_attr_size = SYS_ATTR_BUFFER_MAX_LEN;

        // Fetch System Attributes from stack.
        err_code = sd_ble_gatts_sys_attr_get(m_conn_handle,
                                             m_central.sys_attr.sys_attr,
                                             &sys_attr_size);

        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        m_central.sys_attr.central_handle = m_central.bond.central_handle;
        m_central.sys_attr.sys_attr_size  = (uint16_t)sys_attr_size;

        // Copy the System Attributes to database.
        m_centrals_db[m_central.bond.central_handle].sys_attr = m_central.sys_attr;

        // Write new central's System Attributes to flash.
        return (sys_attr_store(&m_central.sys_attr));
    }
    else
    {
        // Will not write to flash because System Attributes of an old central would already be
        // in flash and so this operation needs a flash erase operation.
        return NRF_ERROR_INVALID_STATE;
    }
}


uint32_t ble_bondmngr_bonded_centrals_delete(void)
{
    VERIFY_MODULE_INITIALIZED();

    m_centrals_in_db_count         = 0;
    m_bond_info_in_flash_count     = 0;
    m_sys_attr_in_flash_count      = 0;

    return flash_pages_erase();
}


uint32_t ble_bondmngr_central_addr_get(int8_t central_handle, ble_gap_addr_t * p_central_addr)
{
    if (
        (central_handle == INVALID_CENTRAL_HANDLE) ||
        (central_handle >= m_centrals_in_db_count) ||
        (p_central_addr == NULL)                   ||
        (
         m_centrals_db[central_handle].bond.central_addr.addr_type
         ==
         BLE_GAP_ADDR_TYPE_RANDOM_PRIVATE_RESOLVABLE
        )
        )
    {
        return NRF_ERROR_INVALID_PARAM;
    }

    *p_central_addr = m_centrals_db[central_handle].bond.central_addr;
    return NRF_SUCCESS;
}


uint32_t ble_bondmngr_whitelist_get(ble_gap_whitelist_t * p_whitelist)
{
    static ble_gap_addr_t * s_addr[MAX_NUM_CENTRAL_WHITE_LIST];
    static ble_gap_irk_t  * s_irk[MAX_NUM_CENTRAL_WHITE_LIST];

    int i;

    for (i = 0; i < m_irk_count; i++)
    {
        s_irk[i] = m_whitelist_irk[i].p_irk;
    }
    for (i = 0; i < m_addr_count; i++)
    {
        s_addr[i] = m_whitelist_addr[i].p_addr;
    }

    p_whitelist->addr_count = m_addr_count;
    p_whitelist->pp_addrs   = (m_addr_count != 0) ? s_addr : NULL;
    p_whitelist->irk_count  = m_irk_count;
    p_whitelist->pp_irks    = (m_irk_count != 0) ? s_irk : NULL;

    return NRF_SUCCESS;
}


static void bm_pstorage_cb_handler(pstorage_handle_t * handle,
                                   uint8_t             op_code,
                                   uint32_t            result,
                                   uint8_t           * p_data,
                                   uint32_t            data_len)
{
    if (result != NRF_SUCCESS)
    {
        m_bondmngr_config.error_handler(result);
    }
}


uint32_t ble_bondmngr_init(ble_bondmngr_init_t * p_init)
{
    pstorage_module_param_t param;
    uint32_t err_code;

    if (p_init->error_handler == NULL)
    {
        return NRF_ERROR_INVALID_PARAM;
    }
    
    if (BLE_BONDMNGR_MAX_BONDED_CENTRALS > MAX_BONDS_IN_FLASH)
    {
        return NRF_ERROR_DATA_SIZE;
    }
          
    param.block_size  = sizeof (central_bond_t) + sizeof (uint32_t);
    param.block_count = MAX_BONDS_IN_FLASH;
    param.cb          = bm_pstorage_cb_handler;
    
    // Blocks are requested twice, once for bond information and once for system attributes.
    // The number of blocks requested has to be the maximum number of bonded devices that
    // need to be supported. However, the size of blocks can be different if the sizes of
    // system attributes and bonds are not too close.
    err_code = pstorage_register(&param, &mp_flash_bond_info);    
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }
    
    param.block_size = sizeof(central_sys_attr_t) + sizeof(uint32_t);
    
    err_code = pstorage_register(&param, &mp_flash_sys_attr);    
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }    
    
    m_bondmngr_config = *p_init;

    memset(&m_central, 0, sizeof(central_t));

    m_central.bond.central_handle  = INVALID_CENTRAL_HANDLE;
    m_conn_handle                  = BLE_CONN_HANDLE_INVALID;
    m_centrals_in_db_count         = 0;
    m_bond_info_in_flash_count     = 0;
    m_sys_attr_in_flash_count      = 0;

    SECURITY_STATUS_RESET();

    // Erase all stored centrals if specified.
    if (m_bondmngr_config.bonds_delete)
    {
        err_code = flash_pages_erase();
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }

        m_centrals_in_db_count = 0;
        
        for (int i = m_centrals_in_db_count; i < BLE_BONDMNGR_MAX_BONDED_CENTRALS; i++)
        {
            m_centrals_db[i].bond.central_handle     = INVALID_CENTRAL_HANDLE;
            m_centrals_db[i].sys_attr.sys_attr_size  = 0;
            m_centrals_db[i].sys_attr.central_handle = INVALID_CENTRAL_HANDLE;
        }
    }
    else
    {
        // Load bond manager data from flash.
        err_code = load_all_from_flash();
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    m_is_bondmngr_initialized = true;
    
    return NRF_SUCCESS;
}


uint32_t ble_bondmngr_central_ids_get(uint16_t * p_central_ids, uint16_t * p_length)
{
    VERIFY_MODULE_INITIALIZED();

    if (p_length == NULL)
    {
        return NRF_ERROR_NULL;
    }

    if (*p_length < m_centrals_in_db_count)
    {
        // Length of the input array is not enough to fit all known central identifiers.
        return NRF_ERROR_DATA_SIZE;
    }

    *p_length = m_centrals_in_db_count;
    if (p_central_ids == NULL)
    {
        // Only the length field was required to be filled.
        return NRF_SUCCESS;
    }

    for (int i = 0; i < m_centrals_in_db_count; i++)
    {
        p_central_ids[i] = m_centrals_db[i].bond.central_id_info.div;
    }

    return NRF_SUCCESS;
}


uint32_t ble_bondmngr_bonded_central_delete(uint16_t central_id)
{
    VERIFY_MODULE_INITIALIZED();

    int8_t  central_handle_to_be_deleted = INVALID_CENTRAL_HANDLE;
    uint8_t i;

    // Search for the handle of the central.
    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        if (m_centrals_db[i].bond.central_id_info.div == central_id)
        {
            central_handle_to_be_deleted = i;
            break;
        }
    }

    if (central_handle_to_be_deleted == INVALID_CENTRAL_HANDLE)
    {
        // Central ID not found.
        return NRF_ERROR_NOT_FOUND;
    }

    // Delete the central in RAM.
    for (i = central_handle_to_be_deleted; i < (m_centrals_in_db_count - 1); i++)
    {
        // Overwrite the current central entry with the next one.
        m_centrals_db[i] = m_centrals_db[i + 1];

        // Decrement the value of handle.
        m_centrals_db[i].bond.central_handle--;
        if (INVALID_CENTRAL_HANDLE != m_centrals_db[i].sys_attr.central_handle)
        {
            m_centrals_db[i].sys_attr.central_handle--;
        }
    }

    // Clear the last database entry.
    memset(&(m_centrals_db[m_centrals_in_db_count - 1]), 0, sizeof(central_t));

    m_centrals_in_db_count--;

    uint32_t err_code;

    // Reinitialize the pointers to the memory where bonding info and System Attributes are stored
    // in flash.
    // Refresh the data in the flash memory (both Bonding Information and System Attributes).
    // Erase and rewrite bonding info and System Attributes.

    err_code = flash_pages_erase();
    if (err_code != NRF_SUCCESS)
    {
        return err_code;
    }

    m_bond_info_in_flash_count    = 0;
    m_sys_attr_in_flash_count     = 0;

    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        err_code = bond_info_store(&(m_centrals_db[i].bond));
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    for (i = 0; i < m_centrals_in_db_count; i++)
    {
        err_code = sys_attr_store(&(m_centrals_db[i].sys_attr));
        if (err_code != NRF_SUCCESS)
        {
            return err_code;
        }
    }

    update_whitelist();

    return NRF_SUCCESS;
}


uint32_t ble_bondmngr_is_link_encrypted (bool * status)
{
    VERIFY_MODULE_INITIALIZED();

    (*status) = ENCRYPTION_STATUS_GET();

    return NRF_SUCCESS;
}
