LE Peripheral Privacy

LE Peripheral Privacy示例工程用于展示Privacy管理模块的使用。Peripheral privacy示例工程基于Peripheral示例工程实现一个Privacy管理模块。

更多关于Peripheral role的信息可参考 LE Peripheral

更多关于Privacy管理模块的信息可参考 代码介绍 中的 Privacy管理模块

环境需求

该示例支持以下开发工具包:

环境需求

Hardware Platforms

Board Name

RTL87x2G HDK

RTL87x2G EVB

为快速搭建起开发环境,可参考 快速入门 中提供的详细指导。

硬件连线

请参考 快速入门 中的 EVB接口和模块

该示例工程需要支持用户命令接口。连线请参考 User Command Interface 中的 Data UART连接

配置

配置选项

该示例工程全部配置选项在 samples\bluetooth\ble_peripheral_privacy\src\app_flags.h 中, 开发者可以根据实际需求配置。

/** @brief  Configure APP LE link number */
#define APP_MAX_LINKS  1
/** @brief  User command: 0-Close user command, 1-Open user command */
#define USER_CMD_EN    1

/** @brief  Configure Privacy1.2 feature: 0-Closed, 1-Open */
#define APP_PRIVACY_EN 1

#if APP_PRIVACY_EN
/** @brief  Configure the authentication requirement of simple_ble_service.c */
#define SIMP_SRV_AUTHEN_EN 1
#endif
Privacy配置

所有privacy相关的代码用宏 APP_PRIVACY_EN 隔开。

Service的Security Requirement

所有service security相关的代码用宏 SIMP_SRV_AUTHEN_EN 隔开。如果开发者想使用prrivacy,就要打开security。更多信息请参考 代码介绍 中的 Service的Security要求

生成系统配置文件

开发者可以通过MP Tool配置以下项目:

MP Tool配置

Configurable Item

Value

LE Slave Link Num

APP_MAX_LINKS

更多有关于MP Tool配置的信息请参考 快速入门 中的 生成System Config File

编译和下载

该示例可以在SDK文件夹中找到:

Project file: samples\bluetooth\ble_peripheral_privacy\proj\rtl87x2g\mdk

Project file: samples\bluetooth\ble_peripheral_privacy\proj\rtl87x2g\gcc

编译和运行该示例请遵循以下步骤:

  1. 打开项目文件。

  2. 要编译目标文件,请参考 快速入门编译APP Image 中列出的步骤。

  3. 编译成功后,目录 samples\bluetooth\ble_peripheral_privacy\proj\rtl87x2g\mdk\bin 下会生成app bin文件 app_MP_sdk_xxx.bin

  4. 要下载app bin到 EVB,请参考 快速入门MP Tool 下载 中列出的步骤。

  5. 在EVB上按下 reset 键,程序将开始运行。

测试验证

将示例工程烧录到EVB后,开发者可以使用一个装有 LightBlue 之类LE应用的手机与之对测。

准备开发板

准备一块开发板命名为 DUT,开发者可以使用 Debug AnalyzerLog验证

准备步骤

使用MP Tool将 DUT 地址设为[00:11:22:33:44:80],然后编译LE Peripheral privacy示例工程,并下载images到 DUT。 开发者可以在电脑的串口辅助工具端输入用户命令,请参考 User Command Interface 中的 如何使用命令

更多关于如何修改 蓝牙地址 的细节请查阅 快速入门 中的 生成System Config File

测试步骤

  1. DUT 上的 reset 键,开发者可以在电脑的串口辅助工具端输入用户命令。

  2. 在iOS设备上打开 LightBlue,搜索并连接 DUT,如图所示:

    ../../../../../_images/test_with_ios.png

    与iOS设备测试

    开发者可以在手机端与peripheral设备进行扫描,连接,配对,查服务,删除绑定信息,断线等交互。

DUT 也可以使用以下用户命令主动断开连线及清除绑定信息:

DUT User Command

Description

DUT Log

disc 0

DUT 断开连线

Serial port assistant tool shows:

