/***************************************************************************
 *
 * Copyright 2015-2019 BES.
 * All rights reserved. All unpublished rights reserved.
 *
 * No part of this work may be used or reproduced in any form or by any
 * means, or stored in a database or retrieval system, without prior written
 * permission of BES.
 *
 * Use of this work is governed by a license granted by BES.
 * This work contains confidential and proprietary information of
 * BES. which is protected by copyright, trade secret,
 * trademark and other intellectual property rights.
 *
 ****************************************************************************/
/**
 ****************************************************************************************
 * @addtogroup APP
 * @{
 ****************************************************************************************
 */
/*
 * INCLUDE FILES
 ****************************************************************************************
 */

#include "rwip_config.h"             // SW configuration
#if (BLE_APP_PRESENT)
#include <string.h>

#include "app_task.h"                // Application task Definition
#include "app.h"                     // Application Definition
#include "gap.h"                     // GAP Definition
#include "tgt_hardware.h"
#include "gapm.h"
#include "gapm_task.h"               // GAP Manager Task API
#include "gapc_task.h"               // GAP Controller Task API

#include "gattc_task.h"

#include "co_bt.h"                   // Common BT Definition
#include "co_math.h"                 // Common Maths Definition
#include "l2cc.h"
#include "l2cc_pdu.h"

#include "nvrecord_ble.h"
#include "prf.h"

#include "nvrecord_env.h"
#ifndef _BLE_NVDS_
#include "tgt_hardware.h"
#endif
#ifdef __AI_VOICE__
#include "app_ai_ble.h"                  // AI Voice Application Definitions
#endif //(__AI_VOICE__)

#if (BLE_APP_SEC)
#include "app_sec.h"                 // Application security Definition
#endif // (BLE_APP_SEC)


#if (BLE_APP_DATAPATH_SERVER)
#include "app_datapath_server.h"                  // Data Path Server Application Definitions
#endif //(BLE_APP_DATAPATH_SERVER)

#if (BLE_APP_DIS)
#include "app_dis.h"                 // Device Information Service Application Definitions
#endif //(BLE_APP_DIS)

#if (BLE_APP_BATT)
#include "app_batt.h"                // Battery Application Definitions
#endif //(BLE_APP_DIS)

#if (BLE_APP_HID)
#include "app_hid.h"                 // HID Application Definitions
#endif //(BLE_APP_HID)

#if (BLE_APP_VOICEPATH)
#include "app_voicepath_ble.h"         // Voice Path Application Definitions
#endif //(BLE_APP_VOICEPATH)

#if (BLE_APP_OTA)
#include "app_ota.h"                  // OTA Application Definitions
#endif //(BLE_APP_OTA)

#if (BLE_APP_TOTA)
#include "app_tota_ble.h"                  // OTA Application Definitions
#endif //(BLE_APP_TOTA)

#if (BLE_APP_ANCC)
#include "app_ancc.h"                  // ANCC Application Definitions
#endif //(BLE_APP_ANCC)

#if (BLE_APP_AMS)
#include "app_amsc.h"               // AMS Module Definition
#endif // (BLE_APP_AMS)

#if (BLE_APP_GFPS)
#include "app_gfps.h"               // Google Fast Pair Service Definitions
#endif
#if (DISPLAY_SUPPORT)
#include "app_display.h"             // Application Display Definition
#endif //(DISPLAY_SUPPORT)

#ifdef BLE_APP_AM0
#include "am0_app.h"                 // Audio Mode 0 Application
#endif //defined(BLE_APP_AM0)

#if (NVDS_SUPPORT)
#include "nvds.h"                    // NVDS Definitions
#endif //(NVDS_SUPPORT)

#include "cmsis_os.h"
#include "ke_timer.h"
#include "nvrecord.h"
#include "ble_app_dbg.h"
#include "app_bt.h"
#include "app_ble_mode_switch.h"
#include "apps.h"
#include "crc16.h"
#include "besbt.h"

#ifdef BISTO_ENABLED
#include "gsound_service.h"
#endif

#if defined(__INTERCONNECTION__)
#include "app_bt.h"
#include "app_battery.h"
#endif

#if (BLE_APP_TILE)
#include "tile_target_ble.h"
#include "tile_service.h"
#endif

#if defined(IBRT)
#include "app_tws_ibrt.h"
#endif

#ifdef FPGA
#include "hal_timer.h"
#endif

#ifdef __GATT_OVER_BR_EDR__
#include "btgatt_api.h"
#endif

/*
 * DEFINES
 ****************************************************************************************
 */
/// Default Device Name if no value can be found in NVDS
#define APP_DFLT_DEVICE_NAME            ("BES_BLE")
#define APP_DFLT_DEVICE_NAME_LEN        (sizeof(APP_DFLT_DEVICE_NAME))

#if (BLE_APP_HID)
// HID Mouse
#define DEVICE_NAME        "Hid Mouse"
#else
#define DEVICE_NAME        "RW DEVICE"
#endif

#define DEVICE_NAME_SIZE    sizeof(DEVICE_NAME)

/**
 * UUID List part of ADV Data
 * --------------------------------------------------------------------------------------
 * x03 - Length
 * x03 - Complete list of 16-bit UUIDs available
 * x09\x18 - Health Thermometer Service UUID
 *   or
 * x12\x18 - HID Service UUID
 * --------------------------------------------------------------------------------------
 */

#if (BLE_APP_HT)
#define APP_HT_ADV_DATA_UUID        "\x03\x03\x09\x18"
#define APP_HT_ADV_DATA_UUID_LEN    (4)
#endif //(BLE_APP_HT)


#if (BLE_APP_HID)
#define APP_HID_ADV_DATA_UUID       "\x03\x03\x12\x18"
#define APP_HID_ADV_DATA_UUID_LEN   (4)
#endif //(BLE_APP_HID)

#if (BLE_APP_DATAPATH_SERVER)
/*
 * x11 - Length
 * x07 - Complete list of 16-bit UUIDs available
 * .... the 128 bit UUIDs
 */
#define APP_DATAPATH_SERVER_ADV_DATA_UUID        "\x11\x07\x9e\x34\x9B\x5F\x80\x00\x00\x80\x00\x10\x00\x00\x00\x01\x00\x01"
#define APP_DATAPATH_SERVER_ADV_DATA_UUID_LEN    (18)
#endif //(BLE_APP_HT)

