精华内容
下载资源
问答
  • 此篇文章简单分析一下蓝牙解除配对在协议栈中的工作流程。分析的协议栈版本是Android8.0 协议栈的接口都定义在bluetooth.cc这个文件中: static int remove_bond(const bt_bdaddr_t* bd_addr) { if (is_...

    此篇文章简单分析一下蓝牙解除配对在协议栈中的工作流程。分析的协议栈版本是Android8.0

    协议栈的接口都定义在bluetooth.cc这个文件中:

    static int remove_bond(const bt_bdaddr_t* bd_addr) {
      if (is_restricted_mode() && !btif_storage_is_restricted_device(bd_addr))
        return BT_STATUS_SUCCESS;
      /* sanity check */
      if (interface_ready() == false) return BT_STATUS_NOT_READY;
      return btif_dm_remove_bond(bd_addr);
    }

    这里需要注意一下bt_bdaddr_t 是一个结构体,内部一个元素是数组。

     /** Bluetooth Address */
     typedef struct {
        uint8_t address[6];
     } __attribute__((packed))bt_bdaddr_t;

    进入btif_dm_remove_bond:

    bt_status_t btif_dm_remove_bond(const bt_bdaddr_t* bd_addr) {
      bdstr_t bdstr;
      btif_transfer_context(btif_dm_generic_evt, BTIF_DM_CB_REMOVE_BOND,
                            (char*)bd_addr, sizeof(bt_bdaddr_t), NULL);
      return BT_STATUS_SUCCESS;
    }

    这个函数btif_transfer_context 是将remove bond这件事情交给bt_jni_workqueue_thread来处理。在该线程中执行的函数就是btif_dm_generic_evt

    static void btif_dm_generic_evt(uint16_t event, char* p_param) {
      BTIF_TRACE_EVENT("%s: event=%d", __func__, event);
      switch (event) {
        case BTIF_DM_CB_REMOVE_BOND: {
          btif_dm_cb_remove_bond((bt_bdaddr_t*)p_param);
        } break;
      }
    }

    执行的函数:btif_dm_cb_remove_bond 

    void btif_dm_cb_remove_bond(bt_bdaddr_t* bd_addr) {
        BTA_DmRemoveDevice((uint8_t*)bd_addr->address);
    }

    函数执行到了BTA层面。

    tBTA_STATUS BTA_DmRemoveDevice(BD_ADDR bd_addr) {
      tBTA_DM_API_REMOVE_DEVICE* p_msg =
       (tBTA_DM_API_REMOVE_DEVICE*)osi_calloc(sizeof(tBTA_DM_API_REMOVE_DEVICE));
      p_msg->hdr.event = BTA_DM_API_REMOVE_DEVICE_EVT;
      bdcpy(p_msg->bd_addr, bd_addr);
      bta_sys_sendmsg(p_msg);
      return BTA_SUCCESS;
    }

    这边是发送了一个BTA_DM_API_REMOVE_DEVICE_EVT到另一个线程:bt_workqueue_thread,这个线程是专门处理bt里面的队列的,当队列里面有数据都会在这个线程里面处理。

    通过bta_sys_sendmsg发送的信号都会经过bta_sys_event来处理,bta_sys_event会根据相应的事件路由到相应的处理函数。这里处理这个事件的函数是:

    /*******************************************************************************
     *
     * Function         bta_dm_remove_device
     *
     * Description      Removes device, disconnects ACL link if required.
     ***
     ******************************************************************************/
    void bta_dm_remove_device(tBTA_DM_MSG* p_data) {
      tBTA_DM_API_REMOVE_DEVICE* p_dev = &p_data->remove_dev;//获取消息
      bool continue_delete_other_dev = false;/* If ACL exists for the device in the remove_bond message*/
      bool continue_delete_dev = false;
      uint8_t other_transport = BT_TRANSPORT_INVALID;
    /*首先判断该address 是否存有link*/
      if (BTM_IsAclConnectionUp(p_dev->bd_addr, BT_TRANSPORT_LE) ||
          BTM_IsAclConnectionUp(p_dev->bd_addr, BT_TRANSPORT_BR_EDR)) {
        APPL_TRACE_DEBUG("%s: ACL Up count  %d", __func__,
                         bta_dm_cb.device_list.count);
        continue_delete_dev = false;
        /* Take the link down first, and mark the device for removal when
         * disconnected */
        for (int i = 0; i < bta_dm_cb.device_list.count; i++) {
          if (!bdcmp(bta_dm_cb.device_list.peer_device[i].peer_bdaddr,
                     p_dev->bd_addr)) {
            uint8_t transport = BT_TRANSPORT_BR_EDR;
            transport = bta_dm_cb.device_list.peer_device[i].transport;
            bta_dm_cb.device_list.peer_device[i].conn_state = BTA_DM_UNPAIRING;//设置标志位,在acl link状态改变的函数中会去删除link key
            btm_remove_acl(p_dev->bd_addr, transport);//已经存在link,那么要先删除这条linkbreak;
          }
        }
      } else {
        continue_delete_dev = true;
      }
    ...
      /* Delete the device mentioned in the msg */
      if (continue_delete_dev) bta_dm_process_remove_device(p_dev->bd_addr);//解配的设备没有处于连接状态则执行
    }

    从上面的代码可以看出,核心的地方就两处:

    1. bta_dm_cb.device_list.peer_device[i].conn_state = BTA_DM_UNPAIRING; 标记该设备是要解除配对的,后续会删除其link key
    2. btm_remove_acl 做实际的断开连接的操作。

     现在简单看看btm_remove_acl 的实现:

    tBTM_STATUS btm_remove_acl(BD_ADDR bd_addr, tBT_TRANSPORT transport) {
      uint16_t hci_handle = BTM_GetHCIConnHandle(bd_addr, transport);
      tBTM_STATUS status = BTM_SUCCESS;
    ...
      {
        if (hci_handle != 0xFFFF && p_dev_rec &&
            p_dev_rec->sec_state != BTM_SEC_STATE_DISCONNECTING) {
          btsnd_hcic_disconnect(hci_handle, HCI_ERR_PEER_USER);
        } else
          status = BTM_UNKNOWN_ADDR;
      }
      return status;
    }

    先搜索btm_cb.acl_db 找到该地址对应的ACL link 的控制体,获取该link 的handle,该handle用于标志是哪一条link,然后调用btsnd_hcic_disconnect(hci_handle, HCI_ERR_PEER_USER); 向controller发送断开的命令。

    到这里,在bt_workqueue_thread 线程中发送断开link 的消息是结束了,controller收到信息处理之后,会有断开完成事件上来,这个时候处理该事件的线程依然是bt_workqueue_thread,我们看看具体的处理过程:处理底层的事件的接口实现在btu_hcif.cc中:

    /*******************************************************************************
     *
     * Function         btu_hcif_disconnection_comp_evt
     *
     * Description      Process event HCI_DISCONNECTION_COMP_EVT
     *
     * Returns          void
     *
     ******************************************************************************/
    static void btu_hcif_disconnection_comp_evt(uint8_t* p) {
      uint16_t handle;
      uint8_t reason;
      ++p;
      STREAM_TO_UINT16(handle, p);
      STREAM_TO_UINT8(reason, p);
      handle = HCID_GET_HANDLE(handle);
      l2c_link_hci_disc_comp(handle, reason);
      /* Notify security manager */
      btm_sec_disconnected(handle, reason);
    }

    从函数的注释我们也能看出其是处理HCI_DISCONNECTION_COMP_EVT 这个事件的。从代码流程中,我们能看到代码主要做了两件事:

    1. l2c_link_hci_disc_comp
    2. btm_sec_disconnected,这个函数是用来通知security manager的。

    下面依次看看这两个函数的实现:

    bool l2c_link_hci_disc_comp(uint16_t handle, uint8_t reason) {
      L2CAP_TRACE_EVENT("entry l2c_link_hci_disc_comp libs_liu");
      tL2C_LCB* p_lcb;
      tL2C_CCB* p_ccb;
      bool status = true;
      bool lcb_is_free = true;
      tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR;
      /* See if we have a link control block for the connection */
      p_lcb = l2cu_find_lcb_by_handle(handle);
    ...
     {
        /* Just in case app decides to try again in the callback context */
        p_lcb->link_state = LST_DISCONNECTING;
    
        /* Check for BLE and handle that differently */
        if (p_lcb->transport == BT_TRANSPORT_LE)
          btm_ble_update_link_topology_mask(p_lcb->link_role, false);
        /* Link is disconnected. For all channels, send the event through */
        /* their FSMs. The CCBs should remove themselves from the LCB     */
        for (p_ccb = p_lcb->ccb_queue.p_first_ccb; p_ccb;) {
          tL2C_CCB* pn = p_ccb->p_next_ccb;
          /* Keep connect pending control block (if exists)
           * Possible Race condition when a reconnect occurs
           * on the channel during a disconnect of link. This
           * ccb will be automatically retried after link disconnect
           * arrives
           */
          if (p_ccb != p_lcb->p_pending_ccb) {
            l2c_csm_execute(p_ccb, L2CEVT_LP_DISCONNECT_IND, &reason);
          }
          p_ccb = pn;
        }
        p_lcb->p_pending_ccb = NULL;
    
        /* Release the LCB */
        if (lcb_is_free){ 
        l2cu_release_lcb(p_lcb);}
      }
    ...
      return status;
    }

    上面函数核心的地方 是释放link 相关的结构体l2cu_release_lcb,这个函数主要就是释放一些定时器,内存以及channel结构释放,另外还做一些通知的事情:

          /* If anyone cares, tell him database changed */
          if (btm_cb.p_bl_changed_cb) {
            evt_data.event = BTM_BL_DISCN_EVT;
            evt_data.discn.p_bda = bda;
            evt_data.discn.handle = p->hci_handle;
            evt_data.discn.transport = p->transport;
            (*btm_cb.p_bl_changed_cb)(&evt_data);
          }

    这里需要思考一下,这个btm_cb.p_bl_changed_cb 回调函数是哪里注册。这个函数其实在bta_dm_sys_hw_cback 函数中注册的:

    BTM_RegBusyLevelNotif(bta_dm_bl_change_cback, NULL,
                              BTM_BL_UPDATE_MASK | BTM_BL_ROLE_CHG_MASK);
    //那其实是
    btm_cb.p_bl_changed_cb  = bta_dm_bl_change_cback

    那我们现在看看这个busy level change的实现:从上面也可以看出是把BTM_BL_DISCN_EVT 传入到 bta_dm_bl_change_cback 来执行:

    static void bta_dm_bl_change_cback(tBTM_BL_EVENT_DATA* p_data) {
      tBTA_DM_ACL_CHANGE* p_msg =
          (tBTA_DM_ACL_CHANGE*)osi_malloc(sizeof(tBTA_DM_ACL_CHANGE));
    
      p_msg->event = p_data->event;
      p_msg->is_new = false;
    
      switch (p_msg->event) {
        case BTM_BL_CONN_EVT:
          p_msg->is_new = true;
          bdcpy(p_msg->bd_addr, p_data->conn.p_bda);
          p_msg->transport = p_data->conn.transport;
          p_msg->handle = p_data->conn.handle;
          break;
        case BTM_BL_DISCN_EVT:
          bdcpy(p_msg->bd_addr, p_data->discn.p_bda);
          p_msg->transport = p_data->discn.transport;
          p_msg->handle = p_data->discn.handle;
          break;
        case BTM_BL_UPDATE_EVT:
          p_msg->busy_level = p_data->update.busy_level;
          p_msg->busy_level_flags = p_data->update.busy_level_flags;
          break;
        case BTM_BL_ROLE_CHG_EVT:
          p_msg->new_role = p_data->role_chg.new_role;
          p_msg->hci_status = p_data->role_chg.hci_status;
          bdcpy(p_msg->bd_addr, p_data->role_chg.p_bda);
          break;
        case BTM_BL_COLLISION_EVT:
          bdcpy(p_msg->bd_addr, p_data->conn.p_bda);
          break;
      }
    
      p_msg->hdr.event = BTA_DM_ACL_CHANGE_EVT;
      bta_sys_sendmsg(p_msg);
    }

    从这个函数的实现来看,他是可以处理很多的event的。然后统一发送给bt_workqueue_thread中的另外一个队列中,看看具体的处理流程:

    其实现的函数是在bta_dm_act.cc中,

    void bta_dm_acl_change(tBTA_DM_MSG* p_data) {
      uint8_t i;
      uint8_t* p;
      tBTA_DM_SEC conn;
      bool is_new = p_data->acl_change.is_new;
      BD_ADDR_PTR p_bda = p_data->acl_change.bd_addr;
      bool need_policy_change = false;
      bool issue_unpair_cb = false;
    
      tBTA_DM_PEER_DEVICE* p_dev;
      memset(&conn, 0, sizeof(tBTA_DM_SEC));
    ...
        for (i = 0; i < bta_dm_cb.device_list.count; i++) {
          if (bdcmp(bta_dm_cb.device_list.peer_device[i].peer_bdaddr, p_bda) ||
              bta_dm_cb.device_list.peer_device[i].transport !=
                  p_data->acl_change.transport)
            continue;
        /*找到之后*/
          if (bta_dm_cb.device_list.peer_device[i].conn_state == BTA_DM_UNPAIRING) {//之前已经标记过
            if (BTM_SecDeleteDevice(bta_dm_cb.device_list.peer_device[i].peer_bdaddr))
          BTA_GATTC_Refresh(p_bda);//remove all gatt information issue_unpair_cb
    = true; } conn.link_down.is_removed = bta_dm_cb.device_list.peer_device[i].remove_dev_pending; break; } ... conn.link_down.link_type = p_data->acl_change.transport; ... bdcpy(conn.link_down.bd_addr, p_bda); conn.link_down.status = (uint8_t)btm_get_acl_disc_reason_code(); if (bta_dm_cb.p_sec_cback) { //执行这里的函数 bta_dm_cb.p_sec_cback(BTA_DM_LINK_DOWN_EVT, &conn); if (issue_unpair_cb) bta_dm_cb.p_sec_cback(BTA_DM_DEV_UNPAIRED_EVT, &conn); } bta_dm_adjust_roles(true); }

    上面的回调函数主要执行如下:

    1. 从bta_dm_cb 查找到该地址的结构。
    2. 找到之后判断bta_dm_cb.device_list.peer_device[i].conn_state的状态,如果是BTA_DM_UNPAIRING,则开始删除link key:BTM_SecDeleteDevice
    3. 回调bta_dm_cb.p_sec_cback :BTA_DM_LINK_DOWN_EVT以及BTA_DM_DEV_UNPAIRED_EVT

    前两个容易理解,看看第三点:

    第三点的回调函数在enable 的时候就已经注册好了:bta_dm_cb.p_sec_cback = bte_dm_evt ,我们进一步看看:

    void bte_dm_evt(tBTA_DM_SEC_EVT event, tBTA_DM_SEC* p_data) {
      bt_status_t status = btif_transfer_context(
          btif_dm_upstreams_evt, (uint16_t)event, (char*)p_data,
          sizeof(tBTA_DM_SEC), btif_dm_data_copy);
    }

    发现其实 他实质执行的函数是btif_dm_upstreams_evt ,这个函数处理的event非常的多,这里就列出相关的代码:

    static void btif_dm_upstreams_evt(uint16_t event, char* p_param) {
      tBTA_DM_SEC* p_data = (tBTA_DM_SEC*)p_param;
      tBTA_SERVICE_MASK service_mask;
      uint32_t i;
      bt_bdaddr_t bd_addr;
    
      switch (event) {
    ...
        case BTA_DM_DEV_UNPAIRED_EVT:
          bdcpy(bd_addr.address, p_data->link_down.bd_addr);
          btm_set_bond_type_dev(p_data->link_down.bd_addr, BOND_TYPE_UNKNOWN);
    
    /*special handling for HID devices */
    #if (defined(BTA_HH_INCLUDED) && (BTA_HH_INCLUDED == true))
          btif_hh_remove_device(bd_addr);
    #endif
    #if (defined(BTA_HD_INCLUDED) && (BTA_HD_INCLUDED == TRUE))
          btif_hd_remove_device(bd_addr);
    #endif
          btif_storage_remove_bonded_device(&bd_addr);
          bond_state_changed(BT_STATUS_SUCCESS, &bd_addr, BT_BOND_STATE_NONE);
          break;
    
        case BTA_DM_LINK_DOWN_EVT:
          bdcpy(bd_addr.address, p_data->link_down.bd_addr);
          btm_set_bond_type_dev(p_data->link_down.bd_addr, BOND_TYPE_UNKNOWN);
          btif_av_move_idle(bd_addr);
          BTIF_TRACE_DEBUG(
              "BTA_DM_LINK_DOWN_EVT. Sending BT_ACL_STATE_DISCONNECTED");
          HAL_CBACK(bt_hal_cbacks, acl_state_changed_cb, BT_STATUS_SUCCESS,
                    &bd_addr, BT_ACL_STATE_DISCONNECTED);
          break;
      }
    
      btif_dm_data_free(event, p_data);
    }

    处理的两个事件是BTA_DM_LINK_DOWN_EVT和BTA_DM_DEV_UNPAIRED_EVT 

    其中BTA_DM_LINK_DOWN_EVT 主要是执行acl_state_changed_cb,向上层传导BT_ACL_STATE_DISCONNECTED

    这里主要看一下BTA_DM_DEV_UNPAIRED_EVT  的处理:

    1. btif_hh_remove_device   
    2. btif_storage_remove_bonded_device  删除config中的key 的信息。

    下面看看btif_hh_remove_device

     

    void btif_hh_remove_device(bt_bdaddr_t bd_addr) {
      int i;
      btif_hh_device_t* p_dev;
      btif_hh_added_device_t* p_added_dev;for (i = 0; i < BTIF_HH_MAX_ADDED_DEV; i++) {
        p_added_dev = &btif_hh_cb.added_devices[i];
        if (memcmp(&(p_added_dev->bd_addr), &bd_addr, 6) == 0) {
          BTA_HhRemoveDev(p_added_dev->dev_handle);//remove dev
          btif_storage_remove_hid_info(&(p_added_dev->bd_addr));//删除config中的hid信息
          memset(&(p_added_dev->bd_addr), 0, 6);
          p_added_dev->dev_handle = BTA_HH_INVALID_HANDLE;
          break;
        }
      }
    ... p_dev
    = btif_hh_find_dev_by_bda(&bd_addr);/* need to notify up-layer device is disconnected to avoid state out of sync * with up-layer */ HAL_CBACK(bt_hh_callbacks, connection_state_cb, &(p_dev->bd_addr),//向上层汇报状态 BTHH_CONN_STATE_DISCONNECTED); p_dev->dev_status = BTHH_CONN_STATE_UNKNOWN; p_dev->dev_handle = BTA_HH_INVALID_HANDLE; p_dev->ready_for_data = false; ...if (p_dev->fd >= 0) { bta_hh_co_destroy(p_dev->fd);//销毁相关的结构 p_dev->fd = -1; } }

    上面代码很简单,主要做清理工作:下面具体看看BTA_HhRemoveDev

    void BTA_HhRemoveDev(uint8_t dev_handle) {
      tBTA_HH_MAINT_DEV* p_buf =
          (tBTA_HH_MAINT_DEV*)osi_calloc(sizeof(tBTA_HH_MAINT_DEV));
      p_buf->hdr.event = BTA_HH_API_MAINT_DEV_EVT;
      p_buf->sub_event = BTA_HH_RMV_DEV_EVT;
      p_buf->hdr.layer_specific = (uint16_t)dev_handle;
      bta_sys_sendmsg(p_buf);
    }

    发送了一个BTA_HH_API_MAINT_DEV_EVT的事件到达BTU task,

    void bta_hh_maint_dev_act(tBTA_HH_DEV_CB* p_cb, tBTA_HH_DATA* p_data) {
      tBTA_HH_MAINT_DEV* p_dev_info = &p_data->api_maintdev;
      tBTA_HH_DEV_INFO dev_info;
      uint8_t dev_handle;
    
      dev_info.status = BTA_HH_ERR;
      dev_info.handle = BTA_HH_INVALID_HANDLE;
    
      switch (p_dev_info->sub_event) {
    ...
        case BTA_HH_RMV_DEV_EVT: /* remove device */
          dev_info.handle = (uint8_t)p_dev_info->hdr.layer_specific;
          bdcpy(dev_info.bda, p_cb->addr);
    
          if (p_cb->is_le_device) {//le 设备
            bta_hh_le_remove_dev_bg_conn(p_cb);//移除background 里面的list,让其不要回连
            bta_hh_sm_execute(p_cb, BTA_HH_API_CLOSE_EVT, NULL);
            bta_hh_clean_up_kdev(p_cb);
          } else//bredr 设备
          {
            if (HID_HostRemoveDev(dev_info.handle) == HID_SUCCESS) {
              dev_info.status = BTA_HH_OK;
              /* remove from known device list in BTA */
              bta_hh_clean_up_kdev(p_cb);
            }
          }
          break;
      }
      (*bta_hh_cb.p_cback)(p_dev_info->sub_event, (tBTA_HH*)&dev_info);
    }

    这里先讲一下bta_hh_cb.p_cback,也是在 BTA_HhEnable(BTUI_HH_SECURITY, bte_hh_evt); 时候注册的,那么也就是说bta_hh_cb.p_cback = bte_hh_evt

    void bte_hh_evt(tBTA_HH_EVT event, tBTA_HH* p_data) {
      bt_status_t status;
      int param_len = 0;
    
      if (BTA_HH_ENABLE_EVT == event)
        param_len = sizeof(tBTA_HH_STATUS);
      else if (BTA_HH_OPEN_EVT == event)
        param_len = sizeof(tBTA_HH_CONN);
      else if (BTA_HH_DISABLE_EVT == event)
        param_len = sizeof(tBTA_HH_STATUS);
      else if (BTA_HH_CLOSE_EVT == event)
        param_len = sizeof(tBTA_HH_CBDATA);
      else if (BTA_HH_GET_DSCP_EVT == event)
        param_len = sizeof(tBTA_HH_DEV_DSCP_INFO);
      else if ((BTA_HH_GET_PROTO_EVT == event) || (BTA_HH_GET_RPT_EVT == event) ||
               (BTA_HH_GET_IDLE_EVT == event))
        param_len = sizeof(tBTA_HH_HSDATA);
      else if ((BTA_HH_SET_PROTO_EVT == event) || (BTA_HH_SET_RPT_EVT == event) ||
               (BTA_HH_VC_UNPLUG_EVT == event) || (BTA_HH_SET_IDLE_EVT == event))
        param_len = sizeof(tBTA_HH_CBDATA);
      else if ((BTA_HH_ADD_DEV_EVT == event) || (BTA_HH_RMV_DEV_EVT == event))
        param_len = sizeof(tBTA_HH_DEV_INFO);
      else if (BTA_HH_API_ERR_EVT == event)
        param_len = 0;
      /* switch context to btif task context (copy full union size for convenience)
       */
      status = btif_transfer_context(btif_hh_upstreams_evt, (uint16_t)event,
                                     (char*)p_data, param_len, NULL);
    
      /* catch any failed context transfers */
      ASSERTC(status == BT_STATUS_SUCCESS, "context transfer failed", status);
    }

    其本质还是transfer 到btif 线程,执行btif_hh_upstreams_evt 函数,这边追踪了一下该事件的处理,只是打印了一些log:

        case BTA_HH_RMV_DEV_EVT:
          BTIF_TRACE_DEBUG("BTA_HH_RMV_DEV_EVT: status = %d, handle = %d",
                           p_data->dev_info.status, p_data->dev_info.handle);
          BTIF_TRACE_DEBUG("BTA_HH_RMV_DEV_EVT:bda = %02x:%02x:%02x:%02x:%02x:%02x",
                           p_data->dev_info.bda[0], p_data->dev_info.bda[1],
                           p_data->dev_info.bda[2], p_data->dev_info.bda[3],
                           p_data->dev_info.bda[4], p_data->dev_info.bda[5]);
          break;

    现在着重看一下bta_hh_le_remove_dev_bg_conn(p_cb) 的处理

    void bta_hh_le_remove_dev_bg_conn(tBTA_HH_DEV_CB* p_dev_cb) {
      if (p_dev_cb->in_bg_conn) {
        p_dev_cb->in_bg_conn = false;
        BTA_GATTC_CancelOpen(bta_hh_cb.gatt_if, p_dev_cb->addr, false);
      }
    
      /* deregister all notifications */
      bta_hh_le_deregister_input_notif(p_dev_cb);
    }
    void BTA_GATTC_CancelOpen(tBTA_GATTC_IF client_if, BD_ADDR remote_bda,
                              bool is_direct) {
      tBTA_GATTC_API_CANCEL_OPEN* p_buf = (tBTA_GATTC_API_CANCEL_OPEN*)osi_malloc(
          sizeof(tBTA_GATTC_API_CANCEL_OPEN));
    
      p_buf->hdr.event = BTA_GATTC_API_CANCEL_OPEN_EVT;
      p_buf->client_if = client_if;
      p_buf->is_direct = is_direct;
      memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN);
    
      bta_sys_sendmsg(p_buf);
    }
    void bta_gattc_process_api_open_cancel(tBTA_GATTC_DATA* p_msg) {
      uint16_t event = ((BT_HDR*)p_msg)->event;
      tBTA_GATTC_CLCB* p_clcb = NULL;
      tBTA_GATTC_RCB* p_clreg;
      tBTA_GATTC cb_data;
    
      if (p_msg->api_cancel_conn.is_direct) {
    ...
      } else {
        bta_gattc_cancel_bk_conn(&p_msg->api_cancel_conn);
      }
    }
    void bta_gattc_cancel_bk_conn(tBTA_GATTC_API_CANCEL_OPEN* p_data) {
      tBTA_GATTC_RCB* p_clreg;
      tBTA_GATTC cb_data;
      cb_data.status = BTA_GATT_ERROR;
    
      /* remove the device from the bg connection mask */
      if (bta_gattc_mark_bg_conn(p_data->client_if, p_data->remote_bda, false)) {
        if (GATT_CancelConnect(p_data->client_if, p_data->remote_bda, false)) {
          cb_data.status = BTA_GATT_OK;
        } else {
          APPL_TRACE_ERROR("bta_gattc_cancel_bk_conn failed");
        }
      }
      p_clreg = bta_gattc_cl_get_regcb(p_data->client_if);
    
      if (p_clreg && p_clreg->p_cback) {
        (*p_clreg->p_cback)(BTA_GATTC_CANCEL_OPEN_EVT, &cb_data);
      }
    }
    bool GATT_CancelConnect(tGATT_IF gatt_if, BD_ADDR bd_addr, bool is_direct) {
      tGATT_REG* p_reg;
      tGATT_TCB* p_tcb;
      bool status = true;
      tGATT_IF temp_gatt_if;
      uint8_t start_idx, found_idx;
    
        if (!gatt_if) 
            ...
            else {
          status = gatt_remove_bg_dev_for_app(gatt_if, bd_addr);
        }
      return status;
    }
    bool gatt_remove_bg_dev_for_app(tGATT_IF gatt_if, BD_ADDR bd_addr) {
      tGATT_TCB* p_tcb = gatt_find_tcb_by_addr(bd_addr, BT_TRANSPORT_LE);
      bool status;
    
      if (p_tcb) gatt_update_app_use_link_flag(gatt_if, p_tcb, false, false);
      status = gatt_update_auto_connect_dev(gatt_if, false, bd_addr);
      return status;
    }
    bool gatt_update_auto_connect_dev(tGATT_IF gatt_if, bool add, BD_ADDR bd_addr) {
    ...
      if (add) {
        ret = gatt_add_bg_dev_list(p_reg, bd_addr);
        if (ret && p_tcb != NULL) {
          /* if a connected device, update the link holding number */
          gatt_update_app_use_link_flag(gatt_if, p_tcb, true, true);
        }
      } else {
        ret = gatt_remove_bg_dev_from_list(p_reg, bd_addr);
      }
      return ret;
    }
    bool gatt_remove_bg_dev_from_list(tGATT_REG* p_reg, BD_ADDR bd_addr) {
      tGATT_IF gatt_if = p_reg->gatt_if;
      tGATT_BG_CONN_DEV* p_dev = NULL;
      uint8_t i, j;
      bool ret = false;
    
      p_dev = gatt_find_bg_dev(bd_addr);
      if (p_dev == NULL) {
        return ret;
      }
    
      for (i = 0; i < GATT_MAX_APPS && (p_dev->gatt_if[i] > 0); i++) {
        if (p_dev->gatt_if[i] == gatt_if) {
          p_dev->gatt_if[i] = 0;
          /* move all element behind one forward */
          for (j = i + 1; j < GATT_MAX_APPS; j++)
            p_dev->gatt_if[j - 1] = p_dev->gatt_if[j];
    
          if (p_dev->gatt_if[0] == 0){//只有设备全部都断开了才会去做BTM_BleUpdateBgConnDev
            ret = BTM_BleUpdateBgConnDev(false, p_dev->remote_bda);
              }
          else
            ret = true;
    
          break;
        }
      }
    
      if (i != GATT_MAX_APPS && p_dev->gatt_if[0] == 0) {
        memset(p_dev, 0, sizeof(tGATT_BG_CONN_DEV));
      }
    
      return ret;
    }
    bool BTM_BleUpdateBgConnDev(bool add_remove, BD_ADDR remote_bda) {
      return btm_update_dev_to_white_list(add_remove, remote_bda);
    }
    bool btm_update_dev_to_white_list(bool to_add, BD_ADDR bd_addr) {
      tBTM_BLE_CB* p_cb = &btm_cb.ble_ctr_cb;
    
      if (to_add && p_cb->white_list_avail_size == 0) {
        BTM_TRACE_ERROR("%s Whitelist full, unable to add device", __func__);
        return false;
      }
      if (to_add)
        background_connection_add((bt_bdaddr_t*)bd_addr);
      else{
        background_connection_remove((bt_bdaddr_t*)bd_addr);
      }
      btm_suspend_wl_activity(p_cb->wl_state);
      btm_enq_wl_dev_operation(to_add, bd_addr);
      btm_resume_wl_activity(p_cb->wl_state);
      return true;
    }

    删除 里面的值: background_connection_remove

    static void btm_resume_wl_activity(tBTM_BLE_WL_STATE wl_state) {
      btm_ble_resume_bg_conn();
      if (wl_state & BTM_BLE_WL_ADV) {
        btm_ble_start_adv();
      }
    }
    bool btm_ble_resume_bg_conn(void) {
      tBTM_BLE_CB* p_cb = &btm_cb.ble_ctr_cb;
      if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) {
        return btm_ble_start_auto_conn(true);//如果background 里面还存在设备,那么就会回连
      }
      return false;
    }

    这个解除绑定的流程 暂时是分析到这里。

     

     

    转载于:https://www.cnblogs.com/libs-liu/p/9193864.html

    展开全文
  • GAP Bond管理和LE安全连接 GAP Bond Manager是一个可配置的模块,使用Bond manager后应用程序可以减少大部分安全机制。下表列出了术语。 术语 描述 配对(Pairing) 交换密钥的过程 加密(Encryption...
    公司主页 文档归类 淘宝

    GAP Bond管理和LE安全连接

    GAP Bond Manager是一个可配置的模块,使用Bond manager后应用程序可以减少大部分安全机制。下表列出了术语。

    术语 描述
    配对(Pairing) 交换密钥的过程
    加密(Encryption) 0x02
    认证(Authentication) 使用中间人(MITM)保护完成的配对
    Bonding 将密钥存储在非易失性存储器中
    授权(Authorization) 除了认证之外,还需要额外的应用级密钥交换
    OOB(Out of band) 密钥不是通过空中交换,而是通过串行端口或NFC等其他来源进行交换。这也提供了MITM保护。
    MITM(Man in the Middle protection) 这可以防止攻击者收听通过空中传输的密钥来破坏加密。
    只是工作(Just work) 配对方法,其中密钥在没有MITM的情况下通过空中传送

    由于蓝牙是无线空中传输,所以数据很容易被窃听,于是蓝牙在数据发送和接收过程中使用了AES加密,关于AES算法超出了本文的范围,大家只需要了解该算法是通过一组密钥(假设密钥是123456)。然后接收方和发送方都使用这个密钥对数据进行加密和解密,传输在空中的数据都是经过加密了的数据,这样安全性就大大提高。AES算法虽然目前没有暴力破解的方法,但是攻击者一旦知道了该密钥就能轻松获取数据,所以密钥的交换一般不通过空中传输,所以蓝牙设计者设计了两个解决方案:OOB和MITM,MITM其实就是在一端生成密钥并显示出来,另一端手动的输入密钥,这样完成密钥交换。OOB和MITM类似,但是密钥的交换通过NFC或串口等形式进行交换。这样攻击者就很难获取密钥进行破解了。当密钥完成交换之后会有Bonding过程,主要目的是保存密钥,下次连接的时候就不需要输入密钥从而直接连接。

    所以在程序中使用GAPBondMgr实现,过程如下:

    • 配对过程通过选择配对模式中描述的以下方法交换密钥。
    • 使用步骤1的密钥加密链接。
    • 绑定过程将密钥存储在安全闪存(SNV)中。
    • 重新连接时,使用存储在SNV中的密钥来加密链接。

    执行所有这些步骤是不必要的。例如,两个设备可以选择配对而不绑定(Bond)。

    选择配对模式

    有四种类型的配对模型,每种模型都在GAPBondMgr中详细描述,用于不同配对模式:

    • Just Work(安全连接或LE遗留)
    • PassKey Entry(安全连接或LE遗留)
    • Numeric Comparison(安全连接)
    • OOB (Out of Band)(安全连接或LE遗留)

    其中Just Work 不能防止攻击者攻击,用于配对没有显示没有输入的设备,主动发起连接即可配对,用户看不到配对过程,例如连接蓝牙耳机。

    PassKey Enter 配对目标输入一个在本地设备上显示的6位数字,输入正确即可配对,并可以防止攻击。使用场景:一端设备可以显示,另一端设备可以输入。

    Numeric Comparison 配对双方都显示一个6位的数字,由用户来核对数字是否一致,并输入Yes/No,两端Yes表示一致即可配对,可以防止中间人攻击。使用场景:两端设备可以弹出6位十进制数,并且有yes和no按钮。

    Out of Band 一般很少使用,通过串口或NFC交换密钥,例如NFC蓝牙音箱。

    所以开发者需要根据自己设备IO输入输出能力选择合适的配对模式。下面给出一些配置参考。

    如果两个设备都支持安全连接,使用下图决定下一步配置。

    如果至少有一个设备不支持安全连接,使用下图决定下一步。

    下表根据IO Capabilities再对模式进行细分

    使用GAPBondMgr

    本节介绍应用程序必须做什么来配置,启动和使用GAPBondMgr。GAPRole处理一些GAPBondMgr功能。GAPBondMgr被定义在gapbondmgr.c 和gapbondmgr.h。BLE Stack API参考(GAPBondMgr部分)描述了完整的API,包括命令,可配置参数,事件和回调。

    使用GAPBondMgr模块的一般步骤如下:

    1. 配置堆栈以包括GAPBondMgr功能,如果需要安全连接。在堆栈项目build_config.opt中定义以下内容:-DGAP_BOND_MGR

    2. 堆栈还必须配置为使用1或2个SNV页面,通过在堆栈项目中定义OSAL_SNV=1或OSAL_SNV=2作为预处理器定义的符号。

    3. 如果使用安全连接,则PDU大小必须大于等于69.这可以通过在应用程序项目中定义以下预处理器符号来设置MAX_PDU_SIZE=69。此外, 可用于安全连接的最小堆大小为3690。

    4. 通过根据需要初始化其参数来配置GAPBondMgr。有关具有描述功能的参数的完整列表,请参阅BLE Stack API参考(GAPBondMgr部分)。有关不同配对模式的GAPBondMgr示例中的各种配对/绑定模式的示例。

    5. 使用GAPBondMgr注册应用程序回调,以便应用程序可以与GAPBondMgr通信并通知事件。

       // Register with bond manager after starting device
       GAPBondMgr_Register(&bondmanager_callbacks);
      

    一旦GAPBondMgr被配置,它主要从应用程序的角度自主运行。当建立连接时,根据初始化期间设置的配置参数启动配对和绑定,并根据需要通过定义的回调与应用程序进行通信。

    可以随时从应用程序中设置一些参数和异步调用的函数。有关详细信息,请参阅BLE Stack API参考(GAPBondMgr部分)。

    GAPBondMgr与此应用程序之间的大多数通信都是通过在步骤5中注册的回调发生的。 下图是GAPBondMgr的流程图示例,通知应用程序配对已经完成。对于各种其他事件的发生也是用相同的方法,并将在后面部分进行扩展。

    GAPBondMgr不同配对模式的示例

    本节提供了上节提到的几种配对模式在应用程序中怎么通过修改参数进行配置,本节提供了程序消息框图,这些模式假设安全模式具有可以接受的IO输入/输出能力。这些例子只考虑配对方面,关于bond实现请参阅下一节,这里的代码片段不是完整的功能示例,仅用于说明目的。

    禁用配对

    配对设置FALSE后,BLE堆栈会自动拒绝任何配对尝试。如下配置GAPBondMgr以禁用配对:

    uint8  pairMode  =  GAPBOND_PAIRING_MODE_NO_PAIRING ; 
    GAPBondMgr_SetParameter (GAPBOND_PAIRING_MODE , sizeof (uint8_t ), &pairMode );
    

    Just work 配对

    Just Works配对允许加密而不需要MITM身份验证,并且容易受到攻击。Just Works配对可以是LE Legacy或Secure Connections配对。GAPBondMgr不需要任何额外的输入从应用程序对于Just Work配对。配置GAPBondMgr为Just Works配对如下。

    	uint8_t  pairMode  =  GAPBOND_PAIRING_MODE_INITIATE ; 
    	uint8_t  mitm  =  FALSE ; 
    	GAPBondMgr_SetParameter ( GAPBOND_PAIRING_MODE , sizeof  (uint8_t ), &pairMode ); 
    	GAPBondMgr_SetParameter ( GAPBOND_MITM_PROTECTION , sizeof  (uint8_t ), &mitm );
    

    下图描述了GAPBondMgr和Just Works配对应用程序之间的交互。如图所示,应用程序GAPBOND_PAIRING_STATE_STARTED一旦发送配对请求就收到一个事件,GAPBOND_PAIRING_STATE_COMPLETE一旦配对过程完成,就会发生一个事件。此时,链接被加密。

    PassKey Entry

    密钥条目是一种可以防止MITM攻击的身份验证配对。它可以是LE Legacy配对或安全连接配对。在这种配对方法中,一个设备显示6位密码,另一个设备输入密码。如选择配对模式所述,IO功能决定哪个设备执行哪个角色。GAPBondMgr在启动时注册的密码回调用于输入或显示密码。以下是启动Passcode Entry配对的示例,其中显示密码。

    1. 定义密码回调

       // Bond ManagerCB
       static  gapBondCBs_t  security_examples_central_bondCB  = 
       { 
         (pfnPasscodeCB_t )security_examples_central_passcodeCB , //密码回调
         security_examples_central_pairStateCB                   //配对状态回调
       };
       
       static  void  security_examples_central_passcodeCB (uint8_t  * deviceAddr , uint16_t  connHandle , uint8_t  uiInputs , uint8_t  uiOutputs , uint32_t  numComparison )
       { 
         gapPasskeyNeededEvent_t  * pData ;  //为密码事件分配空间
       
         if  ((pData  =  ICall_malloc (sizeof (gapPasskeyNeededEvent_t ))))
         { 
           memcpy (pData - > deviceAddr , deviceAddr , B_ADDR_LEN ); 
           pData - > connectionHandle  =  connHandle ; 
           pData - > uiInputs  =  uiInputs ; 
           pData - > uiOutputs  =  uiOutputs ;
       
           //排队事件 
           security_examples_central_enqueueMsg (SEC_PASSCODE_NEEDED_EVT , 0 , (uint8_t  * ) pData ); 
         } 
       }
      
    2. 配置GAPBondMgr

       uint8_t  pairMode  =  GAPBOND_PAIRING_MODE_INITIATE ; 
       uint8_t  mitm  =  TRUE ; 
       GAPBondMgr_SetParameter (GAPBOND_PAIRING_MODE , sizeof (uint8_t ), &uint8_t  pairMode  =  GAPBOND_PAIRING_MODE_INITIATE ; 
       GAPBondMgr_SetParameter (GAPBOND_PAIRING_MODE , sizeof (uint8_t ), &mitm );
      
    3. 处理密码回调并向协议栈发送响应

       static  void  security_examples_central_processPasscode (uint16_t  connectionHandle , gapPasskeyNeededEvent_t  * pData )
       {
       
         if  (pData - > uiInputs ) //如果我们要输入passkey 
         { 
           passcode  =  111111 ; 
           //发送密码响应
           GAPBondMgr_PasscodeRsp (connectionHandle , SUCCESS , passcode ); 
         }
       
         else  if  (pData - > uiOutputs ) //如果我们要显示密码
         { 
           passcode  =  111111 ; 
           DISPLAY_WRITE_STRING_VALUE (“passcode:%d” , passcode, LCD_PAGE4 );
       
           //发送密码响应
           GAPBondMgr_PasscodeRsp (connectionHandle , SUCCESS , passcode ); 
         } 
       }
      

    根据uiInputs和uiOutputs从GAPBondMgr返回的内容,必须显示或输入密码。然后使用GAPBondMgr_PasscodeRsp()将密码发送到GAPBondMgr ,以便配对可以继续。在这种情况下,密码静态设置为111111.在实际产品中,密码很可能是随机生成的,设备必须为用户提供输入密码的方式,然后使用GAPBondMgr_PasscodeRsp()发送给GAPBondMgr 。GAPBondMgr和应用程序之间的一个示例交互如图所示。

    Numeric Comparison

    数字比较是一种经过身份验证的配对,可以防止MITM攻击。只能作为安全连接配对; 不是LE遗产。对于数字比较配对,两个设备都显示6位数的代码。每个设备必须通过按钮或其他一些无输入来指示代码是否匹配。GAPBondMgr在启动时注册的密码回调用于显示6位数代码。以下是启动显示密码的数字比较配对的示例。必须适当设置IO功能才能选择数值比较(即Yes-No)。

    1. 定义密码回调来显示代码。

       // Bond Manager回调
       static  gapBondCBs_t  SimpleBLECentral_bondCB  = 
       { 
         (pfnPasscodeCB_t )SimpleBLECentral_passcodeCB , //密码回调
         SimpleBLECentral_pairStateCB                   //配对状态回调
       }; 
       
       static  void  SimpleBLECentral_passcodeCB  (uint8_t  * deviceAddr , uint16_t  connHandle , uint8_t  uiInputs , uint8_t  uiOutputs , uint32_t  numComparison )
       { 
         gapPasskeyNeededEvent_t  * pData ; 
       
         //为密码事件分配空间 
         if  ((pData  =  ICall_malloc (sizeof (gapPasskeyNeededEvent_t ))))
         { 
           memcpy (pData - > deviceAddr , deviceAddr , B_ADDR_LEN ); 
           pData - > connectionHandle  =  connHandle ; 
           pData - > numComparison  =  numComparison ; 
       
           //排队事件 
           security_examples_central_enqueueMsg (SEC_PASSCODE_NEEDED_EVT , 0 ,   
      
    2. 配置GAPBondMgr

       uint8_t  pairMode  =  GAPBOND_PAIRING_MODE_INITIATE ; 
       uint8_t  scMode  =  GAPBOND_SECURE_CONNECTION_ONLY ; 
       uint8_t  mitm  =  TRUE ; 
       uint8_t  ioCap  =  GAPBOND_IO_CAP_DISPLAY_YES_NO ; 
       
       GAPBondMgr_SetParameter (GAPBOND_IO_CAPABILITIES , sizeof (uint8_t ), &ioCap ); 
       GAPBondMgr_SetParameter (GAPBOND_PAIRING_MODE , sizeof (uint8_t ), &pairMode ); 
       GAPBondMgr_SetParameter (GAPBOND_MITM_PROTECTION , sizeof (uint8_t ), &mitm ); 
       GAPBondMgr_SetParameter (GAPBOND_SECURE_CONNECTION , sizeof (uint8_t ), &scMode );
      
    3. 处理密码回调和显示代码

       static  void  SimpleBLECentral_processPasscode  (uint16_t  connectionHandle , gapPasskeyNeededEvent_t  * pData )
       { 
       
         if  (pData - > numComparison ) //数值比较
         { 
       
           //显示密码
           DISPLAY_WRITE_STRING_VALUE (“Num Cmp:%d” , pData - > numComparison , LCD_PAGE4 ); 
         } 
       }
      
    4. 接受用户输入Yes-No,并发送回应GAPBondMgr。

       if  (keys  & KEY_RIGHT )
       { 
         GAPBondMgr_PasscodeRsp (connHandle , SUCCESS , TRUE ); 
         DISPLAY_WRITE_STRING (“Codes Match!” , LCD_PAGE5 ); 
         return; 
       }
      

    在这种情况下,通常接受密码的GAPBondMgr_PasscodeRsp的第三个参数被重载,以向堆栈发送TRUE,以指示代码匹配并继续配对。数字比较的过程如图56所示。

    Bonding示例

    可以通过GAPBOND_BONDING_ENABLED参数对任何类型的配对启用或禁用绑定,并在配对过程完成后进行绑定 。要启用绑定,请按如下所示配置GAPBondMgr:

    uint8_t  bonding  =  TRUE ; 
    GAPBondMgr_SetParameter (GAPBOND_BONDING_ENABLED , sizeof (uint8_t ), &bonding );
    

    启用绑定后,GAPBondMgr将配对过程中传输的长期密钥存储到SNV。有关详细信息,请参阅GAPBondMgr和SNV。完成后,通过GAPBOND_PAIRING_STATE_COMPLETE事件GAPBOND_PAIRING_STATE_BOND_SAVED通知应用程序。最初连接,配对和绑定时只传递给应用程序对状态回调。为了将来连接到绑定设备,安全密钥从闪存加载,从而跳过配对过程。在这种情况下,只 GAPBOND_PAIRING_STATE_BONDED传递给应用程序对状态回调。这在图57中示出。

    GAPBondMgr和SNV

    本节介绍GAPBondMgr如何使用SNV闪存区域来存储bond信息。有关SNV本身的更多信息,请参阅内存管理 Flash部分。可以存储的bond数量由GAP_BONDINGS_MAX定义设置,默认情况下在gapbondmgr.h中设置为10。GAPBondMgr的功能在不存在可用的绑定时会有所不同。有关参数的更多信息,请参阅BLE Stack API参考(GAPBondMgr部分)。GAPBOND_LRU_BOND_REPLACEMENT:如果此参数设置为false,则不删除bond从而不能添加任何更多的bond。如果参数设置为true,则删除最近最少使用的bond为新bond腾出空间。

    1. bond记录:这个结构体包括设备的地址,地址类型,隐私重新连接地址和状态标志

      typedef struct
      {
      uint8 publicAddr[B_ADDR_LEN]; // Peer's address
      uint8 publicAddrType; // Peer's address type
      uint8 reconnectAddr[B_ADDR_LEN]; // Privacy Reconnection Address
      uint8 stateFlags; // State flags: @ref GAP_BONDED_STATE_FLAGS
      } gapBondRec_t;

    2. 客户端特征配置 Client Characteristic Configurations(CCC):存储在每个条目中的CCC数量由GAP_CHAR_CFG_MAXdefine 设置。默认设置为4。每个CCC由4个字节组成,定义如下:

      typedef struct
      {
      uint16 attrHandle ; //属性句柄
      uint8 value ; //该设备的属性值
      } gapBondCharCfg_t ;

    3. 本地长期密钥(LTK)信息:这存储本地设备的加密信息。这包括28个字节,其组成如下:

      typedef struct
      {
      uint8 LTK [ KEYLEN ]; //长期密钥(LTK)
      uint16 div ; // lint -e754 // LTK eDiv
      uint8 rand [ B_RANDOM_NUM_SIZE ]; // LTK随机数
      uint8 keySize ; // LTK key size
      } gapBondLTK_t ;

    4. 连接设备长期密钥信息:这将存储连接的设备的加密信息。这也是一个gapBondLTK_t,包含28个字节。

    5. 连接的设备身份解析密钥(IRK):存储配对期间生成的IRK。这是一个16字节的数组。

    6. 连接的设备符号解析密钥(SRK):这将存储配对期间生成的SRK。这是一个16字节的数组。

    7. 连接设备标志计数器:这将存储配对期间生成的符号计数器。这是一个4字节的字。

    加入我们

    文章所有代码、工具、文档开源。加入我们QQ群 591679055获取更多支持,共同研究CC2640R2F&BLE5.0。

    CC2640R2F&BLE5.0-乐控畅联© Copyright 2017, 成都乐控畅联科技有限公司.

    展开全文
  • SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合...

    SYD8801是一款低功耗高性能蓝牙低功耗SOC,集成了高性能2.4GHz射频收发机、32位ARM Cortex-M0处理器、128kB Flash存储器、以及丰富的数字接口。SYD8801片上集成了Balun无需阻抗匹配网络、高效率DCDC降压转换器,适合用于可穿戴、物联网设备等。具体可咨询:http://www.sydtek.com/

     

    配对和绑定的关系和定义

    配对的定义:

    绑定的定义:

    也就是说配对只是产生密码,而绑定是要保存这些密码和两个蓝牙之间的关系!

    可以看如下图(摘录于:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=21411227&id=5749244):

     

     

     

    IO Capabilities

     

    sec_params.rsp_key

     

    BONDABLE MODES

     

    加密方式Selecting STK Generation Method

        蓝牙加密方式通过主机和从机两边的IO能力来确定,具体功能列表如下:

     

     

     

    Encryption Procedure

     

    计算MICDERIVATION OF THE MIC AND ENCRYPTED DATA

    展开全文
  • NRF52832 SDK15.3 ble bond

    2019-11-16 07:38:04
    NRF52832 SDK15.3蓝牙绑定例程,在官网uart例程基础上演示,共三个例程,无密码绑定,静态密码绑定,动态密码绑定,支持断电保存最近一台绑定的设备信息
  • 蓝牙配对鉴权

    千次阅读 2019-09-05 15:16:14
    Action: BluetoothDevice.ACTION_BOND_STATE_CHANGED 远程设备不需要鉴权,可以直接使用 需要输入 Action : BluetoothDevice.ACTION_PAIRING_REQUEST。 BluetoothDevice.PAIRING_VARIANT_PASSKEY_...

    蓝牙配对

    • 本机请求配对其他设备。调用BluetoothDevice的createBond。
    • 本机接收到其他设备的配对请求。类BluetoothPairingRequest处理

    BluetoothPairingDialog: 蓝牙配对弹窗

    不需要鉴权

    Action: BluetoothDevice.ACTION_BOND_STATE_CHANGED
    远程设备不需要鉴权,可以直接使用

    需要输入

    Action : BluetoothDevice.ACTION_PAIRING_REQUEST。

    • BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION
      需要用户确认

    • BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY
      输入显示在远程设备上的秘钥

    • BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN
      在本机输入显示在远程设备上的pin码

    这三种可以自动配对,远程设备本身携带有配对码pairingkey,可以通过BluetoothDevice.EXTRA_PAIRING_KEY获取到配对码

    其他的需要手动输入

    对话框显示

     switch (mType) {
                //提示用户输入pin码或passkey   ------秘钥接入 (Passkey Entry)
                case BluetoothDevice.PAIRING_VARIANT_PIN:
                case BluetoothDevice.PAIRING_VARIANT_PASSKEY:
                    createUserEntryDialog();
                    break;
                //提示用户确定显示在屏幕上的passkey   ------使用简单 (Just Works)
                case BluetoothDevice.PAIRING_VARIANT_PASSKEY_CONFIRMATION:
                     createConfirmationDialog();
                    break;
                //用户将被提示接受或拒绝传入的配对请求。  ------带外数据(Out Of Band)
                case BluetoothDevice.PAIRING_VARIANT_CONSENT:
                case BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT:
                    createConsentDialog();
                    break;
                //用户将被提示输入显示在屏幕上的passkey/pin   ------数字比较(Numeric Comparison)
                case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PASSKEY:
                case BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN:
                     createDisplayPasskeyOrPinDialog();
                    break;
    
                default:
                    Log.e(TAG, "Incorrect pairing type received, not showing any dialog");
            }
    
    
    

    去提示的方法

        private void createConfirmationDialog(CachedBluetoothDeviceManager deviceManager) {
            final AlertController.AlertParams p = mAlertParams;
            p.mIconId = android.R.drawable.ic_dialog_info;
            p.mTitle = getString(R.string.bluetooth_pairing_request);
            p.mView = createView(deviceManager);
            p.mPositiveButtonText = getString(R.string.bluetooth_pairing_accept);
            p.mPositiveButtonListener = this;
            p.mNegativeButtonText = getString(R.string.bluetooth_pairing_decline);
            p.mNegativeButtonListener = this;
            setupAlert();
            //Add
    	mOkButton = mAlert.getButton(BUTTON_POSITIVE);
            mOkButton.performClick();
        }
    
    蓝牙事件管理BluetoothEventManager

    注册一个事件接收广播mContext.registerReceiver(mBroadcastReceiver, mAdapterIntentFilter);,用于接收底层驱动发来的广播。然后根据不同的action分发给不同的handler处理。

     interface Handler {
            void onReceive(Context context, Intent intent, BluetoothDevice device);
        }
    
        void addHandler(String action, Handler handler) {
            mHandlerMap.put(action, handler);
            mAdapterIntentFilter.addAction(action);
        }
        
        private class AdapterStateChangedHandler implements Handler {
            public void onReceive(Context context, Intent intent,
                    BluetoothDevice device) {
                int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
                                        BluetoothAdapter.ERROR);
                // update local profiles and get paired devices
                mLocalAdapter.setBluetoothStateInt(state);
                // send callback to update UI and possibly start scanning
                synchronized (mCallbacks) {
                    for (BluetoothCallback callback : mCallbacks) {
                        callback.onBluetoothStateChanged(state);
                    }
                }
                // Inform CachedDeviceManager that the adapter state has changed
                mDeviceManager.onBluetoothStateChanged(state);
            }
        }
        private class ScanningStateChangedHandler implements Handler {
            private final boolean mStarted;
    
            ScanningStateChangedHandler(boolean started) {
                mStarted = started;
            }
            public void onReceive(Context context, Intent intent,
                    BluetoothDevice device) {
                synchronized (mCallbacks) {
                    for (BluetoothCallback callback : mCallbacks) {
                        callback.onScanningStateChanged(mStarted);
                    }
                }
                mDeviceManager.onScanningStateChanged(mStarted);
                LocalBluetoothPreferences.persistDiscoveringTimestamp(context);
            }
        }
            private class DeviceFoundHandler implements Handler {
    ...
    }
     private class BondStateChangedHandler implements Handler {
            public void onReceive(Context context, Intent intent,
                    BluetoothDevice device) {
                    ...
     cachedDevice.onBondingStateChanged(bondState);
    }
    }
    
    Devices

    CachedBluetoothDeviceManager负责管理蓝牙的缓存(已配对的设备和搜索到的设备)
    主要都保存在List mCachedDevices中.
    通过external/bluetooth/bluedroid 保存在/data/misc/bluedroid/bt_config.xml
    btif_config.c 中save_cfg().

            <N19 Tag="dc:0c:5c:d5:c7:2f">
                <N1 Tag="Timestamp" Type="int">1568600552</N1>
                <N2 Tag="Name" Type="string">gbmaotai</N2>
                <N3 Tag="DevClass" Type="int">7995916</N3>
                <N4 Tag="DevType" Type="int">1</N4>
                <N5 Tag="AddrType" Type="int">0</N5>
                <N6 Tag="Manufacturer" Type="int">15</N6>
                <N7 Tag="LmpVer" Type="int">8</N7>
                <N8 Tag="LmpSubVer" Type="int">24835</N8>
                <N9 Tag="LinkKeyType" Type="int">5</N9>
                <N10 Tag="PinLength" Type="int">0</N10>
                <N11 Tag="LinkKey" Type="binary">d32a09b687235cdd8b4a2c67489dd45c</N11>
                <N12 Tag="Service" Type="string">0000110a-0000-1000-8000-00805f9b34fb 00001116-0000-1000-8000-00805f9b34fb 0000110e-0000-1000-8000-00805f9b34fb 0000112f-0000-1000-8000-00805f9b34fb 0000111f-0000-1000-8000-00805f9b34fb 00001132-0000-1000-8000-00805f9b34fb 02030302-1d19-415f-86f2-22a2106a0a77 00000000-deca-fade-deca-deafdecacafe </N12>
            </N19>
    

    DevType字段的含义:1代表是传统蓝牙设备,2代表BLE设备;设备没有上报自动识别为3。

    蓝牙列表

    DeviceListPreferenceFragment负责管理蓝牙的显示设备
    搜索到的蓝牙设备是以BluetoothDevicePreference的形式展示在PreferenceScreen中

        void onClicked() {
           int bondState = mCachedDevice.getBondState();
           if (mCachedDevice.isConnected()) {
               askDisconnect();    //如果已经处于连接状态则弹出对话框询问用户是否取消当前蓝牙设备的连接
           } else if (bondState == BluetoothDevice.BOND_BONDED) {
                    mCachedDevice.connect(true);   //当前设备是历史配对设备(即通过BOND状态判断如果是已配对过,则在本地应该保留有pin/passkey),则尝试通过profile连接蓝牙设备。
           } else if (bondState == BluetoothDevice.BOND_NONE) {
               pair();                       //当前蓝牙设备未曾配对过(BOND_NONE),则进入配对流程
           }
       }
    
     boolean startPairing() {
            // Pairing is unreliable while scanning, so cancel discovery
            if (mLocalAdapter.isDiscovering()) {
                mLocalAdapter.cancelDiscovery();
            }
    
            if (!mDevice.createBond()) {
                return false;
            }
    
            mConnectAfterPairing = true; 
            return true;
        }
    
    展开全文
  • 连接蓝牙耳机过程

    千次阅读 2013-01-18 16:03:14
    首先BluetoothAudioGateway 会在一个线程中收到来自蓝牙耳机的RFCOMM 连接(MSG_INCOMING_HEADSET_CONNECTION),然后发送消息给BluetoothHeadsetService。如果一个设备支持headset和...在配对上蓝牙耳机之后,收到BOND
  • Android 蓝牙状态

    千次阅读 2013-03-28 10:02:04
    BluetoothDevice类的方法 getBondState 获得的是匹配状态,不是连接状态。 public int getBondState() ...Get the bond state of the remote device. Possible values for the bond state are: BON
  • 蓝牙列表点击   在蓝牙设置页面,打开搜索到的蓝牙设备是以BluetoothDevicePreference的形式展示在PreferenceScreen中,当我们点击蓝牙列表中的蓝牙设备时,... int bondState = mCachedDevice.getBondState(); ...
  • 扫描通过广播得到我要的已经配对过的蓝牙设备之后进行连接,一直报E/TAG: java.io.IOException: read failed, socket might closed or timeout, read ret: -1代码如下 if(addr.equals("74:23:44:CF:4E:DF")){ ...
  • 在开发蓝牙功能的时候在网上下了一个demo,demo代码移植在我的一个测试项目上是完全可以查找和连接的,但是吧代码移植到我的正式工程的时候总是不行不知道怎么回事(报错信息在网上也不好找到解决原因),这里有这...
  • 什么是低功耗蓝牙配对? 什么又是绑定? 配对和绑定有什么区别? 配对有什么好处? 如何删除绑定信息? 如何确定配对的安全等级? just work的配对一定就不安全吗? 如何开发自己的配对应用? 本文将对以上...
  • **两部手机开启蓝牙,只...= BluetoothDevice.BOND_BONDED) { > > //信号强度。 > short rssi = intent.getExtras().getShort( > BluetoothDevice.EXTRA_RSSI); > > } > > } > } > }; > >
  • 比如手机去掉耳机孔,蓝牙耳机开始流行;笔记本电脑去掉一些不常用的网口、USB接口,也让Type-C扩展坞成为了一些用户的刚需。为了满足不同人群的需求,扩展坞的种类也是非常多,如何挑选一个适合自己使用的扩展坞就...
  • Android Bluetooth Profile通信

    千次阅读 2016-07-02 12:02:01
    随着智能设备的普及,蓝牙开发在手机应用端使用的越来越多。但是目前网上对蓝牙开发的介绍还相当少...1.BondBond即设备之间绑定(配对),这是蓝牙设备之间通信的基础。当搜索到需要bond的设备时,获取到设备对应的B
  • Android Bluetooth连接以及Profile使用

    千次阅读 2016-07-08 10:04:55
    随着智能设备的普及,蓝牙开发在手机应用端使用的越来越多。但是目前网上对蓝牙开发的介绍还相当少...1.BondBond即设备之间绑定(配对),这是蓝牙设备之间通信的基础。当搜索到需要bond的设备时,获取到设备对应的B
  • 4.3 android bluetooth hfp分析

    千次阅读 2013-08-20 20:16:24
    所有程序执行的代码都是有入口的,在这里我们暂时分析一种情景,蓝牙打开着,蓝牙耳机连接。 在设置界面点击蓝牙耳机操作: packages/apps/Settings/src/... void onClicked() { int bondState
  • 在BLE的源码架构中,感觉是好复杂,还好TI对协议...正如上图所示,最简单一次蓝牙通信需要以上相关步骤,包括discovery device,connect,pairing,bond等4个主要部分。   2.BLE中的GAP和GATT 初始接触,感觉十分的抽
  • = BluetoothDevice.BOND_BONDED) { mDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress()); } // 扫描结束 } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { ...
  • intent.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_SCAN_MODE_CHANGED); intent.addAction(BluetoothAdapter.ACTION_STATE_CHANGED); registerReceiver...

空空如也

空空如也

1
收藏数 20
精华内容 8
关键字:

蓝牙bond