Disconnect conn_id 0

bondclear

清除 DUT 端的绑定信息

Debug Analyzer shows:

[GAP] !**le_clear_all_keys

代码介绍

本章将按照以下几个部分进行介绍:

源码路径

  • 工程目录: samples\bluetooth\ble_peripheral_privacy\proj

  • 源码目录: samples\bluetooth\ble_peripheral_privacy\src

LE peripheral privacy示例工程中的源文件当前被分为几个组,如下所示。

└── Project: peripheral_privacy
    └── secure_only_app
        └── Device                   includes startup code
        ├── CMSE Library             Non-secure callable library
        ├── Lib                      includes all binary symbol files that user application is built on
            ├── ROM_NS.lib
            └── lowerstack.lib
            └── rtl87x2g_sdk.lib
            └── gap_utils.lib
        ├── peripheral               includes all peripheral drivers and module code used by the application
        ├── Profile                  includes LE profiles or services used by the sample application
        └── APP                      includes the ble_peripheral_privacy user application implementation
            ├── app_task.c
            ├── main.c
            ├── peripheral_privacy_app.c
            ├── user_cmd.c
            ├── privacy_mgnt.c
            ├── data_uart.c
            └── user_cmd_parse.c

示例工程使用与 bt_host_0_0 匹配的默认GAP LIB,请参阅文档 Bluetooth Host Image 中的 GAP LIB的使用方法 以获取更多信息。

Bluetooth Host介绍

示例工程默认使用Bluetooth Host image版本 bt_host_0_0,更多信息可参阅 Bluetooth Host Image

关于Bluetooth Host所支持的蓝牙功能的详细信息,请参阅文件 bin\rtl87x2g\bt_host_image\bt_host_0_0\bt_host_config.h

初始化

当EVB启动并且芯片复位时, main() 函数将被调用,它执行以下初始化函数:

int main(void)
{
   board_init();
   le_gap_init(APP_MAX_LINKS);
   gap_lib_init();
   app_le_gap_init();
   app_le_profile_init();
   pwr_mgr_init();
   task_init();
   os_sched_start();

   return 0;
 }
void app_le_gap_init(void)
{
   /* Device name and device appearance */
   ......


   /* Advertising parameters */
   ......

   /* GAP Bond Manager parameters */
   ......

   /* Set device name and device appearance */
   le_set_gap_param(GAP_PARAM_DEVICE_NAME, GAP_DEVICE_NAME_LEN, device_name);
   le_set_gap_param(GAP_PARAM_APPEARANCE, sizeof(appearance), &appearance);
   le_set_gap_param(GAP_PARAM_SLAVE_INIT_GATT_MTU_REQ, sizeof(slave_init_mtu_req),
                     &slave_init_mtu_req);

   /* Set advertising parameters */
   le_adv_set_param(GAP_PARAM_ADV_EVENT_TYPE, sizeof(adv_evt_type), &adv_evt_type);
   le_adv_set_param(GAP_PARAM_ADV_DIRECT_ADDR_TYPE, sizeof(adv_direct_type), &adv_direct_type);
   le_adv_set_param(GAP_PARAM_ADV_DIRECT_ADDR, sizeof(adv_direct_addr), adv_direct_addr);
   le_adv_set_param(GAP_PARAM_ADV_CHANNEL_MAP, sizeof(adv_chann_map), &adv_chann_map);
   le_adv_set_param(GAP_PARAM_ADV_FILTER_POLICY, sizeof(adv_filter_policy), &adv_filter_policy);
   le_adv_set_param(GAP_PARAM_ADV_INTERVAL_MIN, sizeof(adv_int_min), &adv_int_min);
   le_adv_set_param(GAP_PARAM_ADV_INTERVAL_MAX, sizeof(adv_int_max), &adv_int_max);
   le_adv_set_param(GAP_PARAM_ADV_DATA, sizeof(adv_data), (void *)adv_data);
   le_adv_set_param(GAP_PARAM_SCAN_RSP_DATA, sizeof(scan_rsp_data), (void *)scan_rsp_data);

   /* Setup the GAP Bond Manager */
   gap_set_param(GAP_PARAM_BOND_PAIRING_MODE, sizeof(auth_pair_mode), &auth_pair_mode);
   gap_set_param(GAP_PARAM_BOND_AUTHEN_REQUIREMENTS_FLAGS, sizeof(auth_flags), &auth_flags);
   gap_set_param(GAP_PARAM_BOND_IO_CAPABILITIES, sizeof(auth_io_cap), &auth_io_cap);
   gap_set_param(GAP_PARAM_BOND_OOB_ENABLED, sizeof(auth_oob), &auth_oob);
   le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY, sizeof(auth_fix_passkey), &auth_fix_passkey);
   le_bond_set_param(GAP_PARAM_BOND_FIXED_PASSKEY_ENABLE, sizeof(auth_use_fix_passkey),
                     &auth_use_fix_passkey);
   le_bond_set_param(GAP_PARAM_BOND_SEC_REQ_REQUIREMENT, sizeof(auth_sec_req_flags),
                     &auth_sec_req_flags);

   /* register gap message callback */
   le_register_app_cb(app_gap_callback);