/**
 * Appearance part of ADV Data
 * --------------------------------------------------------------------------------------
 * x03 - Length
 * x19 - Appearance
 * x03\x00 - Generic Thermometer
 *   or
 * xC2\x04 - HID Mouse
 * --------------------------------------------------------------------------------------
 */

#if (BLE_APP_HT)
#define APP_HT_ADV_DATA_APPEARANCE    "\x03\x19\x00\x03"
#endif //(BLE_APP_HT)

#if (BLE_APP_HID)
#define APP_HID_ADV_DATA_APPEARANCE   "\x03\x19\xC2\x03"
#endif //(BLE_APP_HID)

#define APP_ADV_DATA_APPEARANCE_LEN  (4)



/**
 * Advertising Parameters
 */
#if (BLE_APP_HID)
/// Default Advertising duration - 30s (in multiple of 10ms)
#define APP_DFLT_ADV_DURATION   (3000)
#endif //(BLE_APP_HID)
/// Advertising channel map - 37, 38, 39
#define APP_ADV_CHMAP           (0x07)

/*
 * TYPE DEFINITIONS
 ****************************************************************************************
 */

typedef void (*appm_add_svc_func_t)(void);

/*
 * ENUMERATIONS
 ****************************************************************************************
 */

/// List of service to add in the database
enum appm_svc_list
{
    #if (BLE_APP_HT)
    APPM_SVC_HTS,
    #endif //(BLE_APP_HT)
    #if (BLE_APP_DIS)
    APPM_SVC_DIS,
    #endif //(BLE_APP_DIS)
    #if (BLE_APP_BATT)
    APPM_SVC_BATT,
    #endif //(BLE_APP_BATT)
    #if (BLE_APP_HID)
    APPM_SVC_HIDS,
    #endif //(BLE_APP_HID)
    #ifdef BLE_APP_AM0
    APPM_SVC_AM0_HAS,
    #endif //defined(BLE_APP_AM0)
    #if (BLE_APP_HR)
    APPM_SVC_HRP,
    #endif
    #if (BLE_APP_DATAPATH_SERVER)
    APPM_SVC_DATAPATH_SERVER,
    #endif //(BLE_APP_DATAPATH_SERVER)
    #if (BLE_APP_VOICEPATH)
    APPM_SVC_VOICEPATH,
    #ifdef BISTO_ENABLED
    APPM_SVC_BMS,
    #endif
    #endif //(BLE_APP_VOICEPATH)
    #if (ANCS_PROXY_ENABLE)
    APPM_SVC_ANCSP,
    APPM_SVC_AMSP,
    #endif
    #if (BLE_APP_ANCC)
    APPM_SVC_ANCC,
    #endif //(BLE_APP_ANCC)
    #if (BLE_APP_AMS)
    APPM_SVC_AMSC,
    #endif //(BLE_APP_AMS)
    #if (BLE_APP_OTA)
    APPM_SVC_OTA,
    #endif //(BLE_APP_OTA)
    #if (BLE_APP_GFPS)
    APPM_SVC_GFPS,
    #endif //(BLE_APP_GFPS)
    #if (BLE_AI_VOICE)
    APPM_AI_SMARTVOICE,
    #endif //(BLE_AI_VOICE)
    #if (BLE_APP_TOTA)
    APPM_SVC_TOTA,
    #endif //(BLE_APP_TOTA)

    #if (BLE_APP_TILE)
    APPM_SVC_TILE,
    #endif //(BLE_APP_TILE)

    APPM_SVC_LIST_STOP,
};

/*
 * LOCAL VARIABLES DEFINITIONS
 ****************************************************************************************
 */
//gattc_msg_handler_tab
//#define KE_MSG_HANDLER_TAB(task)   __STATIC const struct ke_msg_handler task##_msg_handler_tab[] =
/// Application Task Descriptor

//static const struct ke_task_desc TASK_DESC_APP = {&appm_default_handler,
//                                                  appm_state, APPM_STATE_MAX, APP_IDX_MAX};
extern const struct ke_task_desc TASK_DESC_APP;

/// List of functions used to create the database
static const appm_add_svc_func_t appm_add_svc_func_list[APPM_SVC_LIST_STOP] =
{
    #if (BLE_APP_HT)
    (appm_add_svc_func_t)app_ht_add_hts,
    #endif //(BLE_APP_HT)
    #if (BLE_APP_DIS)
    (appm_add_svc_func_t)app_dis_add_dis,
    #endif //(BLE_APP_DIS)
    #if (BLE_APP_BATT)
    (appm_add_svc_func_t)app_batt_add_bas,
    #endif //(BLE_APP_BATT)
    #if (BLE_APP_HID)
    (appm_add_svc_func_t)app_hid_add_hids,
    #endif //(BLE_APP_HID)
    #ifdef BLE_APP_AM0
    (appm_add_svc_func_t)am0_app_add_has,
    #endif //defined(BLE_APP_AM0)
    #if (BLE_APP_HR)
    (appm_add_svc_func_t)app_hrps_add_profile,
    #endif
    #if (BLE_APP_DATAPATH_SERVER)
    (appm_add_svc_func_t)app_datapath_add_datapathps,
    #endif //(BLE_APP_DATAPATH_SERVER)
    #if (BLE_APP_VOICEPATH)
    (appm_add_svc_func_t)app_ble_voicepath_add_svc,
    #ifdef BISTO_ENABLED
    (appm_add_svc_func_t)app_ble_bms_add_svc,
    #endif
    #endif //(BLE_APP_VOICEPATH)
    #if (ANCS_PROXY_ENABLE)
    (appm_add_svc_func_t)app_ble_ancsp_add_svc,
    (appm_add_svc_func_t)app_ble_amsp_add_svc,
    #endif
    #if (BLE_APP_ANCC)
    (appm_add_svc_func_t)app_ancc_add_ancc,
    #endif
    #if (BLE_APP_AMS)
    (appm_add_svc_func_t)app_amsc_add_amsc,
    #endif
    #if (BLE_APP_OTA)
    (appm_add_svc_func_t)app_ota_add_ota,
    #endif //(BLE_APP_OTA)
    #if (BLE_APP_GFPS)
    (appm_add_svc_func_t)app_gfps_add_gfps,
    #endif
    #if (BLE_APP_AI_VOICE)
    (appm_add_svc_func_t)app_ai_add_ai,
    #endif //(BLE_APP_AI_VOICE)
    #if (BLE_APP_TOTA)
    (appm_add_svc_func_t)app_tota_add_tota,
    #endif //(BLE_APP_TOTA)
    #if (BLE_APP_TILE)
    (appm_add_svc_func_t)app_ble_tile_add_svc,
    #endif
};

/*
 * GLOBAL VARIABLE DEFINITIONS
 ****************************************************************************************
 */

/// Application Environment Structure
struct app_env_tag app_env;

static APP_BLE_NEGOTIATED_CONN_PARAM_T negotiatedBleConnParam[BLE_CONNECTION_MAX];

/*
 * FUNCTION DEFINITIONS
 ****************************************************************************************
 */
void appm_refresh_ble_irk(void)
{
    nv_record_blerec_get_local_irk(app_env.loc_irk);
    LOG_I("local irk:");
    DUMP8("0x%02x ", app_env.loc_irk, 16);
    gapm_update_irk(app_env.loc_irk);
}

uint8_t *appm_get_current_ble_irk(void)
{
    return app_env.loc_irk;
}

void appm_init()
{
    BLE_APP_FUNC_ENTER();

    // Reset the application manager environment
    memset(&app_env, 0, sizeof(app_env));

    // Create APP task
    ke_task_create(TASK_APP, &TASK_DESC_APP);

    // Initialize Task state
    ke_state_set(TASK_APP, APPM_INIT);

    // Load the device name from NVDS

    // Get the Device Name to add in the Advertising Data (Default one or NVDS one)
#ifdef _BLE_NVDS_
    const char* ble_name_in_nv = nvrec_dev_get_ble_name();
#else
    const char* ble_name_in_nv = BLE_DEFAULT_NAME;
#endif
    uint32_t nameLen = strlen(ble_name_in_nv) + 1;
    if (nameLen > APP_DEVICE_NAME_MAX_LEN)
    {
        nameLen = APP_DEVICE_NAME_MAX_LEN;
    }
    // Get default Device Name (No name if not enough space)
    memcpy(app_env.dev_name, ble_name_in_nv, nameLen);
    app_env.dev_name[nameLen - 1] = '\0';
    app_env.dev_name_len = nameLen;
    LOG_I("device ble name:%s", app_env.dev_name);
#ifdef _BLE_NVDS_
    nv_record_blerec_init();

    nv_record_blerec_get_local_irk(app_env.loc_irk);
#else
    uint8_t counter;

    //avoid ble irk collision low probability
    uint32_t generatedSeed = hal_sys_timer_get();
    for (uint8_t index = 0; index < sizeof(bt_addr); index++)
    {
        generatedSeed ^= (((uint32_t)(bt_addr[index])) << (hal_sys_timer_get()&0xF));
    }
    srand(generatedSeed);

    // generate a new IRK
    for (counter = 0; counter < KEY_LEN; counter++)
    {
        app_env.loc_irk[counter]    = (uint8_t)co_rand_word();
    }
#endif

    /*------------------------------------------------------
     * INITIALIZE ALL MODULES
     *------------------------------------------------------*/

    // load device information:

    #if (DISPLAY_SUPPORT)
    app_display_init();
    #endif //(DISPLAY_SUPPORT)

    #if (BLE_APP_SEC)
    // Security Module
    app_sec_init();
    #endif // (BLE_APP_SEC)

    #if (BLE_APP_HT)
    // Health Thermometer Module
    app_ht_init();
    #endif //(BLE_APP_HT)

    #if (BLE_APP_DIS)
    // Device Information Module
    app_dis_init();
    #endif //(BLE_APP_DIS)

    #if (BLE_APP_HID)
    // HID Module
    app_hid_init();
    #endif //(BLE_APP_HID)

    #if (BLE_APP_BATT)
    // Battery Module
    app_batt_init();
    #endif //(BLE_APP_BATT)

    #ifdef BLE_APP_AM0
    // Audio Mode 0 Module
    am0_app_init();
    #endif // defined(BLE_APP_AM0)

    #if (BLE_APP_VOICEPATH)
    // Voice Path Module
    app_ble_voicepath_init();
    #endif //(BLE_APP_VOICEPATH)

    #if (BLE_APP_TILE)
    app_ble_tile_init();
    #endif
    
    #if (BLE_APP_DATAPATH_SERVER)
    // Data Path Server Module
    app_datapath_server_init();
    #endif //(BLE_APP_DATAPATH_SERVER)
    #if (BLE_APP_AI_VOICE)
    // AI Voice Module
    app_ai_init();
    #endif //(BLE_APP_AI_VOICE)

    #if (BLE_APP_OTA)
    // OTA Module
    app_ota_init();
    #endif //(BLE_APP_OTA)

    #if (BLE_APP_TOTA)
    // TOTA Module
    app_tota_ble_init();
    #endif //(BLE_APP_TOTA)

    #if (BLE_APP_GFPS)
    //
    app_gfps_init();
    #endif

    #if (BLE_APP_TILE)
    app_tile_init();
     #endif 
    
    BLE_APP_FUNC_LEAVE();
}

bool appm_add_svc(void)
{
    // Indicate if more services need to be added in the database
    bool more_svc = false;

    // Check if another should be added in the database
    if (app_env.next_svc != APPM_SVC_LIST_STOP)
    {
        ASSERT_INFO(appm_add_svc_func_list[app_env.next_svc] != NULL, app_env.next_svc, 1);

        BLE_APP_DBG("appm_add_svc adds service");

        // Call the function used to add the required service
        appm_add_svc_func_list[app_env.next_svc]();

        // Select following service to add
        app_env.next_svc++;
        more_svc = true;
    }
    else
    {
        BLE_APP_DBG("appm_add_svc doesn't execute, next svc is %d", app_env.next_svc);
    }


    return more_svc;
}

uint16_t appm_get_conhdl_from_conidx(uint8_t conidx)
{
    return app_env.context[conidx].conhdl;
}

void appm_disconnect(uint8_t conidx)
{
    if (BLE_DISCONNECTED != app_env.context[conidx].connectStatus)
    {
        struct gapc_disconnect_cmd *cmd = KE_MSG_ALLOC(GAPC_DISCONNECT_CMD,
                                                   KE_BUILD_ID(TASK_GAPC, conidx),
                                                   TASK_APP,
                                                   gapc_disconnect_cmd);

        cmd->operation = GAPC_DISCONNECT;
        cmd->reason    = CO_ERROR_REMOTE_USER_TERM_CON;

        // Send the message
        ke_msg_send(cmd);
    }
}