#if APP_PRIVACY_EN
   privacy_init(app_privacy_callback, true);
#endif
}

更多关于LE GAP初始化和启动流程的信息可以查阅 LE Host 中的 GAP参数的初始化

Privacy管理模块

Privacy管理模块的开发是基于头文件 gap_privacy.h 的。 若开发者需要使用Privacy管理模块,可以使用头文件 privacy_mgnt.h 用于开发privacy相关应用。

对于RTL87x2G,Privacy管理模块的文件路径如下:

  • 源文件: samples\bluetooth\ble_peripheral_privacy\src\privacy_mgnt.c

  • 头文件: samples\bluetooth\ble_peripheral_privacy\src\privacy_mgnt.h

开发者需要在工程的 app group 中添加 privacy_mgnt.c 文件,在 Include Paths 中添加头文件路径。

接口

Privacy管理模块的接口都在头文件 privacy_mgnt.h 中定义。

/* privacy_mgnt.h APIs */
typedef void(*P_FUN_PRIVACY_STATE_CB)(T_PRIVACY_CB_TYPE type, T_PRIVACY_CB_DATA cb_data);
void privacy_init(P_FUN_PRIVACY_STATE_CB p_fun, bool whitelist);
T_PRIVACY_STATE privacy_handle_resolv_list(void);
void privacy_handle_bond_modify_msg(T_LE_BOND_MODIFY_TYPE type, T_LE_KEY_ENTRY *p_entry,
                                 bool handle_add);
bool privacy_add_device(T_LE_KEY_ENTRY *p_entry);
T_GAP_CAUSE privacy_set_addr_resolution(bool enable);
T_GAP_CAUSE privacy_read_peer_resolv_addr(T_GAP_REMOTE_ADDR_TYPE peer_address_type,
                                       uint8_t *peer_address);
T_GAP_CAUSE privacy_read_local_resolv_addr(T_GAP_REMOTE_ADDR_TYPE peer_address_type,
                                        uint8_t *peer_address);

Privacy管理模块的使用方法

Privacy管理模块初始化

APP需要调用 privacy_init(),以初始化Privacy管理模块并注册回调函数。

void app_le_gap_init(void)
{
   /* register gap message callback */
   le_register_app_cb(app_gap_callback);
#if APP_PRIVACY_EN
   privacy_init(app_privacy_callback, true);
#endif
}

privacy_init() 的参数 whitelist 用于配置对white list的管理。若whitelist参数为true,当Privacy管理模块修改resolving list时,Privacy管理模块会管理white list。若whitelist参数为false,APP需要管理white list。

回调函数用于处理Privacy管理模块发送的消息。