// TODO: freddie modify this part
#if (BLE_APP_GFPS)
#define UPDATE_BLE_ADV_DATA_RIGHT_BEFORE_STARTING_ADV_ENABLED   1
#else
#define UPDATE_BLE_ADV_DATA_RIGHT_BEFORE_STARTING_ADV_ENABLED   0
#endif

void gapm_update_ble_adv_data_right_before_started(uint8_t* pAdvData, uint8_t* advDataLen,
    uint8_t* pScanRspData, uint8_t* scanRspDataLen, void* adv_cmd)
{
    // for the user case that the BLE adv data or scan rsp data need to be generated from the
    // run-time BLE address.
    // Just enable macro UPDATE_BLE_ADV_DATA_RIGHT_BEFORE_STARTING_ADV_ENABLED,
    // and call appm_get_current_ble_addr to get the run-time used BLE address
    // For fastpair GFPS_ACCOUNTKEY_SALT_TYPE==USE_BLE_ADDR_AS_SALT case,
    // this macro must be enabled

// #if UPDATE_BLE_ADV_DATA_RIGHT_BEFORE_STARTING_ADV_ENABLED
//     appm_fill_ble_adv_scan_rsp_data(pAdvData, advDataLen, pScanRspData, scanRspDataLen, adv_cmd);
// #endif
    // TODO: freddie implement this API
}

/**
 ****************************************************************************************
 * Advertising Functions
 ****************************************************************************************
 */
void appm_start_advertising(void *param)
{
    BLE_APP_FUNC_ENTER();

    LOG_I("%s state: %d", __func__, ke_state_get(TASK_APP));

    // Prepare the GAPM_START_ADVERTISE_CMD message
    struct gapm_start_advertise_cmd *cmd = KE_MSG_ALLOC(GAPM_START_ADVERTISE_CMD,
                                                        TASK_GAPM, TASK_APP,
                                                        gapm_start_advertise_cmd);

    BLE_ADV_PARAM_T* advParam = (BLE_ADV_PARAM_T*)param;

#ifdef BLE_USE_RPA
    cmd->op.addr_src    = GAPM_GEN_RSLV_ADDR;
    cmd->op.randomAddrRenewIntervalInSecond = (uint16_t)(60*15);
#else
    cmd->op.addr_src    = GAPM_STATIC_ADDR;

    // To use non-resolvable address
    //cmd->op.addr_src    = GAPM_GEN_NON_RSLV_ADDR;
    //cmd->op.randomAddrRenewIntervalInSecond = (uint16_t)(60*15);
#endif

    cmd->channel_map     = APP_ADV_CHMAP;
    cmd->intv_min        = advParam->advInterval * 8 / 5;
    cmd->intv_max        = advParam->advInterval * 8 / 5;
    cmd->op.code         = advParam->advType;
    cmd->info.host.mode  = GAP_GEN_DISCOVERABLE;

    cmd->isIncludedFlags = advParam->withFlag;
    cmd->info.host.flags = GAP_LE_GEN_DISCOVERABLE_FLG | GAP_BR_EDR_NOT_SUPPORTED;

    cmd->info.host.adv_data_len = advParam->advDataLen;
    memcpy(cmd->info.host.adv_data, advParam->advData, cmd->info.host.adv_data_len);
    cmd->info.host.scan_rsp_data_len = advParam->scanRspDataLen;
    memcpy(cmd->info.host.scan_rsp_data, advParam->scanRspData, cmd->info.host.scan_rsp_data_len);

    if (advParam->withFlag)
    {
        ASSERT(cmd->info.host.adv_data_len <= BLE_ADV_DATA_WITH_FLAG_LEN, "adv data exceed");
    }
    else
    {
        ASSERT(cmd->info.host.adv_data_len <= BLE_ADV_DATA_WITHOUT_FLAG_LEN, "adv data exceed");
    }
    ASSERT(cmd->info.host.scan_rsp_data_len <= SCAN_RSP_DATA_LEN, "scan rsp data exceed");
    LOG_I("[ADDR_TYPE]:%d", cmd->op.addr_src);
    if (GAPM_GEN_RSLV_ADDR == cmd->op.addr_src)
    {
        LOG_I("[IRK]:");
        DUMP8("0x%02x ", appm_get_current_ble_irk(), KEY_LEN);
    }
    else if (GAPM_STATIC_ADDR == cmd->op.addr_src)
    {
        LOG_I("[ADDR]:");
        DUMP8("0x%02x ", bt_get_ble_local_address(), 6);
    }

    LOG_I("[ADV_TYPE]:%d", cmd->op.code);
    LOG_I("[ADV_INTERVAL]:%d", cmd->intv_min);

    // Send the message
    ke_msg_send(cmd);

    // Set the state of the task to APPM_ADVERTISING
    ke_state_set(TASK_APP, APPM_ADVERTISING);

    BLE_APP_FUNC_LEAVE();
}


void appm_stop_advertising(void)
{

    #if (BLE_APP_HID)
    // Stop the advertising timer if needed
    if (ke_timer_active(APP_ADV_TIMEOUT_TIMER, TASK_APP))
    {
        ke_timer_clear(APP_ADV_TIMEOUT_TIMER, TASK_APP);
    }
    #endif //(BLE_APP_HID)

    // Go in ready state
    ke_state_set(TASK_APP, APPM_READY);

    // Prepare the GAPM_CANCEL_CMD message
    struct gapm_cancel_cmd *cmd = KE_MSG_ALLOC(GAPM_CANCEL_CMD,
                                                TASK_GAPM, TASK_APP,
                                                gapm_cancel_cmd);

    cmd->operation = GAPM_CANCEL;

    // Send the message
    ke_msg_send(cmd);

    #if (DISPLAY_SUPPORT)
    // Update advertising state screen
    app_display_set_adv(false);
    #endif //(DISPLAY_SUPPORT)

}
void appm_start_scanning(uint16_t intervalInMs, uint16_t windowInMs, uint32_t filtPolicy)
{
    struct gapm_start_scan_cmd *cmd = KE_MSG_ALLOC(GAPM_START_SCAN_CMD,
                                                   TASK_GAPM,
                                                   TASK_APP,
                                                   gapm_start_scan_cmd);

    cmd->op.code = GAPM_SCAN_PASSIVE;
    cmd->op.addr_src = GAPM_STATIC_ADDR;

    cmd->interval = intervalInMs*1000/625;
    cmd->window = windowInMs*1000/625;
    cmd->mode = GAP_OBSERVER_MODE; //GAP_GEN_DISCOVERY;
    cmd->filt_policy = filtPolicy;
    cmd->filter_duplic = SCAN_FILT_DUPLIC_DIS;

    ke_state_set(TASK_APP, APPM_SCANNING);

    ke_msg_send(cmd);
}

void appm_stop_scanning(void)
{
    //if (ke_state_get(TASK_APP) == APPM_SCANNING)
    {
        // Go in ready state
        ke_state_set(TASK_APP, APPM_READY);

        // Prepare the GAPM_CANCEL_CMD message
        struct gapm_cancel_cmd *cmd = KE_MSG_ALLOC(GAPM_CANCEL_CMD,
                                                   TASK_GAPM, TASK_APP,
                                                   gapm_cancel_cmd);

        cmd->operation = GAPM_CANCEL;

        // Send the message
        ke_msg_send(cmd);
    }
}

void appm_add_dev_into_whitelist(struct gap_bdaddr* ptBdAddr)
{
    struct gapm_white_list_mgt_cmd *cmd = KE_MSG_ALLOC_DYN(GAPM_WHITE_LIST_MGT_CMD,
                                                           TASK_GAPM,
                                                           TASK_APP,
                                                           gapm_white_list_mgt_cmd,
                                                           sizeof(struct gap_bdaddr));
    cmd->operation = GAPM_ADD_DEV_IN_WLIST;
    cmd->nb = 1;

    memcpy(cmd->devices, ptBdAddr, sizeof(struct gap_bdaddr));

    ke_msg_send(cmd);
}


struct gap_bdaddr BLE_BdAddr;//{{0x14, 0x71, 0xda, 0x7d, 0x1a, 0x00}, 0};
void pts_ble_addr_init(void)
{
    uint8_t addr[6] = {0x14, 0x71, 0xda, 0x7d, 0x1a, 0x00};
    memcpy(BLE_BdAddr.addr.addr, addr, 6);
    BLE_BdAddr.addr_type = 0;
}

void appm_start_connecting(struct gap_bdaddr* ptBdAddr)
{
    struct gapm_start_connection_cmd *cmd = KE_MSG_ALLOC_DYN(GAPM_START_CONNECTION_CMD,
                                                             TASK_GAPM,
                                                             TASK_APP,
                                                             gapm_start_connection_cmd,
                                                             sizeof(struct gap_bdaddr));
    cmd->ce_len_max = 4;
    cmd->ce_len_min = 0;
    cmd->con_intv_max = 6; // in the unit of 1.25ms
    cmd->con_intv_min = 6; // in the unit of 1.25ms
    cmd->con_latency = 0;
    cmd->superv_to = 1000;  // in the unit of 10ms
    cmd->op.code = GAPM_CONNECTION_DIRECT;
    cmd->op.addr_src = GAPM_STATIC_ADDR;
    cmd->nb_peers = 1;
    cmd->scan_interval = ((60) * 1000 / 625);
    cmd->scan_window = ((30) * 1000 / 625);

    memcpy(cmd->peers, ptBdAddr, sizeof(struct gap_bdaddr));

    ke_state_set(TASK_APP, APPM_CONNECTING);

    ke_msg_send(cmd);
}

void appm_stop_connecting(void)
{
        // Go in ready state
        ke_state_set(TASK_APP, APPM_READY);

        // Prepare the GAPM_CANCEL_CMD message
        struct gapm_cancel_cmd *cmd = KE_MSG_ALLOC(GAPM_CANCEL_CMD,
                                                   TASK_GAPM,
                                                   TASK_APP,
                                                   gapm_cancel_cmd);

        cmd->operation = GAPM_CANCEL;

        // Send the message
        ke_msg_send(cmd);
}


void appm_update_param(uint8_t conidx, struct gapc_conn_param *conn_param)
{
    // Prepare the GAPC_PARAM_UPDATE_CMD message
    struct gapc_param_update_cmd *cmd = KE_MSG_ALLOC(GAPC_PARAM_UPDATE_CMD,
                                                     KE_BUILD_ID(TASK_GAPC, conidx),
                                                     TASK_APP,
                                                     gapc_param_update_cmd);

    cmd->operation  = GAPC_UPDATE_PARAMS;
    cmd->intv_min   = conn_param->intv_min;
    cmd->intv_max   = conn_param->intv_max;
    cmd->latency    = conn_param->latency;
    cmd->time_out   = conn_param->time_out;

    // not used by a slave device
    cmd->ce_len_min = 0xFFFF;
    cmd->ce_len_max = 0xFFFF;

    // Send the message
    ke_msg_send(cmd);
}

void l2cap_update_param(uint8_t  conidx,
                        uint32_t min_interval_in_ms,
                        uint32_t max_interval_in_ms,
                        uint32_t supervision_timeout_in_ms,
                        uint8_t  slaveLantency)
{
    struct l2cc_update_param_req *req = L2CC_SIG_PDU_ALLOC(conidx,
                                                           L2C_CODE_CONN_PARAM_UPD_REQ,
                                                           KE_BUILD_ID(TASK_GAPC, conidx),
                                                           l2cc_update_param_req);

    // generate packet identifier
    uint8_t pkt_id = co_rand_word() & 0xFF;
    if  (pkt_id == 0)
    {
        pkt_id = 1;
    }

    /* fill up the parameters */
    req->intv_max = (uint16_t)(max_interval_in_ms/1.25);
    req->intv_min = (uint16_t)(min_interval_in_ms/1.25);
    req->latency  = slaveLantency;
    req->timeout  = supervision_timeout_in_ms/10;
    req->pkt_id   = pkt_id;
    LOG_I("%s val: 0x%x 0x%x 0x%x 0x%x 0x%x",
          __func__,
          req->intv_max,
          req->intv_min,
          req->latency,
          req->timeout,
          req->pkt_id);
    l2cc_pdu_send(req);
}

uint8_t appm_get_dev_name(uint8_t* name)
{
    // copy name to provided pointer
    memcpy(name, app_env.dev_name, app_env.dev_name_len);
    // return name length
    return app_env.dev_name_len;
}

void appm_exchange_mtu(uint8_t conidx)
{
    struct gattc_exc_mtu_cmd *cmd = KE_MSG_ALLOC(GATTC_EXC_MTU_CMD,
                                                 KE_BUILD_ID(TASK_GATTC, conidx),
                                                 TASK_APP,
                                                 gattc_exc_mtu_cmd);

    cmd->operation = GATTC_MTU_EXCH;
    cmd->seq_num= 0;

    ke_msg_send(cmd);
}