void app_privacy_callback(T_PRIVACY_CB_TYPE type, T_PRIVACY_CB_DATA cb_data)
{
   APP_PRINT_INFO1("app_privacy_callback: type %d", type);
   switch (type)
   {
   case PRIVACY_STATE_MSGTYPE:
      app_privacy_state = cb_data.privacy_state;
      APP_PRINT_INFO1("PRIVACY_STATE_MSGTYPE: status %d", app_privacy_state);
      break;
   case PRIVACY_RESOLUTION_STATUS_MSGTYPE:
      app_privacy_resolution_state = cb_data.resolution_state;
      APP_PRINT_INFO1("PRIVACY_RESOLUTION_STATUS_MSGTYPE: status %d", app_privacy_resolution_state);
      break;
   default:
      break;
   }
}
Resolving List的管理

Privacy管理模块的关键功能是当绑定信息变化时,用于管理resolving list和white list。white list的管理是可选特性, privacy_init()whitelist 参数用于配置对white list的管理。

当APP处理 GAP_MSG_LE_BOND_MODIFY_INFO 消息时,APP必须调用函数 privacy_handle_bond_modify_msg()

Privacy管理模块将根据绑定信息的修改类型管理resolving list,流程如下:

  • LE_BOND_DELETE:在resolving list删除该设备。

  • LE_BOND_ADD:在resolving list中加入该设备。

  • LE_BOND_CLEAR:清除resolving list。