void appm_check_and_resolve_ble_address(uint8_t conidx)
{
    APP_BLE_CONN_CONTEXT_T* pContext = &(app_env.context[conidx]);

#ifdef __GATT_OVER_BR_EDR__
    if (conidx == btif_btgatt_get_connection_index())
    {
        pContext->isGotSolvedBdAddr = true;
        pContext->isBdAddrResolvingInProgress = false;
        btif_btgatt_get_device_address(pContext->solvedBdAddr);
    }
#endif

    // solved already, return
    if (pContext->isGotSolvedBdAddr)
    {
        LOG_I("Already get solved bd addr.");
        return;
    }
    // not solved yet and the solving is in progress, return and wait
    else if (app_is_resolving_ble_bd_addr())
    {
        LOG_I("Random bd addr solving on going.");
        return;
    }

    if (BLE_RANDOM_ADDR == pContext->peerAddrType)
    {
        memset(pContext->solvedBdAddr, 0, BD_ADDR_LEN);
        bool isSuccessful = appm_resolve_random_ble_addr_from_nv(conidx, pContext->bdAddr);
        LOG_I("%s isSuccessful %d", __func__, isSuccessful);
        if (isSuccessful)
        {
            pContext->isBdAddrResolvingInProgress = true;
        }
        else
        {
            pContext->isGotSolvedBdAddr = true;
            pContext->isBdAddrResolvingInProgress = false;
        }
    }
    else
    {
        pContext->isGotSolvedBdAddr = true;
        pContext->isBdAddrResolvingInProgress = false;
        memcpy(pContext->solvedBdAddr, pContext->bdAddr, BD_ADDR_LEN);
    }

}

bool appm_resolve_random_ble_addr_from_nv(uint8_t conidx, uint8_t* randomAddr)
{
#ifdef _BLE_NVDS_
    struct gapm_resolv_addr_cmd *cmd = KE_MSG_ALLOC_DYN(GAPM_RESOLV_ADDR_CMD,
                                                        KE_BUILD_ID(TASK_GAPM, conidx),
                                                        TASK_APP,
                                                        gapm_resolv_addr_cmd,
                                                        BLE_RECORD_NUM * GAP_KEY_LEN);

    uint8_t irkeyNum = nv_record_ble_fill_irk((uint8_t *)(cmd->irk));
    if (0 == irkeyNum)
    {
        LOG_I("No history irk, cannot solve bd addr.");
        KE_MSG_FREE(cmd);
        return false;
    }

    LOG_I("Start random bd addr solving.");

    cmd->operation = GAPM_RESOLV_ADDR;
    cmd->nb_key = irkeyNum;
    memcpy(cmd->addr.addr, randomAddr, GAP_BD_ADDR_LEN);
    ke_msg_send(cmd);
    return true;
#else
    return false;
#endif

}

void appm_resolve_random_ble_addr_with_sepcific_irk(uint8_t conidx, uint8_t* randomAddr, uint8_t* pIrk)
{
    struct gapm_resolv_addr_cmd *cmd = KE_MSG_ALLOC_DYN(GAPM_RESOLV_ADDR_CMD,
                                       KE_BUILD_ID(TASK_GAPM, conidx), TASK_APP,
                                       gapm_resolv_addr_cmd,
                                       GAP_KEY_LEN);
    cmd->operation = GAPM_RESOLV_ADDR;
    cmd->nb_key = 1;
    memcpy(cmd->addr.addr, randomAddr, GAP_BD_ADDR_LEN);
    memcpy(cmd->irk, pIrk, GAP_KEY_LEN);
    ke_msg_send(cmd);
}

void appm_random_ble_addr_solved(bool isSolvedSuccessfully, uint8_t* irkUsedForSolving)
{
    APP_BLE_CONN_CONTEXT_T* pContext;
    uint32_t conidx;
    for (conidx = 0;conidx < BLE_CONNECTION_MAX;conidx++)
    {
        pContext = &(app_env.context[conidx]);
        if (pContext->isBdAddrResolvingInProgress)
        {
            break;
        }
    }

    if (conidx < BLE_CONNECTION_MAX)
    {
        pContext->isBdAddrResolvingInProgress = false;
        pContext->isGotSolvedBdAddr = true;

        LOG_I("%s conidx %d isSolvedSuccessfully %d", __func__, conidx, isSolvedSuccessfully);
        if (isSolvedSuccessfully)
        {
#ifdef _BLE_NVDS_
            bool isSuccessful = nv_record_blerec_get_bd_addr_from_irk(app_env.context[conidx].solvedBdAddr, irkUsedForSolving);
            if (isSuccessful)
            {
                LOG_I("[CONNECT]Connected random address's original addr is:");
                DUMP8("%02x ", app_env.context[conidx].solvedBdAddr, GAP_BD_ADDR_LEN);
            }
            else
#endif
            {
                LOG_I("[CONNECT]Resolving of the connected BLE random addr failed.");
            }
        }
        else
        {
            LOG_I("[CONNECT]random resolving failed.");
        }
    }

#if defined(CFG_VOICEPATH)
    ke_task_msg_retrieve(prf_get_task_from_id(TASK_ID_VOICEPATH));
#endif
    ke_task_msg_retrieve(TASK_GAPC);
    ke_task_msg_retrieve(TASK_APP);

    app_ble_start_connectable_adv(BLE_ADVERTISING_INTERVAL);
}

uint8_t app_ble_connection_count(void)
{
    return app_env.conn_cnt;
}

bool app_is_arrive_at_max_ble_connections(void)
{
    LOG_I("connection count %d", app_env.conn_cnt);

#if defined(GFPS_ENABLED) && (BLE_APP_GFPS_VER==FAST_PAIR_REV_2_0)
    return (app_env.conn_cnt >= BLE_CONNECTION_MAX);
#else
    return (app_env.conn_cnt >= 1);
#endif
}

bool app_is_resolving_ble_bd_addr(void)
{
    for (uint32_t index = 0;index < BLE_CONNECTION_MAX;index++)
    {
        if (app_env.context[index].isBdAddrResolvingInProgress)
        {
            return true;
        }
    }

    return false;
}

void app_trigger_ble_service_discovery(uint8_t conidx, uint16_t shl, uint16_t ehl)
{
    struct gattc_send_svc_changed_cmd *cmd = KE_MSG_ALLOC(GATTC_SEND_SVC_CHANGED_CMD,
                                                          KE_BUILD_ID(TASK_GATTC, conidx),
                                                          TASK_APP,
                                                          gattc_send_svc_changed_cmd);
    cmd->operation = GATTC_SVC_CHANGED;
    cmd->svc_shdl = shl;
    cmd->svc_ehdl = ehl;
    ke_msg_send(cmd);
}