T_APP_RESULT app_gap_callback(uint8_t cb_type, void *p_cb_data)
{
   T_APP_RESULT result = APP_RESULT_SUCCESS;
   T_LE_CB_DATA *p_data = (T_LE_CB_DATA *)p_cb_data;
   switch (cb_type)
   {
#if APP_PRIVACY_EN
   case GAP_MSG_LE_BOND_MODIFY_INFO:
     APP_PRINT_INFO1("GAP_MSG_LE_BOND_MODIFY_INFO: type 0x%x",
                     p_data->p_le_bond_modify_info->type);
     privacy_handle_bond_modify_msg(p_data->p_le_bond_modify_info->type,
                                    p_data->p_le_bond_modify_info->p_entry, true);
     break;
#endif
   ......
}

当address resolution已启用,在以下场景,不能执行对resolving list的修改流程:

  • 已启用advertising。

  • 已启用scanning。

  • 正在创建连线

当address resolution未启用,resolving list的修改流程可以在任何时候执行。

当GAP设备状态为idle状态时,APP必须调用函数 privacy_handle_resolv_list()privacy_handle_resolv_list() 用于处理挂起的resolving list修改流程。

void app_handle_dev_state_evt(T_GAP_DEV_STATE new_state, uint16_t cause)
{
   APP_PRINT_INFO3("app_handle_dev_state_evt: init state %d, adv state %d, cause 0x%x",
                 new_state.gap_init_state, new_state.gap_adv_state, cause);
#if APP_PRIVACY_EN
   if ((new_state.gap_init_state == GAP_INIT_STATE_STACK_READY)
      && (new_state.gap_adv_state == GAP_ADV_STATE_IDLE)
      && (new_state.gap_conn_state == GAP_CONN_DEV_STATE_IDLE))
   {
     privacy_handle_resolv_list();
   }
#endif
   ......
}

回调类型 PRIVACY_STATE_MSGTYPE 用于通知resolving list修改流程的状态。回调数据为 T_PRIVACY_STATE

typedef enum
{
   PRIVACY_STATE_INIT,  //!< Privacy management module is not initialization.
   PRIVACY_STATE_IDLE,  //!< Idle. No pending resolving list modification procedure.
   PRIVACY_STATE_BUSY   //!< Busy. Resolving list modification procedure is not completed.
} T_PRIVACY_STATE;
Address Resolution

若对端设备使用resolvable private address,APP希望使用white list过滤该对端设备。APP需要调用函数 privacy_set_addr_resolution() 以启用address resolution。

若APP希望与新的设备配对,APP需要调用函数 privacy_set_addr_resolution() 以禁用address resolution。当address resolution已启用,本地设备不能与不属于resolving list的设备建立连线。示例代码如下:

void app_adv_start(void)
{
   uint8_t  adv_evt_type = GAP_ADTYPE_ADV_IND;
   uint8_t  adv_filter_policy = GAP_ADV_FILTER_ANY;
#if APP_PRIVACY_EN
   T_LE_KEY_ENTRY *p_entry;
   p_entry = le_get_high_priority_bond();
   if (p_entry == NULL)
   {
      /* No bonded device, send connectable undirected advertising event without using whitelist*/
      app_work_mode = APP_PAIRABLE_MODE;
      adv_filter_policy = GAP_ADV_FILTER_ANY;
      if (app_privacy_resolution_state == PRIVACY_ADDR_RESOLUTION_ENABLED)
      {
            privacy_set_addr_resolution(false);
      }
   }
   else
   {
      app_work_mode = APP_RECONNECTION_MODE;
      adv_filter_policy = GAP_ADV_FILTER_WHITE_LIST_ALL;
      if (app_privacy_resolution_state == PRIVACY_ADDR_RESOLUTION_DISABLED)
      {
            privacy_set_addr_resolution(true);
      }
   }
#endif
   le_adv_set_param(GAP_PARAM_ADV_EVENT_TYPE, sizeof(adv_evt_type), &adv_evt_type);
   le_adv_set_param(GAP_PARAM_ADV_FILTER_POLICY, sizeof(adv_filter_policy), &adv_filter_policy);
   le_adv_start();
}

函数 privacy_set_addr_resolution() 用于启用Controller中对resolvable private address的解析功能。这将使得Controller收到本地或对端的resolvable private address时,Controller将使用resolving list。

除了以下场景,其它时间均可启用调用该函数:

  • 已启用advertising。

  • 已启用scanning。

  • 正在创建连线。

回调类型 PRIVACY_RESOLUTION_STATUS_MSGTYPE 用于通知address resolution的状态。回调数据为 T_PRIVACY_ADDR_RESOLUTION_STATE

/** @brief Define the privacy address resolution state */
typedef enum
{
   PRIVACY_ADDR_RESOLUTION_DISABLED,
   PRIVACY_ADDR_RESOLUTION_DISABLING,
   PRIVACY_ADDR_RESOLUTION_ENABLING,
   PRIVACY_ADDR_RESOLUTION_ENABLED
} T_PRIVACY_ADDR_RESOLUTION_STATE;

Privacy使用流程

在APP中有两种模式:配对模式和回连模式。处于配对模式的设备可以与任何设备建立连线,必须禁用address resolution和white list filter policy;处于回连模式的设备只能与在resolving list和white list中的设备建立连线。若APP希望过滤使用resolvable private address的设备,则必须调用函数 privacy_set_addr_resolution() 以启用address resolution。

peripheral privacy应用的流程如图所示:

../../../../../_images/Peripheral_Privacy_APP_Flow_Chart.png

Peripheral Privacy APP流程图

Service的Security要求

所有service security相关的代码通过宏定义 SIMP_SRV_AUTHEN_EN 分隔。 若APP希望使用privacy,那么APP需要启用security。 更多信息参见 LE Host 中的 Service的security要求

/* client characteristic configuration */
{
   ATTRIB_FLAG_VALUE_INCL | ATTRIB_FLAG_CCCD_APPL,                 /* flags */
   {                                          /* type_value */
      LO_WORD(GATT_UUID_CHAR_CLIENT_CONFIG),
      HI_WORD(GATT_UUID_CHAR_CLIENT_CONFIG),
      /* NOTE: this value has an instantiation for each client, a write to */
      /* this attribute does not modify this default value:                */
      LO_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT), /* client char. config. bit field */
      HI_WORD(GATT_CLIENT_CHAR_CONFIG_DEFAULT)
   },
   2,                                          /* bValueLen */
   NULL,
#if SIMP_SRV_AUTHEN_EN
   (GATT_PERM_READ_AUTHEN_REQ | GATT_PERM_WRITE_AUTHEN_REQ) /* permissions */
#else
   (GATT_PERM_READ | GATT_PERM_WRITE)          /* permissions */
#endif
},