void appm_update_adv_data(uint8_t *pAdvData,
                          uint32_t advDataLen,
                          uint8_t *pScanRspData,
                          uint32_t scanRspDataLen)
{
    LOG_I("%s", __func__);
    ASSERT(advDataLen <= BLE_DATA_LEN, "adv data exceed");
    ASSERT(scanRspDataLen <= SCAN_RSP_DATA_LEN, "scan rsp data exceed");

    struct gapm_update_advertise_data_cmd *cmd = KE_MSG_ALLOC(GAPM_UPDATE_ADVERTISE_DATA_CMD,
                                                              TASK_GAPM, TASK_APP,
                                                              gapm_update_advertise_data_cmd);
    cmd->operation = GAPM_UPDATE_ADVERTISE_DATA;
    memcpy(cmd->adv_data, pAdvData, advDataLen);
    cmd->adv_data_len = advDataLen;

    memcpy(cmd->scan_rsp_data, pScanRspData, scanRspDataLen);
    cmd->scan_rsp_data_len = scanRspDataLen;

    ke_msg_send(cmd);
}

__attribute__((weak)) void app_ble_connected_evt_handler(uint8_t conidx, const uint8_t* pPeerBdAddress)
{

}

__attribute__((weak)) void app_ble_disconnected_evt_handler(uint8_t conidx)
{

}

__attribute__((weak)) void app_advertising_stopped(void)
{

}

__attribute__((weak)) void app_advertising_started(void)
{

}

__attribute__((weak)) void app_connecting_started(void)
{

}

__attribute__((weak)) void app_scanning_stopped(void)
{

}

__attribute__((weak)) void app_connecting_stopped(void)
{

}


__attribute__((weak)) void app_scanning_started(void)
{

}

__attribute__((weak)) void app_ble_system_ready(void)
{

}

__attribute__((weak)) void app_adv_reported_scanned(struct gapm_adv_report_ind* ptInd)
{

}

uint8_t* appm_get_current_ble_addr(void)
{
#ifdef BLE_USE_RPA
    return (uint8_t *)gapm_get_bdaddr();
#else
    return ble_addr;
#endif
}

#define IS_WORKAROUND_SAME_BT_BLE_ADDR_FOR_RPA_CASEx
uint8_t* appm_get_local_identity_ble_addr(void)
{
#if defined(BLE_USE_RPA)&&defined(IS_WORKAROUND_SAME_BT_BLE_ADDR_FOR_RPA_CASE)
    return (uint8_t *)gapm_get_bdaddr(); 
#else
    return ble_addr;
#endif
}

// bit mask of the existing conn param modes
static uint32_t existingBleConnParamModes[BLE_CONNECTION_MAX] = {0};

static BLE_CONN_PARAM_CONFIG_T ble_conn_param_config[] =
{
    // default value: for the case of BLE just connected and the BT idle state
    {BLE_CONN_PARAM_MODE_DEFAULT, BLE_CONN_PARAM_PRIORITY_NORMAL, 24},

    {BLE_CONN_PARAM_MODE_AI_STREAM_ON, BLE_CONN_PARAM_PRIORITY_ABOVE_NORMAL1, 24},

    //{BLE_CONN_PARAM_MODE_A2DP_ON, BLE_CONN_PARAM_PRIORITY_ABOVE_NORMAL0, 36},

    {BLE_CONN_PARAM_MODE_HFP_ON, BLE_CONN_PARAM_PRIORITY_ABOVE_NORMAL2, 36},

    {BLE_CONN_PARAM_MODE_OTA, BLE_CONN_PARAM_PRIORITY_HIGH, 12},

    {BLE_CONN_PARAM_MODE_OTA_SLOWER, BLE_CONN_PARAM_PRIORITY_HIGH, 20},
        
    {BLE_CONN_PARAM_MODE_SNOOP_EXCHANGE, BLE_CONN_PARAM_PRIORITY_HIGH, 8},

    // TODO: add mode cases if needed
};

void app_ble_reset_conn_param_mode(uint8_t conidx)
{
    uint32_t lock = int_lock_global();
    existingBleConnParamModes[conidx] = 0;
    int_unlock_global(lock);
}

void app_ble_update_conn_param_mode(BLE_CONN_PARAM_MODE_E mode, bool isEnable)
{
    for (uint8_t index = 0;index < BLE_CONNECTION_MAX;index++)
    {        
        if (BLE_CONNECTED == app_env.context[index].connectStatus)
        {
            app_ble_update_conn_param_mode_of_specific_connection(
                index, mode, isEnable);
        }
    }
}

bool app_ble_is_parameter_mode_enabled(uint8_t conidx, BLE_CONN_PARAM_MODE_E mode)
{
    bool isEnabled = false;
    uint32_t lock = int_lock_global();
    isEnabled = existingBleConnParamModes[conidx]&(1 << mode);
    int_unlock_global(lock);

    return isEnabled;
}

void app_ble_parameter_mode_clear(uint8_t conidx, BLE_CONN_PARAM_MODE_E mode)
{
    uint32_t lock = int_lock_global();
    existingBleConnParamModes[conidx] &= (~(1 << mode));
    int_unlock_global(lock);
}

void app_ble_update_conn_param_mode_of_specific_connection(uint8_t conidx,
                                                           BLE_CONN_PARAM_MODE_E mode,
                                                           bool isEnable)
{
    ASSERT(mode < BLE_CONN_PARAM_MODE_NUM, "Wrong ble conn param mode %d!", mode);

    uint32_t lock = int_lock_global();

    // locate the conn param mode
    BLE_CONN_PARAM_CONFIG_T *pConfig = NULL;
    uint8_t index;

    for (index = 0;
         index < sizeof(ble_conn_param_config) / sizeof(BLE_CONN_PARAM_CONFIG_T);
         index++)
    {
        if (mode == ble_conn_param_config[index].ble_conn_param_mode)
        {
            pConfig = &ble_conn_param_config[index];
            break;
        }
    }

    if (NULL == pConfig)
    {
        int_unlock_global(lock);
        LOG_W("conn param mode %d not defined!", mode);
        return;
    }

    if (isEnable)
    {
        if (0 == existingBleConnParamModes[conidx])
        {
            // no other params existing, just configure this one
            existingBleConnParamModes[conidx] = 1 << mode;
        }
        else
        {
            // already existing, directly return
            if (existingBleConnParamModes[conidx]&(1 << mode))
            {
                int_unlock_global(lock);
                return;
            }
            else
            {
                // update the bit-mask 
                existingBleConnParamModes[conidx] |= (1 << mode);

                // not existing yet, need to go throuth the existing params to see whether
                // we need to update the param
                for (index = 0;
                    index < sizeof(ble_conn_param_config)/sizeof(BLE_CONN_PARAM_CONFIG_T);
                    index++)
                {
                    if ((( uint32_t )1 << ( uint8_t )ble_conn_param_config[index].ble_conn_param_mode) &
                        existingBleConnParamModes[conidx])
                    {
                        if (ble_conn_param_config[index].priority > pConfig->priority)
                        {
                            // one of the exiting param has higher priority than this one,
                            // so do nothing but update the bit-mask
                            int_unlock_global(lock);
                            return;
                        }
                    }
                }

                // no higher priority conn param existing, so we need to apply this one
            }
        }
    }
    else
    {
        if (0 == existingBleConnParamModes[conidx])
        {
            // no other params existing, just return
            int_unlock_global(lock);
            return;
        }
        else 
        {
            // doesn't exist, directly return
            if (!(existingBleConnParamModes[conidx]&(1 << mode)))
            {
                int_unlock_global(lock);
                return;
            }
            else
            {
                // update the bit-mask 
                existingBleConnParamModes[conidx] &= (~(1 << mode));

                if (0 == existingBleConnParamModes[conidx])
                {
                    int_unlock_global(lock);
                    return;
                }

                pConfig = NULL;

                // existing, need to apply for the highest priority conn param
                for (index = 0;
                     index < sizeof(ble_conn_param_config) / sizeof(BLE_CONN_PARAM_CONFIG_T);
                     index++)
                {
                    if ((( uint32_t )1 << ( uint8_t )ble_conn_param_config[index].ble_conn_param_mode) &
                        existingBleConnParamModes[conidx])
                    {
                        if (NULL != pConfig)
                        {
                            if (ble_conn_param_config[index].priority > pConfig->priority)
                            {
                                pConfig = &ble_conn_param_config[index];
                            }
                        }
                        else
                        {
                            pConfig = &ble_conn_param_config[index];
                        }
                    }
                }
            }
        }
    }

    int_unlock_global(lock);

    // if we can arrive here, it means we have got one config to apply
    ASSERT(NULL != pConfig, "It's strange that config pointer is still NULL.");

    APP_BLE_CONN_CONTEXT_T* pContext = &(app_env.context[conidx]);

    if (pContext->connInterval != pConfig->conn_param_interval)
    {
        l2cap_update_param(conidx, pConfig->conn_param_interval*10/8,
                            pConfig->conn_param_interval*10/8,
                            BLE_CONN_PARAM_SUPERVISE_TIMEOUT_MS,
                            BLE_CONN_PARAM_SLAVE_LATENCY_CNT);
                            
        LOG_I("try to update conn interval to %d", pConfig->conn_param_interval);
    }

    LOG_I("conn param mode of conidx %d switched to:0x%x", 
        conidx, existingBleConnParamModes[conidx]);

}

void app_ble_save_negotiated_conn_param(uint8_t conidx, APP_BLE_NEGOTIATED_CONN_PARAM_T* pConnParam)
{
    if (conidx < BLE_CONNECTION_MAX)
    {
        negotiatedBleConnParam[conidx] = *pConnParam;
    }
}

bool app_ble_get_connection_interval(uint8_t conidx,  APP_BLE_NEGOTIATED_CONN_PARAM_T* pConnParam)
{
    if ((conidx < BLE_CONNECTION_MAX) &&
        (BLE_CONNECTED == app_env.context[conidx].connectStatus))
    {
        *pConnParam = negotiatedBleConnParam[conidx];
        return true;
    }
    else
    {
        return false;
    }
}

#if GFPS_ENABLED
uint8_t delay_update_conidx = BLE_INVALID_CONNECTION_INDEX;
#define FP_DELAY_UPDATE_BLE_CONN_PARAM_TIMER_VALUE      (10000)
osTimerId fp_update_ble_param_timer = NULL;
static void fp_update_ble_connect_param_timer_handler(void const *param);
osTimerDef (FP_UPDATE_BLE_CONNECT_PARAM_TIMER, (void (*)(void const *))fp_update_ble_connect_param_timer_handler);
extern uint8_t is_sco_mode (void);
static void fp_update_ble_connect_param_timer_handler(void const *param)
{
    LOG_I("fp_update_ble_connect_param_timer_handler");
    for (uint8_t index = 0;index < BLE_CONNECTION_MAX;index++)
    {
        if ((BLE_CONNECTED == app_env.context[index].connectStatus) &&
            (index == delay_update_conidx))
        {
            LOG_I("update connection interval of conidx %d", delay_update_conidx);

            if (is_sco_mode())
            {
                app_ble_update_conn_param_mode_of_specific_connection(delay_update_conidx, BLE_CONN_PARAM_MODE_HFP_ON, true);
            }
            else
            {
                app_ble_update_conn_param_mode_of_specific_connection(delay_update_conidx, BLE_CONN_PARAM_MODE_DEFAULT, true);
            }
            break;
        }
    }
    delay_update_conidx = BLE_INVALID_CONNECTION_INDEX;
}

void fp_update_ble_connect_param_start(uint8_t ble_conidx)
{
    if (fp_update_ble_param_timer == NULL)
    {
        fp_update_ble_param_timer = osTimerCreate(osTimer(FP_UPDATE_BLE_CONNECT_PARAM_TIMER), osTimerOnce, NULL);
        return;
    }

    delay_update_conidx = ble_conidx;
    if (fp_update_ble_param_timer)
        osTimerStart(fp_update_ble_param_timer, FP_DELAY_UPDATE_BLE_CONN_PARAM_TIMER_VALUE);
}

void fp_update_ble_connect_param_stop(uint8_t ble_conidx)
{
    if (delay_update_conidx == ble_conidx)
    {
        if (fp_update_ble_param_timer)
            osTimerStop(fp_update_ble_param_timer);
        delay_update_conidx = BLE_INVALID_CONNECTION_INDEX;
    }
}
#endif

#endif //(BLE_APP_PRESENT)
/// @} APP
