zoukankan      html  css  js  c++  java
  • Hogp连接流程分析

    当BLE设备已经完成配对,并且完成GATT服务的搜索,下一步就开始profile 的连接流程了,一般LE设备都是走的HOGP的流程,我们这篇文章就分析一下hogp的连接流程。

    连接是从framework到JNI,再到协议栈,我们从JNI 分析流程吧。

    在HID profile中,与framework 层对接的JNI 文件是com_android_bluetooth_hid.cpp,其调用的connct函数是connectHidNative:

    static jboolean connectHidNative(JNIEnv *env, jobject object, jbyteArray address) {
        bt_status_t status;
        jbyte *addr;
        jboolean ret = JNI_TRUE;
        if (!sBluetoothHidInterface) return JNI_FALSE;
    
        addr = env->GetByteArrayElements(address, NULL);
        if (!addr) {
            ALOGE("Bluetooth device address null");
            return JNI_FALSE;
        }
    
        if ((status = sBluetoothHidInterface->connect((bt_bdaddr_t *) addr)) !=
             BT_STATUS_SUCCESS) {//获取hid host的interface,然后调用connect的接口
            ALOGE("Failed HID channel connection, status: %d", status);
            ret = JNI_FALSE;
        }
        env->ReleaseByteArrayElements(address, addr, 0);
    
        return ret;
    }

    我们看hid的interface:

    static const bthh_interface_t bthhInterface = {
        sizeof(bthhInterface),
        init,
        connect,
        disconnect,
        virtual_unplug,
        set_info,
        get_protocol,
        set_protocol,
        get_report,
        set_report,
        send_data,
        cleanup,
    }

     直接看 connect:

    /*******************************************************************************
    **
    ** Function        connect
    **
    ** Description     connect to hid device
    **
    ** Returns         bt_status_t
    **
    *******************************************************************************/
    static bt_status_t connect( bt_bdaddr_t *bd_addr)
    {
        if(btif_hh_cb.status != BTIF_HH_DEV_CONNECTING)
        {
            btif_transfer_context(btif_hh_handle_evt, BTIF_HH_CONNECT_REQ_EVT,
                                     (char*)bd_addr, sizeof(bt_bdaddr_t), NULL);//转移到btif task中去执行
            return BT_STATUS_SUCCESS;
        }
        else
            return BT_STATUS_BUSY;
    }
    /*******************************************************************************
    **
    ** Function         btif_hh_handle_evt
    **
    ** Description      Switches context for immediate callback
    **
    ** Returns          void
    **
    *******************************************************************************/
    
    static void btif_hh_handle_evt(UINT16 event, char *p_param)
    {
        bt_bdaddr_t *bd_addr = (bt_bdaddr_t*)p_param;
        BTIF_TRACE_EVENT("%s: event=%d", __FUNCTION__, event);
        int ret;
        switch(event)
        {
            case BTIF_HH_CONNECT_REQ_EVT:
            {
                ret = btif_hh_connect(bd_addr);//连接走到btif
                if(ret == BT_STATUS_SUCCESS)
                {
                    HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_CONNECTING);//向上汇报正在连接
                }
                else
                    HAL_CBACK(bt_hh_callbacks, connection_state_cb,bd_addr,BTHH_CONN_STATE_DISCONNECTED);
            }

    看看btif_hh的实现:

    /*******************************************************************************
    **
    ** Function         btif_hh_connect
    **
    ** Description      connection initiated from the BTIF thread context
    **
    ** Returns          int status
    **
    *******************************************************************************/
    
    bt_status_t btif_hh_connect(bt_bdaddr_t *bd_addr)
    {
        btif_hh_device_t *dev;
        btif_hh_added_device_t *added_dev = NULL;
        char bda_str[20];
        int i;
        BD_ADDR *bda = (BD_ADDR*)bd_addr;
        CHECK_BTHH_INIT();
        dev = btif_hh_find_dev_by_bda(bd_addr);
        BTIF_TRACE_DEBUG("Connect _hh");
        sprintf(bda_str, "%02X:%02X:%02X:%02X:%02X:%02X",
                (*bda)[0], (*bda)[1], (*bda)[2], (*bda)[3], (*bda)[4], (*bda)[5]);
    ...
    
        /* Not checking the NORMALLY_Connectible flags from sdp record, and anyways sending this
         request from host, for subsequent user initiated connection. If the remote is not in
         pagescan mode, we will do 2 retries to connect before giving up */
        tBTA_SEC sec_mask = BTUI_HH_SECURITY;
        btif_hh_cb.status = BTIF_HH_DEV_CONNECTING;
        BTA_HhOpen(*bda, BTA_HH_PROTO_RPT_MODE, sec_mask);
    
        HAL_CBACK(bt_hh_callbacks, connection_state_cb, bd_addr, BTHH_CONN_STATE_CONNECTING);//这里再次汇报了连接的状态
        return BT_STATUS_SUCCESS;
    }

    这里留一个问题:此刻调用btif_hh_find_dev_by_bda 有没有找到相应的记录?估计是没有,

    那么这里的重点是 就是BTA_HhOpenle ,从 函数名字看,流程已经走到了BTA 层,继续看。

    /*******************************************************************************
    **
    ** Function         BTA_HhOpen
    **
    ** Description      Connect to a device of specified BD address in specified
    **                  protocol mode and security level.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void BTA_HhOpen(BD_ADDR dev_bda, tBTA_HH_PROTO_MODE mode, tBTA_SEC sec_mask)
    {
        tBTA_HH_API_CONN *p_buf;
    
        p_buf = (tBTA_HH_API_CONN *)GKI_getbuf((UINT16)sizeof(tBTA_HH_API_CONN));
    
        if (p_buf!= NULL)
        {
            memset((void *)p_buf, 0, sizeof(tBTA_HH_API_CONN));
    
            p_buf->hdr.event            = BTA_HH_API_OPEN_EVT;
            p_buf->hdr.layer_specific   = BTA_HH_INVALID_HANDLE;
            p_buf->sec_mask             = sec_mask;
            p_buf->mode                 = mode;
            bdcpy(p_buf->bd_addr, dev_bda);
    
            bta_sys_sendmsg((void *)p_buf);
        }
        else
        {
            APPL_TRACE_ERROR("No resource to send HID host Connect request.");
        }
    }

    想BTU发送了BTA_HH_API_OPEN_EVT 事件,从函数的注释看,虽然名字上面带有open,其实就是干connect的活,我们继续看:

    /*******************************************************************************
    **
    ** Function         bta_hh_hdl_event
    **
    ** Description      HID host main event handling function.
    **
    **
    ** Returns          void
    **
    *******************************************************************************/
    BOOLEAN bta_hh_hdl_event(BT_HDR *p_msg)
    {
        UINT8           index = BTA_HH_IDX_INVALID;
        tBTA_HH_DEV_CB *p_cb = NULL;
    
        switch (p_msg->event)
        {
    ...
            default:
                /* all events processed in state machine need to find corresponding
                    CB before proceed */
                if (p_msg->event == BTA_HH_API_OPEN_EVT)
                {
                    index = bta_hh_find_cb(((tBTA_HH_API_CONN *)p_msg)->bd_addr);
                }
               ...
                if (index != BTA_HH_IDX_INVALID)
                    p_cb = &bta_hh_cb.kdev[index];
    
                bta_hh_sm_execute(p_cb, p_msg->event, (tBTA_HH_DATA *) p_msg);//进入状态机
        }
        return (TRUE);
    }

    我们继续看状态机中如何处理:bluedroid中状态机的处理的流程都i一样,这里不做详细分析:

    /*******************************************************************************
    **
    ** Function         bta_hh_sm_execute
    **
    ** Description      State machine event handling function for HID Host
    **
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data)
    {
        tBTA_HH_ST_TBL  state_table;
        UINT8           action;
        tBTA_HH         cback_data;
        tBTA_HH_EVT     cback_event = 0;
    #if BTA_HH_DEBUG == TRUE
        tBTA_HH_STATE   in_state ;
        UINT16          debug_event = event;
    #endif
    
        memset(&cback_data, 0, sizeof(tBTA_HH));
    ...
    else
        {
            state_table = bta_hh_st_tbl[p_cb->state - 1];
    
            event &= 0xff;
    
            p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ;
    
            if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE)
            {
                (*bta_hh_action[action])(p_cb, p_data);
            }
       }
    
        return;
    }

    看看状态表:

    /* BTA_HH_API_OPEN_EVT      */    {BTA_HH_START_SDP,     BTA_HH_W4_CONN_ST },

    那么下一个状态是BTA_HH_W4_CONN_ST,当前执行的action 是BTA_HH_START_SDP ,我们从action 的名字上面可以看出,肯定是 做HID host相关的搜索的工作,这里执行的函数是bta_hh_start_sdp:

    其实我们知道,BREDR的设备进行服务搜索走的是sdp的流程,但是BLE  一般走的是GATT的流程。

    /*******************************************************************************
    **
    ** Function         bta_hh_start_sdp
    **
    ** Description      Start SDP service search, and obtain necessary SDP records.
    **                  Only one SDP service search request is allowed at the same
    **                  time. For every BTA_HhOpen API call, do SDP first unless SDP
    **                  has been done previously.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_hh_start_sdp(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
    {
        tBTA_HH_STATUS          status = BTA_HH_ERR_SDP;
        UINT8                   hdl;
    
        p_cb->sec_mask  = p_data->api_conn.sec_mask;
        p_cb->mode      = p_data->api_conn.mode;
        bta_hh_cb.p_cur = p_cb;
    
    #if (BTA_HH_LE_INCLUDED == TRUE)
        if (bta_hh_is_le_device(p_cb, p_data->api_conn.bd_addr))
        {
            bta_hh_le_open_conn(p_cb, p_data->api_conn.bd_addr);//le open
            return;
        }
    #endif
    ...

    这里判断是le 的设备,走le 的流程:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_open_conn
    **
    ** Description      open a GATT connection first.
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_open_conn(tBTA_HH_DEV_CB *p_cb, BD_ADDR remote_bda)
    {
        /* update cb_index[] map */
        p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index);
        memcpy(p_cb->addr, remote_bda, BD_ADDR_LEN);
        bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index;
        p_cb->in_use = TRUE;
    
        BTA_GATTC_Open(bta_hh_cb.gatt_if, remote_bda, TRUE, BTA_GATT_TRANSPORT_LE);//open
    }

    这里又走到了BTA_GATTC_Open,我们在 这篇文章中分析过这个函数,这里需要注意的是 这里的bta_hh_cb.gatt_if  是 hogp 注册到GATT的 interface,这里打开也是打开 hogp的接口。

    从之前的文章分析中得知,在配对之后也会进行BTA_GATTC_Open,只是那时打开的是 device manager注册到GATT的接口,并且在那个流程已经完成了对于GATT service的查询。这一次再次打开一个GATT 的接口,应该不会再进行服务的重新搜索,可能会对已经搜索到的服务进行读取工作。我们继续看:

    void BTA_GATTC_Open(tBTA_GATTC_IF client_if, BD_ADDR remote_bda,
                        BOOLEAN is_direct, tBTA_GATT_TRANSPORT transport)
    {
        tBTA_GATTC_API_OPEN  *p_buf;
    
        if ((p_buf = (tBTA_GATTC_API_OPEN *) GKI_getbuf(sizeof(tBTA_GATTC_API_OPEN))) != NULL)
        {
            p_buf->hdr.event = BTA_GATTC_API_OPEN_EVT;
    
            p_buf->client_if = client_if;
            p_buf->is_direct = is_direct;
            p_buf->transport = transport;
            memcpy(p_buf->remote_bda, remote_bda, BD_ADDR_LEN);
    
    
            bta_sys_sendmsg(p_buf);
        }
        return;
    }

    发送事件到btu: BTA_GATTC_API_OPEN_EVT;

    BOOLEAN bta_gattc_hdl_event(BT_HDR *p_msg)
    {
        tBTA_GATTC_CB *p_cb = &bta_gattc_cb;
        tBTA_GATTC_CLCB *p_clcb = NULL;
        tBTA_GATTC_RCB      *p_clreg;
        BOOLEAN             rt = TRUE;
        switch (p_msg->event)
        {
            case BTA_GATTC_API_OPEN_EVT:
                bta_gattc_process_api_open(p_cb, (tBTA_GATTC_DATA *) p_msg);
                break;
    ...

    继续看:

    /*******************************************************************************
    **
    ** Function         bta_gattc_process_api_open
    **
    ** Description      process connect API request.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_gattc_process_api_open (tBTA_GATTC_CB *p_cb, tBTA_GATTC_DATA * p_msg)
    {
        UINT16 event = ((BT_HDR *)p_msg)->event;
        tBTA_GATTC_CLCB *p_clcb = NULL;
        tBTA_GATTC_RCB *p_clreg = bta_gattc_cl_get_regcb(p_msg->api_conn.client_if);//r is register 
        UNUSED(p_cb);
    
        if (p_clreg != NULL)
        {
            if (p_msg->api_conn.is_direct)
            {
                if ((p_clcb = bta_gattc_find_alloc_clcb(p_msg->api_conn.client_if,
                                                        p_msg->api_conn.remote_bda,
                                                        p_msg->api_conn.transport)) != NULL)//分配一个p_clcb
                {
                    bta_gattc_sm_execute(p_clcb, event, p_msg);
                }
    ...
    
    }

    进入状态机,继续看:,状态处理的流程也和之前一样:

    /*******************************************************************************
    **
    ** Function         bta_gattc_sm_execute
    **
    ** Description      State machine event handling function for GATTC
    **
    **
    ** Returns          BOOLEAN  : TRUE if queued client request buffer can be immediately released
    **                                        else FALSE
    **
    *******************************************************************************/
    BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data)
    {
        tBTA_GATTC_ST_TBL     state_table;
        UINT8               action;
        int                 i;
        BOOLEAN             rt = TRUE;
        /* look up the state table for the current state */
        state_table = bta_gattc_st_tbl[p_clcb->state];
    
        event &= 0x00FF;
    
        /* set next state */
        p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE];
    
        /* execute action functions */
        for (i = 0; i < BTA_GATTC_ACTIONS; i++)
        {
            if ((action = state_table[event][i]) != BTA_GATTC_IGNORE)
            {
                (*bta_gattc_action[action])(p_clcb, p_data);
                if (p_clcb->p_q_cmd == p_data) {
                    /* buffer is queued, don't free in the bta dispatcher.
                     * we free it ourselves when a completion event is received.
                     */
                    rt = FALSE;
                }
            }
            else
            {
                break;
            }
        }
    
        return rt;
    }

    刚开始的是idle 状态:

    /* BTA_GATTC_API_OPEN_EVT           */   {BTA_GATTC_OPEN,              BTA_GATTC_W4_CONN_ST},

     这里的状态是GATT client 的状态转换,文章开头也涉及到状态机,那个是BTA_HH的状态转换,两者不能搞错,这种情况应该是属于状态机嵌套。

    我们继续分析GATT client 的状态转换,下一个状态是BTA_GATTC_W4_CONN_ST,执行的函数丨BTA_GATTC_OPEN-->

    /*******************************************************************************
    **
    ** Function         bta_gattc_open
    **
    ** Description      Process API connection function.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_gattc_open(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
    {
        tBTA_GATTC_DATA gattc_data;
    
        /* open/hold a connection */
        if (!GATT_Connect(p_clcb->p_rcb->client_if, p_data->api_conn.remote_bda,
                          TRUE, p_data->api_conn.transport))
        {
    ...//错误处理
        }
        else
        {
            /* a connected remote device */
            if (GATT_GetConnIdIfConnected(p_clcb->p_rcb->client_if,
                                          p_data->api_conn.remote_bda,
                                          &p_clcb->bta_conn_id,
                                          p_data->api_conn.transport))
            {
                gattc_data.int_conn.hdr.layer_specific = p_clcb->bta_conn_id;
    
                bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_CONN_EVT, &gattc_data);//还是在状态机里面轮转
            }
            /* else wait for the callback event */
        }
    }

    因为这个link 已经存在了,所以GATT_Connect 肯定是 直接返回了。那就是走到状态机发送新的事件:BTA_GATTC_INT_CONN_EVT,那我们继续看这个event 的处理:

    /* BTA_GATTC_INT_CONN_EVT           */   {BTA_GATTC_CONN,               BTA_GATTC_CONN_ST},

    下一个状态是 BTA_GATTC_CONN_ST,执行的action 是BTA_GATTC_CONN,我们继续看,

    /*******************************************************************************
    **
    ** Function         bta_gattc_conn
    **
    ** Description      receive connection callback from stack
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_gattc_conn(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
    {
        tBTA_GATTC_IF   gatt_if;
        if (p_data != NULL)
        {
            p_clcb->bta_conn_id  = p_data->int_conn.hdr.layer_specific;
    
            GATT_GetConnectionInfor(p_data->hdr.layer_specific,
                                    &gatt_if, p_clcb->bda, &p_clcb->transport);
        }
    
            p_clcb->p_srcb->connected = TRUE;
    
            if (p_clcb->p_srcb->mtu == 0)
                p_clcb->p_srcb->mtu = GATT_DEF_BLE_MTU_SIZE;
    
            /* start database cache if needed */
            if (p_clcb->p_srcb->p_srvc_cache == NULL ||
                p_clcb->p_srcb->state != BTA_GATTC_SERV_IDLE)//第一次打开的时候会进行service的搜索,现在p_srvc_cache!=NULL,并且p_srcb->state == BTA_GATTC_SERV_IDLE
            {
                APPL_TRACE_DEBUG("bta_gattc_conn start database cache if needed libs_liu");
                if (p_clcb->p_srcb->state == BTA_GATTC_SERV_IDLE)
                {
                    p_clcb->p_srcb->state = BTA_GATTC_SERV_LOAD;
                    bta_gattc_sm_execute(p_clcb, BTA_GATTC_START_CACHE_EVT, NULL);
                }
                else /* cache is building */
                    p_clcb->state = BTA_GATTC_DISCOVER_ST;
            }
    
            else
            {
                /* a pending service handle change indication */
                if (p_clcb->p_srcb->srvc_hdl_chg)
                {
                    p_clcb->p_srcb->srvc_hdl_chg = FALSE;
                    /* start discovery */
                    bta_gattc_sm_execute(p_clcb, BTA_GATTC_INT_DISCOVER_EVT, NULL);
                }
            }
    
            if (p_clcb->p_rcb)
            {
          ...
            bta_gattc_send_open_cback(p_clcb->p_rcb,
                                      BTA_GATT_OK,
                                      p_clcb->bda,
                                      p_clcb->bta_conn_id,
                                      p_clcb->transport,
                                      p_clcb->p_srcb->mtu);直接上报状态
            }
        }

    这里注意的是 。因为之前已经打开过了,已经走了gatt service的discovery的动作了,所以不会进行再次discovery了,直接来到上报状态的流程:

    /*******************************************************************************
    **
    ** Function         bta_gattc_send_open_cback
    **
    ** Description      send open callback
    **
    ** Returns
    **
    *******************************************************************************/
    void bta_gattc_send_open_cback( tBTA_GATTC_RCB *p_clreg, tBTA_GATT_STATUS status,
                                    BD_ADDR remote_bda, UINT16 conn_id,
                                    tBTA_TRANSPORT transport, UINT16 mtu)
    {
        tBTA_GATTC      cb_data;
    
        if (p_clreg->p_cback)
        {
            memset(&cb_data, 0, sizeof(tBTA_GATTC));
    
            cb_data.open.status = status;
            cb_data.open.client_if = p_clreg->client_if;
            cb_data.open.conn_id = conn_id;
            cb_data.open.mtu = mtu;
            cb_data.open.transport = transport;
            bdcpy(cb_data.open.remote_bda, remote_bda);
    
            (*p_clreg->p_cback)(BTA_GATTC_OPEN_EVT, &cb_data);//这里是注册的回调
        }
    }

    这个函数很简单,就是上报 BTA_GATTC_OPEN_EVT的状态,这里有时候会忘记这个回调是在在哪里注册的,其实很简单,看看上报的event 的名字是BTA_GATTC 打头的,那肯定是BTA_GATTC_AppRegister中注册的。

    那注册的回调函数是什么呢?注册的时候是在bta_hh_le_enable,注册是”BTA HH OVER LE“,

    /*******************************************************************************
    **
    ** Function         bta_hh_le_enable
    **
    ** Description      initialize LE HID related functionality
    **
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_hh_le_enable(void)
    {
        char       app_name[LEN_UUID_128 + 1];
        tBT_UUID    app_uuid = {LEN_UUID_128,{0}};
        UINT8       xx;
    
        bta_hh_cb.gatt_if = BTA_GATTS_INVALID_IF;
    
        for (xx = 0; xx < BTA_HH_MAX_DEVICE; xx ++)
            bta_hh_cb.le_cb_index[xx]       = BTA_HH_IDX_INVALID;
    
        memset (app_name, 0, LEN_UUID_128 + 1);
        strncpy(app_name, "BTA HH OVER LE", LEN_UUID_128);
    
        memcpy((void *)app_uuid.uu.uuid128, (void *)app_name, LEN_UUID_128);
    
        BTA_GATTC_AppRegister(&app_uuid, bta_hh_gattc_callback);
    
        return;
    }

    那我们知道,其实这个回调就是bta_hh_gattc_callback,它是”GATT client callback function used in BTA HH“,我们看看其处理的event有:

    BTA_GATTC_REG_EVT、BTA_GATTC_DEREG_EVT、BTA_GATTC_OPEN_EVT、BTA_GATTC_READ_CHAR_EVT、BTA_GATTC_WRITE_DESCR_EVT、BTA_GATTC_WRITE_CHAR_EVT、BTA_GATTC_CLOSE_EVT、BTA_GATTC_SEARCH_CMPL_EVT、BTA_GATTC_SEARCH_RES_EVT、BTA_GATTC_NOTIF_EVT、BTA_GATTC_ENC_CMPL_CB_EVT、BTA_GATTC_CFG_MTU_EVT

    从上面处理的事件来看,其回调几乎处理了hogp 相关的所有的事件,包括读写各种属性,搜索完成,以及搜索结果,以及LE设备的消息通知,都是从这里来处理,可见其是GATT流程中的重要一环。

    我们继续分析BTA_GATTC_OPEN_EVT 的处理:

    /*******************************************************************************
    **
    ** Function         bta_hh_gattc_callback
    **
    ** Description      This is GATT client callback function used in BTA HH.
    **
    ** Parameters:
    **
    *******************************************************************************/
    static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
    {
        tBTA_HH_DEV_CB *p_dev_cb;
        UINT16          evt;
    
        if (p_data == NULL)
            return;
    
        switch (event)
        {
    ...
            case BTA_GATTC_OPEN_EVT: /* 2 */
                p_dev_cb = bta_hh_le_find_dev_cb_by_bda(p_data->open.remote_bda);
                if (p_dev_cb) {
                    bta_hh_sm_execute(p_dev_cb, BTA_HH_GATT_OPEN_EVT, (tBTA_HH_DATA *)&p_data->open);//这里我们发现跳到HH相关的状态机
                }
                break;

     这个 事件是关联模块是BTA_ID_HH ,这里又转回了文章开始的时候的状态机

    #define BTA_ID_HH           23           /* Human Interface Device Host */

    看看 bta_hh_sm_execute的处理,当前的状态是BTA_HH_W4_CONN_ST ,

    /* BTA_HH_GATT_OPEN_EVT    */    ,{BTA_HH_GATT_OPEN,     BTA_HH_W4_CONN_ST }

    下一个状态还是BTA_HH_W4_CONN_ST ,执行的action 是BTA_HH_GATT_OPEN,我们看看其实现:

    /*******************************************************************************
    **
    ** Function         bta_hh_gatt_open
    **
    ** Description      process GATT open event.
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_gatt_open(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf)
    {
        tBTA_GATTC_OPEN *p_data = &p_buf->le_open;
        UINT8           *p2;
        tHID_STATUS     status = BTA_HH_ERR;
    
        /* if received invalid callback data , ignore it */
        if (p_cb == NULL || p_data == NULL)
            return;
    
        p2 = p_data->remote_bda;
    
        if (p_data->status == BTA_GATT_OK)
        {
            p_cb->is_le_device  = TRUE;
            p_cb->in_use    = TRUE;
            p_cb->conn_id   = p_data->conn_id;
            p_cb->hid_handle = BTA_HH_GET_LE_DEV_HDL(p_cb->index);
    
            bta_hh_cb.le_cb_index[BTA_HH_GET_LE_CB_IDX(p_cb->hid_handle)] = p_cb->index;
            bta_hh_sm_execute(p_cb, BTA_HH_START_ENC_EVT, NULL);//开始进行encryption相关动作
    
        }
        else /* open failure */
        {
            bta_hh_sm_execute(p_cb, BTA_HH_SDP_CMPL_EVT, (tBTA_HH_DATA *)&status);
        }
    
    }

     我们继续看:

    /* BTA_HH_START_ENC_EVT    */    ,{BTA_HH_START_SEC,     BTA_HH_W4_SEC     }

    下一个状态是  BTA_HH_W4_SEC,执行的操作是BTA_HH_START_SEC,我们继续看:

    /*******************************************************************************
    **
    ** Function         bta_hh_start_security
    **
    ** Description      start the security check of the established connection
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_start_security(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf)
    {
        UINT8           sec_flag=0;
        tBTM_SEC_DEV_REC  *p_dev_rec;
        UNUSED(p_buf);
    
        p_dev_rec = btm_find_dev(p_cb->addr);
    ...
        /* verify bond */
        BTM_GetSecurityFlagsByTransport(p_cb->addr, &sec_flag, BT_TRANSPORT_LE);//获取设备的sec flag
    
        /* if link has been encrypted */
        if (sec_flag & BTM_SEC_FLAG_ENCRYPTED)
        {
            bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL);//继续状态机
        }
        /* if bonded and link not encrypted */
        else if (sec_flag & BTM_SEC_FLAG_LKEY_KNOWN)
        {
    ...
        /* otherwise let it go through */
        else
        {
            bta_hh_sm_execute(p_cb, BTA_HH_ENC_CMPL_EVT, NULL);
        }
    
    
    }
     

    我们继续分析状态机流程:

    /* BTA_HH_ENC_CMPL_EVT     */     {BTA_HH_SEC_CMPL,      BTA_HH_W4_CONN_ST },

    新状态变成了BTA_HH_W4_CONN_ST,执行的action是BTA_HH_SEC_CMPL,我们看看bta_hh_security_cmpl 的具体实现:

    /*******************************************************************************
    **
    ** Function         bta_hh_security_cmpl
    **
    ** Description      Security check completed, start the service discovery
    **                  if no cache available, otherwise report connection open completed
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_security_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_buf)
    {
        tBTA_HH_RPT_CACHE_ENTRY     *p_rpt_cache;
        UINT8                       num_rpt = 0;
        UNUSED(p_buf);
    
        if (p_cb->status == BTA_HH_OK)
        {
            if (!p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use)
            {
                /* start loading the cache if not in stack */
                if ((p_rpt_cache = bta_hh_le_co_cache_load(p_cb->addr, &num_rpt, p_cb->app_id)) != NULL)
                {
                    bta_hh_process_cache_rpt(p_cb, p_rpt_cache, num_rpt);
                }
            }
            /*  discovery has been done for HID service */
            if (p_cb->app_id != 0 && p_cb->hid_srvc[BTA_HH_LE_SRVC_DEF].in_use)
            {
                /* configure protocol mode */
                if (bta_hh_le_set_protocol_mode(p_cb, p_cb->mode) == FALSE)
                {
                    APPL_TRACE_ERROR("bta_hh_security_cmpl");
                    bta_hh_le_open_cmpl(p_cb);
                }
            }
            /* start primary service discovery for HID service */
            else
            {
                bta_hh_le_pri_service_discovery(p_cb);//继续进行hid service的discovery
            }
        }
        else
        {
            ...
        }
    }

    下面进行的hid service 的搜索,也就是 hogp的范畴了:bta_hh_le_pri_service_discovery:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_pri_service_discovery
    **
    ** Description      Initialize GATT discovery on the remote LE HID device by opening
    **                  a GATT connection first.
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_pri_service_discovery(tBTA_HH_DEV_CB *p_cb)
    {
        tBT_UUID        pri_srvc;
    
        bta_hh_le_co_reset_rpt_cache(p_cb->addr, p_cb->app_id);
    
        p_cb->disc_active |= (BTA_HH_LE_DISC_HIDS|BTA_HH_LE_DISC_DIS);//这里是标志位,标志要搜索两个服务,当两个服务搜索完成,会调用bta_hh_le_open_cmpl 来上报状态
    
        /* read DIS info */
        if (!DIS_ReadDISInfo(p_cb->addr, bta_hh_le_dis_cback, DIS_ATTR_PNP_ID_BIT))//注意第三个参数,读的是PID、VID
        {
            APPL_TRACE_ERROR("read DIS failed");
            p_cb->disc_active &= ~BTA_HH_LE_DISC_DIS;
        }
    
        /* in parallel */
        /* start primary service discovery for HID service */
        pri_srvc.len        = LEN_UUID_16;
        pri_srvc.uu.uuid16  = UUID_SERVCLASS_LE_HID;
        BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc);//开始HID 的service的搜索
        return;
    }

    这里分为两个部分:

    1. DIS信息的搜索DIS_ReadDISInfo
    2. HID service 的信息的搜索。BTA_GATTC_ServiceSearchRequest

     我们先看 DIS 的信息的搜索:

    /*******************************************************************************
    **
    ** Function         DIS_ReadDISInfo
    **
    ** Description      Read remote device DIS information.
    **
    ** Returns          void
    **
    *******************************************************************************/
    BOOLEAN DIS_ReadDISInfo(BD_ADDR peer_bda, tDIS_READ_CBACK *p_cback, tDIS_ATTR_MASK mask)
    {
        UINT16             conn_id;
    
        /* Initialize the DIS client if it hasn't been initialized already. */
        srvc_eng_init();//对DIS client 进行初始化,并注册了srvc_eng_cb.gatt_if 到GATT
    ...
        dis_cb.p_read_dis_cback = p_cback;//保存callback = bta_hh_le_dis_cback
        /* Mark currently active operation */
        dis_cb.dis_read_uuid_idx = 0;
    
        dis_cb.request_mask = mask;
    
        GATT_GetConnIdIfConnected(srvc_eng_cb.gatt_if, peer_bda, &conn_id, BT_TRANSPORT_LE);
    
        /* need to enhance it as multiple service is needed */
        srvc_eng_request_channel(peer_bda, SRVC_ID_DIS);
    ...
        return dis_gatt_c_read_dis_req(conn_id);//读PID VID
    
    }

    我们继续看:

    /*******************************************************************************
    **
    ** Function         dis_gatt_c_read_dis_req
    **
    ** Description      Read remote device DIS attribute request.
    **
    ** Returns          void
    **
    *******************************************************************************/
    BOOLEAN dis_gatt_c_read_dis_req(UINT16 conn_id)
    {
        tGATT_READ_PARAM   param;
        memset(&param, 0, sizeof(tGATT_READ_PARAM));
        param.service.uuid.len       = LEN_UUID_16;
        param.service.s_handle       = 1;
        param.service.e_handle       = 0xFFFF;
        param.service.auth_req       = 0;
        while (dis_cb.dis_read_uuid_idx < DIS_MAX_CHAR_NUM)
        {
            if (dis_uuid_to_attr(dis_attr_uuid[dis_cb.dis_read_uuid_idx]) &
                    dis_cb.request_mask)//根据mask 来读相关的属性
            {
                 param.service.uuid.uu.uuid16 = dis_attr_uuid[dis_cb.dis_read_uuid_idx];
    
                 if (GATTC_Read(conn_id, GATT_READ_BY_TYPE, &param) == GATT_SUCCESS)//开始读
                     return TRUE;
    
                GATT_TRACE_ERROR ("Read DISInfo: 0x%04x GATT_Read Failed", param.service.uuid.uu.uuid16);
            }
    
            dis_cb.dis_read_uuid_idx++;
        }
    
        dis_gatt_c_read_dis_value_cmpl(conn_id);
    
        return(FALSE);
    }

    读写成功就直接返回true。

    这里简单看一下,对于DIS 结果的处理:

    gatt_process_read_by_type_rsp  -->(p_clcb->operation == GATTC_OPTYPE_READ && p_clcb->op_subtype == GATT_READ_BY_TYPE)-->gatt_end_operation(p_clcb, GATT_SUCCESS, (void *)p);--->(*p_cmpl_cb)(conn_id, op, status, &cb_data);(这里需要注意,因为注册这个eng的时候,没有注册discovery result或者discovery complete等接口,所以调用的是p_cmpl_cb,实际调用的函数就是srvc_eng_c_cmpl_cback),--->srvc_eng_c_cmpl_cback-->srvc_eng_c_cmpl_act[0]--->dis_c_cmpl_cback:

    /*******************************************************************************
    **
    ** Function         dis_c_cmpl_cback
    **
    ** Description      Client operation complete callback.
    **
    ** Returns          void
    **
    *******************************************************************************/
    void dis_c_cmpl_cback (tSRVC_CLCB *p_clcb, tGATTC_OPTYPE op,
                                  tGATT_STATUS status, tGATT_CL_COMPLETE *p_data)
    {
        UINT16      read_type = dis_attr_uuid[dis_cb.dis_read_uuid_idx];
        UINT8       *pp = NULL, *p_str;
        UINT16      conn_id = p_clcb->conn_id;
    ...
    case GATT_UUID_PNP_ID:
                    if (p_data->att_value.len == DIS_PNP_ID_SIZE)//保存pid vid
                    {
                        p_clcb->dis_value.attr_mask |= DIS_ATTR_PNP_ID_BIT;
                        STREAM_TO_UINT8 (p_clcb->dis_value.pnp_id.vendor_id_src, pp);
                        STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.vendor_id, pp);
                        STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.product_id, pp);
                        STREAM_TO_UINT16 (p_clcb->dis_value.pnp_id.product_version, pp);
                    }
                    break;
    ...
        dis_cb.dis_read_uuid_idx ++;
    
        dis_gatt_c_read_dis_req(conn_id);//继续搜索

    继续搜索的结果是 根据mask来判定的。如果当初mask只设定一个值,那么就会直接调用discovery 完成事件:

    /*******************************************************************************
    **
    ** Function         dis_gatt_c_read_dis_value_cmpl
    **
    ** Description      Client read DIS database complete callback.
    **
    ** Returns          void
    **
    *******************************************************************************/
    static void dis_gatt_c_read_dis_value_cmpl(UINT16 conn_id)
    {
        tSRVC_CLCB *p_clcb = srvc_eng_find_clcb_by_conn_id(conn_id);
    
        dis_cb.dis_read_uuid_idx = 0xff;
    
        srvc_eng_release_channel(conn_id);
    
        if (dis_cb.p_read_dis_cback && p_clcb)//调用进行搜索的时候(DIS_ReadDISInfo)注册的函数bta_hh_le_dis_cback
        {
            LOG_INFO("%s conn_id:%d attr_mask = 0x%04x", __func__, conn_id,
                    p_clcb->dis_value.attr_mask);
    
            (*dis_cb.p_read_dis_cback)(p_clcb->bda, &p_clcb->dis_value);
            dis_cb.p_read_dis_cback = NULL;
        }
    }

    下面我们 看看 第二点: 

    • HID service 的信息的搜索。BTA_GATTC_ServiceSearchRequest

    /*******************************************************************************
    **
    ** Function         BTA_GATTC_ServiceSearchRequest
    **
    ** Description      This function is called to request a GATT service discovery
    **                    on a GATT server. This function report service search result
    **                  by a callback event, and followed by a service search complete
    **                  event.
    **
    ** Parameters       conn_id: connection ID.
    **                  p_srvc_uuid: a UUID of the service application is interested in.
    **                              If Null, discover for all services.
    **
    ** Returns          None
    **
    *******************************************************************************/
    void BTA_GATTC_ServiceSearchRequest (UINT16 conn_id, tBT_UUID *p_srvc_uuid)
    {
        tBTA_GATTC_API_SEARCH  *p_buf;
        UINT16  len = sizeof(tBTA_GATTC_API_SEARCH) + sizeof(tBT_UUID);
    
        if ((p_buf = (tBTA_GATTC_API_SEARCH *) GKI_getbuf(len)) != NULL)
        {
            memset(p_buf, 0, len);
    
            p_buf->hdr.event = BTA_GATTC_API_SEARCH_EVT;//gatt search
            p_buf->hdr.layer_specific = conn_id;
    
            if (p_srvc_uuid)
            {
                p_buf->p_srvc_uuid = (tBT_UUID *)(p_buf + 1);
                memcpy(p_buf->p_srvc_uuid, p_srvc_uuid, sizeof(tBT_UUID));
            }
            else
                p_buf->p_srvc_uuid = NULL;
    
            bta_sys_sendmsg(p_buf);
        }
        return;
    }

     我们继续看,上面的流程就是触发”GATT service discovery“ ,那这里又进入了熟悉的状态机轮转,当前的状态机的状态是已经连接的状态:

    BOOLEAN bta_gattc_sm_execute(tBTA_GATTC_CLCB *p_clcb, UINT16 event, tBTA_GATTC_DATA *p_data)
    {
        tBTA_GATTC_ST_TBL     state_table;
        UINT8               action;
        int                 i;
        BOOLEAN             rt = TRUE;
    
        /* look up the state table for the current state */
        state_table = bta_gattc_st_tbl[p_clcb->state];
    
        event &= 0x00FF;
    
        /* set next state */
        p_clcb->state = state_table[event][BTA_GATTC_NEXT_STATE];
    
        /* execute action functions */
        for (i = 0; i < BTA_GATTC_ACTIONS; i++)
        {
            if ((action = state_table[event][i]) != BTA_GATTC_IGNORE)
            {
                (*bta_gattc_action[action])(p_clcb, p_data);
                if (p_clcb->p_q_cmd == p_data) {
                    /* buffer is queued, don't free in the bta dispatcher.
                     * we free it ourselves when a completion event is received.
                     */
                    rt = FALSE;
                }
            }
            else
            {
                break;
            }
        }
    
        return rt;
    }
    /* BTA_GATTC_API_SEARCH_EVT         */   {BTA_GATTC_SEARCH,             BTA_GATTC_CONN_ST},

    下一个状态 还是BTA_GATTC_CONN_ST ,执行的action 是BTA_GATTC_SEARCH:

    /*******************************************************************************
    **
    ** Function         bta_gattc_search
    **
    ** Description      start a search in the local server cache
    **
    ** Returns          None.
    **
    *******************************************************************************/
    void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
    {
        tBTA_GATT_STATUS    status = GATT_INTERNAL_ERROR;
        tBTA_GATTC cb_data;
        APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d",p_clcb->bta_conn_id);//注意这里的p_clcb是hogp 注册到GATT的接口
        if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache)
        {
            status = BTA_GATT_OK;
            /* search the local cache of a server device */
            bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);//这里的uuid = UUID_SERVCLASS_LE_HID 0x1812
        }
        cb_data.search_cmpl.status  = status;
        cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id;
    
        /* end of search or no server cache available */
        ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT,  &cb_data);
    }//bta_hh_gattc_callback

    search 这个接口,他实现的思想是先 搜索cache 里面有没有相关的服务,如果cache 存在,那么就会 调用回调(再GATT register注册的)进行上报,这里 是已经了cache,直接load,load完成之后还会通过BTA_GATTC_SEARCH_RES_EVT把结果(p_dev_cb->hid_srvc[idx].in_use = TRUE)保存在p_dev_cb。       我们直接看看 下面的回调函数的处理:

    /*******************************************************************************
    **
    ** Function         bta_hh_gattc_callback
    **
    ** Description      This is GATT client callback function used in BTA HH.
    **
    ** Parameters:
    **
    *******************************************************************************/
    static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
    {
        tBTA_HH_DEV_CB *p_dev_cb;
        UINT16          evt;
    ...
    case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */
                bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl);
                break;
    ...

    继续看bta_hh_le_srvc_search_cmpl:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_srvc_search_cmpl
    **
    ** Description      This function process the GATT service search complete.
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL *p_data)
    {
        tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id);
    ...
        if(p_data->status != BTA_GATT_OK || p_dev_cb->total_srvc == 0)
        {
    ...
        }
        /* GATT service discovery sucessfully finished */
        else
        {
            if (p_dev_cb->disc_active  & BTA_HH_LE_DISC_SCPS)//active = 1+2 = DIS+HID
            {
                p_dev_cb->disc_active  &= ~BTA_HH_LE_DISC_SCPS;
                bta_hh_le_open_cmpl(p_dev_cb);
            }
            else /* discover HID service */
            {
            p_dev_cb->cur_srvc_index = 0;
            bta_hh_le_srvc_expl_srvc(p_dev_cb);
        }
    }
    }

    现在开始 真正的HID services 的搜索,这里因为已经load 了相关的LE 的pri service,所以接下来的搜索应该是直接对handle 进行搜索,我们继续看bta_hh_le_srvc_expl_srvc的实现:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_srvc_expl_srvc
    **
    ** Description      This function discover the next avaible HID service.
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb)
    {
        if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX &&
            p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use)
        {
            if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc)
                /* explore included service first */
                bta_hh_le_search_hid_included(p_dev_cb);//先进行include的搜索
            else
            {
                /* explore characterisc */
                p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0;
                bta_hh_le_search_hid_chars(p_dev_cb);
            }
        }
        else /* all service discvery finished */
        {
            bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status);
        }
    }

    最先进行的是include的服务的搜索,然后应该是 chars,这里发现,这个include 的搜索 就是搜索cache 里面是不是有相应的项目,分析代码发现,搜索的是电池服务UUID_SERVCLASS_BATTERY, 不管有没有搜索到,都会设置p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc 为true,然后继续调用bta_hh_le_srvc_expl_srvc进行搜索,那么接下来就是explore characterisc  的流程了bta_hh_le_search_hid_chars:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_search_hid_chars
    **
    ** Description      This function discover all characteristics a service and
    **                  all descriptors available.
    **
    ** Parameters:
    **
    *******************************************************************************/
    static void bta_hh_le_search_hid_chars(tBTA_HH_DEV_CB *p_dev_cb)
    {
        tBT_UUID    char_cond;
        tBTA_GATTC_CHAR_ID  char_result;
        tBTA_GATT_CHAR_PROP prop;
        BOOLEAN     next = TRUE;
        UINT16      char_uuid = 0;
        tBTA_GATT_SRVC_ID srvc_id;
    
        if (p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx == BTA_HH_LE_DISC_CHAR_NUM ||
            (p_dev_cb->status != BTA_HH_OK && p_dev_cb->status != BTA_HH_ERR_PROTO))
        {
            p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0;
            /* explore next service */
            p_dev_cb->cur_srvc_index ++;
            bta_hh_le_srvc_expl_srvc(p_dev_cb);//搜索完成,继续调用此函数,进入到
            return;
        }
    
        p_dev_cb->hid_srvc[ p_dev_cb->cur_srvc_index].cur_expl_char_idx ++;
        char_uuid = bta_hh_le_disc_char_uuid[p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx - 1];//总共有8个chars 等待搜索
    
        char_cond.len = LEN_UUID_16;
        char_cond.uu.uuid16 = char_uuid;
    
        bta_hh_le_fill_16bits_srvc_id(TRUE, p_dev_cb->cur_srvc_index, UUID_SERVCLASS_LE_HID, &srvc_id);
    
        if (BTA_GATTC_GetFirstChar( p_dev_cb->conn_id,
                                &srvc_id,
                                &char_cond,
                                &char_result,
                                &prop) == BTA_GATT_OK)//查找 相应的UUID
        {
            switch (char_uuid)
            {
            case GATT_UUID_HID_CONTROL_POINT:
                p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_CP_BIT;
                next = TRUE;
                break;
            case GATT_UUID_HID_INFORMATION:
            case GATT_UUID_HID_REPORT_MAP:
                /* read the char value */
                BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id,
                                            &char_result,
                                            BTA_GATT_AUTH_REQ_NONE);//对于相应的uuid 进行搜索,并且涉及到uuid2handle的转换
                next = FALSE;
                break;
    
            case GATT_UUID_HID_PROTO_MODE:
                p_dev_cb->hid_srvc[char_result.srvc_id.id.inst_id].option_char |= BTA_HH_LE_PROTO_MODE_BIT;
                next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode);
                break;
    
            case GATT_UUID_HID_REPORT:
    #ifdef BLUETOOTH_RTK_BQB
                if(cert_conf_mask&BLUETOOTH_RTK_BQB_PATCH_HOGP)
                {
                    APPL_TRACE_ERROR("BQB:this is seen in hogp bqb test");
                    BTA_GATTC_ReadCharacteristic(p_dev_cb->conn_id,
                        &char_result,
                        BTA_GATT_AUTH_REQ_NONE);
                    memcpy(&(p_dev_cb->bqb_report_char_result), &char_result, sizeof(tBTA_GATTC_CHAR_ID));
                    memcpy(&(p_dev_cb->bqb_report_char_cond), &char_cond, sizeof(tBT_UUID));
                    p_dev_cb->bqb_report_prop = prop;
                }
                else
    #endif
                bta_hh_le_expl_rpt(p_dev_cb, &char_result, &char_cond, prop);
                next = FALSE;
                break;
    
            /* found boot mode report types */
            case GATT_UUID_HID_BT_KB_OUTPUT:
            case GATT_UUID_HID_BT_MOUSE_INPUT:
            case GATT_UUID_HID_BT_KB_INPUT:
                bta_hh_le_expl_boot_rpt(p_dev_cb, char_uuid, prop);
                break;
            }
        }
        else
        {
            if (char_uuid == GATT_UUID_HID_PROTO_MODE)
                next = !bta_hh_le_set_protocol_mode(p_dev_cb, p_dev_cb->mode);
    
        }
    
        if (next == TRUE)
        {
            bta_hh_le_search_hid_chars(p_dev_cb);
        }
    }

    从上面我们发现对于不同的UUID,往往有不同的函数去处理,当所有的char 都被搜索完成了以后,就又会执行bta_hh_le_srvc_expl_srvc 来结束这个流程。

    这里先看下 我们上面搜索的有哪些UUID:

    static const UINT16 bta_hh_le_disc_char_uuid[BTA_HH_LE_DISC_CHAR_NUM] =
    {
        GATT_UUID_HID_INFORMATION,
        GATT_UUID_HID_REPORT_MAP,
        GATT_UUID_HID_CONTROL_POINT,
        GATT_UUID_HID_REPORT,
        GATT_UUID_HID_BT_KB_INPUT,
        GATT_UUID_HID_BT_KB_OUTPUT,
        GATT_UUID_HID_BT_MOUSE_INPUT,
        GATT_UUID_HID_PROTO_MODE        /* always make sure this is the last attribute to discover */
    }

    我们再次看一下bta_hh_le_srvc_expl_srvc:

    void bta_hh_le_srvc_expl_srvc(tBTA_HH_DEV_CB *p_dev_cb)
    {
    #if BTA_HH_DEBUG == TRUE
        APPL_TRACE_DEBUG("bta_hh_le_srvc_expl_srvc cur_srvc_index = %d in_use = %d",
                        p_dev_cb->cur_srvc_index,
                        p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use);
    #endif
    
        if (p_dev_cb->cur_srvc_index < BTA_HH_LE_HID_SRVC_MAX &&
            p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].in_use)//
        {
            if (!p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].expl_incl_srvc)
                /* explore included service first */
                bta_hh_le_search_hid_included(p_dev_cb);
            else
            {
                /* explore characterisc */
                p_dev_cb->hid_srvc[p_dev_cb->cur_srvc_index].cur_expl_char_idx = 0;
                bta_hh_le_search_hid_chars(p_dev_cb);
            }
        }
        else /* all service discvery finished */
        {
            bta_hh_le_gatt_disc_cmpl(p_dev_cb, p_dev_cb->status);//所有的搜索已经完成
        }
    }

    我们继续看bta_hh_le_gatt_disc_cmpl:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_gatt_disc_cmpl
    **
    ** Description      Check to see if the remote device is a LE only device
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_gatt_disc_cmpl(tBTA_HH_DEV_CB *p_cb, tBTA_HH_STATUS status)
    {
        /* if open sucessful or protocol mode not desired, keep the connection open but inform app */
        if (status == BTA_HH_OK || status == BTA_HH_ERR_PROTO)
        {
            /* assign a special APP ID temp, since device type unknown */
            p_cb->app_id = BTA_HH_APP_ID_LE;
    
            /* set report notification configuration */
            p_cb->clt_cfg_idx = 0;
            bta_hh_le_write_rpt_clt_cfg(p_cb, BTA_HH_LE_SRVC_DEF);//开始做report的配置工作
        }
        else /* error, close the GATT connection */
        {
            /* close GATT connection if it's on */
            bta_hh_le_api_disc_act(p_cb);
        }
    }

     上面的配置的工作就是把所有的input 的属性,都配置成notification 的形式。

    这里关于hogp的服务搜索基本完成,下面 再说一下 每次配置属性之后进行的回调的流程:

    每次一个行为配置完成都会调用:bta_gattc_op_cmpl:

    /*******************************************************************************
    **
    ** Function         bta_gattc_op_cmpl
    **
    ** Description      operation completed.
    **
    ** Returns          None.
    **
    *******************************************************************************/
    void  bta_gattc_op_cmpl(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
    {
        UINT8           op = (UINT8)p_data->op_cmpl.op_code;
        UINT8           mapped_op = 0;
    ...
            else if (op == GATTC_OPTYPE_WRITE)
                bta_gattc_write_cmpl(p_clcb, &p_data->op_cmpl);
    ...

    我们看看bta_gattc_write_cmpl的实现:

      ....
            event = BTA_GATTC_WRITE_DESCR_EVT;
    
        utl_freebuf((void **)&p_clcb->p_q_cmd);
        cb_data.write.conn_id = p_clcb->bta_conn_id;
        /* write complete, callback */
        ( *p_clcb->p_rcb->p_cback)(event, (tBTA_GATTC *)&cb_data);
    ...

    调用注册的时候的回调:bta_hh_gattc_callback:

        ...
        case BTA_GATTC_WRITE_DESCR_EVT: /* 9 */
            case BTA_GATTC_WRITE_CHAR_EVT: /* 4 */
                p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->write.conn_id);
                if (event == BTA_GATTC_WRITE_CHAR_EVT)
                    evt = BTA_HH_GATT_WRITE_CHAR_CMPL_EVT;
                else
                    evt = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT;
    
                bta_hh_sm_execute(p_dev_cb, evt, (tBTA_HH_DATA *)&p_data->write);
                break;

     这里注意,此时的bta_hh的状态应是BTA_HH_W4_CONN_ST ,我们看其状态机的转换:event = BTA_HH_GATT_WRITE_DESCR_CMPL_EVT

    /* WRITE_DESCR_CMPL_EVT */       ,{BTA_HH_WRITE_DESCR,   BTA_HH_W4_CONN_ST   }

    下一个状态不变,执行的函数是:bta_hh_le_write_char_descr_cmpl:该函数的实现套路还是一样的,就是继续之前的行为,如果是已经搜索完成,那么会有回调向上:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_write_char_descr_cmpl
    **
    ** Description      Write charactersitic descriptor complete event
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_write_char_descr_cmpl(tBTA_HH_DEV_CB *p_dev_cb, tBTA_HH_DATA *p_buf)
    {
        tBTA_GATTC_WRITE    *p_data = (tBTA_GATTC_WRITE *)p_buf;
        UINT8   srvc_inst_id, hid_inst_id;
    
        /* only write client configuration possible */
        if (p_data->descr_type.uuid.uu.uuid16 == GATT_UUID_CHAR_CLIENT_CONFIG)
        {
            srvc_inst_id = p_data->srvc_id.id.inst_id;
            hid_inst_id = srvc_inst_id;
            switch (p_data->char_id.uuid.uu.uuid16)
            {
            case GATT_UUID_BATTERY_LEVEL: /* battery level clt cfg registered */
                hid_inst_id = bta_hh_le_find_service_inst_by_battery_inst_id(p_dev_cb, srvc_inst_id);
                /* fall through */
            case GATT_UUID_HID_BT_KB_INPUT:
            case GATT_UUID_HID_BT_MOUSE_INPUT:
            case GATT_UUID_HID_REPORT:
                if (p_data->status == BTA_GATT_OK)
                    p_dev_cb->hid_srvc[hid_inst_id].report[p_dev_cb->clt_cfg_idx].client_cfg_value =
                            BTA_GATT_CLT_CONFIG_NOTIFICATION;
                p_dev_cb->clt_cfg_idx ++;
                bta_hh_le_write_rpt_clt_cfg(p_dev_cb, hid_inst_id);//继续write
    
                break;

    我们继续分析:在bta_hh_le_write_rpt_clt_cfg 这个函数里面 ,肯定要发生”质变“:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_write_rpt_clt_cfg
    **
    ** Description      write client configuration. This is only for input report
    **                  enable all input notification upon connection open.
    **
    *******************************************************************************/
    BOOLEAN bta_hh_le_write_rpt_clt_cfg(tBTA_HH_DEV_CB *p_cb, UINT8 srvc_inst_id)
    {
        UINT8           i;
        tBTA_HH_LE_RPT  *p_rpt = &p_cb->hid_srvc[srvc_inst_id].report[p_cb->clt_cfg_idx];
        UINT16          srvc_uuid;
    
        for (i = p_cb->clt_cfg_idx; i < BTA_HH_LE_RPT_MAX && p_rpt->in_use; i ++, p_rpt ++)
        {
            /* enable notification for all input report, regardless mode */
            if (p_rpt->rpt_type == BTA_HH_RPTT_INPUT)
    
            {
                if (p_rpt->uuid == GATT_UUID_BATTERY_LEVEL)
                    srvc_uuid = UUID_SERVCLASS_BATTERY;
                else
                    srvc_uuid = UUID_SERVCLASS_LE_HID;
    
                if (bta_hh_le_write_char_clt_cfg(p_cb,
                                                 BTA_HH_LE_RPT_GET_SRVC_INST_ID(p_rpt->inst_id),
                                                 srvc_uuid,
                                                 BTA_HH_LE_RPT_GET_RPT_INST_ID(p_rpt->inst_id),
                                                 p_rpt->uuid,
                                                 BTA_GATT_CLT_CONFIG_NOTIFICATION))
                {
                    p_cb->clt_cfg_idx = i;
                    return TRUE;
                }
            }
    
        }
        p_cb->clt_cfg_idx = 0;//执行到这里说明,write 行为已经全部结束
    
        /* client configuration is completed, send open callback */
        if (p_cb->state == BTA_HH_W4_CONN_ST)//此时bta_hh 正是BTA_HH_W4_CONN_ST
        {
            p_cb->disc_active &= ~BTA_HH_LE_DISC_HIDS;//清掉 这个标志,也意味着hid service流程完成
    
            /* discover scan parameter profile is act as report host */
            bta_hh_le_search_scps(p_cb);//进行scan parameter service 流程
        }
        return FALSE;
    }

    上面 的函数已经注释,我们继续看bta_hh_le_search_scps:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_search_scps
    **
    ** Description      discovery scan parameter service if act as report host, otherwise
    **                  finish LE connection.
    **
    ** Parameters:
    **
    *******************************************************************************/
    static void bta_hh_le_search_scps(tBTA_HH_DEV_CB *p_cb)
    {
        tBT_UUID        pri_srvc;
    
        if ( p_cb->mode == BTA_HH_PROTO_RPT_MODE)//猜测:搜索完成会设置这个标志位
        {
            p_cb->disc_active  |= BTA_HH_LE_DISC_SCPS;//设置此标志位
            /* start  service discovery for Scan Parameter service */
            pri_srvc.len        = LEN_UUID_16;
            pri_srvc.uu.uuid16  = UUID_SERVCLASS_SCAN_PARAM;
    
            BTA_GATTC_ServiceSearchRequest(p_cb->conn_id, &pri_srvc);
        }//开始搜索
        else
            bta_hh_le_open_cmpl(p_cb);
    }

    继续看:会发出0x1f09 继续搜索:

    void BTA_GATTC_ServiceSearchRequest (UINT16 conn_id, tBT_UUID *p_srvc_uuid)
    {
        tBTA_GATTC_API_SEARCH  *p_buf;
        UINT16  len = sizeof(tBTA_GATTC_API_SEARCH) + sizeof(tBT_UUID);
    
        if ((p_buf = (tBTA_GATTC_API_SEARCH *) GKI_getbuf(len)) != NULL)
        {
            memset(p_buf, 0, len);
    
            p_buf->hdr.event = BTA_GATTC_API_SEARCH_EVT;
            p_buf->hdr.layer_specific = conn_id;
    
            if (p_srvc_uuid)
            {
                p_buf->p_srvc_uuid = (tBT_UUID *)(p_buf + 1);
                memcpy(p_buf->p_srvc_uuid, p_srvc_uuid, sizeof(tBT_UUID));
            }
            else
                p_buf->p_srvc_uuid = NULL;
    
            bta_sys_sendmsg(p_buf);
        }
        return;
    }

    继续执行bta_gattc_search:

    void bta_gattc_search(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
    {
        tBTA_GATT_STATUS    status = GATT_INTERNAL_ERROR;
        tBTA_GATTC cb_data;
        APPL_TRACE_DEBUG("bta_gattc_search conn_id=%d",p_clcb->bta_conn_id);
        if (p_clcb->p_srcb && p_clcb->p_srcb->p_srvc_cache)
        {
            status = BTA_GATT_OK;
            /* search the local cache of a server device */
            bta_gattc_search_service(p_clcb, p_data->api_search.p_srvc_uuid);//seach local cache
        }
        cb_data.search_cmpl.status  = status;
        cb_data.search_cmpl.conn_id = p_clcb->bta_conn_id;
    
        /* end of search or no server cache available */
        ( *p_clcb->p_rcb->p_cback)(BTA_GATTC_SEARCH_CMPL_EVT,  &cb_data);//不管结果,直接上报BTA_GATTC_SEARCH_CMPL_EVT
    }

    这里会对cache 里面去搜索,这个看看btsnoop 发现其是可以搜索到的:通过btsnoop,我们发现从handle 74-79 就是scan parameter的相关属性。

    下面我们看看回调函数 对于BTA_GATTC_SEARCH_CMPL_EVT的处理:

     static void bta_hh_gattc_callback(tBTA_GATTC_EVT event, tBTA_GATTC *p_data)
    {
        tBTA_HH_DEV_CB *p_dev_cb;
        UINT16          evt; 
    ...
       case BTA_GATTC_SEARCH_CMPL_EVT: /* 6 */
                bta_hh_le_srvc_search_cmpl(&p_data->search_cmpl);
                break;
    ...

    继续看:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_srvc_search_cmpl
    **
    ** Description      This function process the GATT service search complete.
    **
    ** Parameters:
    **
    *******************************************************************************/
    void bta_hh_le_srvc_search_cmpl(tBTA_GATTC_SEARCH_CMPL *p_data)
    {
        tBTA_HH_DEV_CB *p_dev_cb = bta_hh_le_find_dev_cb_by_conn_id(p_data->conn_id);
    
        if(p_data->status != BTA_GATT_OK || p_dev_cb->total_srvc == 0)
        {
    ...
        }
        /* GATT service discovery sucessfully finished */
        else
        {
            if (p_dev_cb->disc_active  & BTA_HH_LE_DISC_SCPS)
            {
                p_dev_cb->disc_active  &= ~BTA_HH_LE_DISC_SCPS;//清掉scps标志
                bta_hh_le_open_cmpl(p_dev_cb);//上报hh open event 
            }
            else /* discover HID service */
            {
            p_dev_cb->cur_srvc_index = 0;
            bta_hh_le_srvc_expl_srvc(p_dev_cb);
        }
    }
    }

     调用bta_hh_le_open_cmpl 上报状态,汇报”HID over GATT connection sucessfully opened“,我们继续分析:

    /*******************************************************************************
    **
    ** Function         bta_hh_le_open_cmpl
    **
    ** Description      HID over GATT connection sucessfully opened
    **
    *******************************************************************************/
    void bta_hh_le_open_cmpl(tBTA_HH_DEV_CB *p_cb)
    {
        if ( p_cb->disc_active == BTA_HH_LE_DISC_NONE)
        {
            bta_hh_le_register_input_notif(p_cb, 0, p_cb->mode, TRUE);//注册input notification 
            bta_hh_sm_execute(p_cb, BTA_HH_OPEN_CMPL_EVT, NULL);//状态机继续轮转
    
    #if (BTA_HH_LE_RECONN == TRUE)
            if (p_cb->status == BTA_HH_OK)
            {
                bta_hh_le_add_dev_bg_conn(p_cb, TRUE);//加入到bg conn,用于回连
            }
    #endif
        }
    }

    关于注册input notification  的部分,这里就不细讲了,我们看看 状态机的转换:

    void bta_hh_sm_execute(tBTA_HH_DEV_CB *p_cb, UINT16 event, tBTA_HH_DATA * p_data)
    {
        tBTA_HH_ST_TBL  state_table;
        UINT8           action;
        tBTA_HH         cback_data;
        tBTA_HH_EVT     cback_event = 0;
    #if BTA_HH_DEBUG == TRUE
        tBTA_HH_STATE   in_state ;
        UINT16          debug_event = event;
    #endif
    
        memset(&cback_data, 0, sizeof(tBTA_HH));
    ...
    state_table = bta_hh_st_tbl[p_cb->state - 1];
    
            event &= 0xff;
    
            p_cb->state = state_table[event][BTA_HH_NEXT_STATE] ;
    
            if ((action = state_table[event][BTA_HH_ACTION]) != BTA_HH_IGNORE)
            {
                (*bta_hh_action[action])(p_cb, p_data);
            }
    ...

    可以看到,下一个状态是 connection 状态:

    /* BTA_HH_OPEN_CMPL_EVT     */    {BTA_HH_OPEN_CMPL_ACT, BTA_HH_CONN_ST    }

    执行的行为是:BTA_HH_OPEN_CMPL_ACT:

    /*******************************************************************************
    **
    ** Function         bta_hh_open_cmpl_act
    **
    ** Description      HID host connection completed
    **
    **
    ** Returns          void
    **
    *******************************************************************************/
    void bta_hh_open_cmpl_act(tBTA_HH_DEV_CB *p_cb, tBTA_HH_DATA *p_data)
    {
        tBTA_HH_CONN        conn ;
        UINT8   dev_handle = p_data ? (UINT8)p_data->hid_cback.hdr.layer_specific : 
                            p_cb->hid_handle;
    
        memset((void *)&conn, 0, sizeof (tBTA_HH_CONN));
        conn.handle = dev_handle;
        bdcpy(conn.bda, p_cb->addr);
    
        /* increase connection number */
        bta_hh_cb.cnt_num ++;
    
        /* initialize device driver */
        bta_hh_co_open(p_cb->hid_handle, p_cb->sub_class,
                           p_cb->attr_mask,  p_cb->app_id);//打开uhid驱动,并且初始化线程btif_hh_poll_event_thread
    
        conn.status = p_cb->status;
        conn.le_hid = p_cb->is_le_device;
        conn.scps_supported = p_cb->scps_supported;
    
        /* set protocol mode when not default report mode */
        if ( p_cb->mode != BTA_HH_PROTO_RPT_MODE
             && !p_cb->is_le_device)
        {
    ...
        }
        else
            (* bta_hh_cb.p_cback)(BTA_HH_OPEN_EVT, (tBTA_HH *)&conn);//继续上报bta_hh_cb.p_cback = bte_hh_evt
    
        p_cb->incoming_conn = FALSE;
        p_cb->incoming_hid_handle = BTA_HH_INVALID_HANDLE;
    
    }

    初始化驱动的部分,这里不讲了,,我们继续看看 状态上报的流程:callback 实际执行流程是btif_hh_upstreams_evt,我们继续分析:

    static void btif_hh_upstreams_evt(UINT16 event, char* p_param)
    {
        tBTA_HH *p_data = (tBTA_HH *)p_param;
        btif_hh_device_t *p_dev = NULL;
        int i;
        int len, tmplen;
    ...
     case BTA_HH_OPEN_EVT:
                if (p_data->conn.status == BTA_HH_OK) {
                    p_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle);
                    if (p_dev == NULL) {
    ...
                    }
                    else if (p_dev->fd < 0) {
    ...
                    }
                    else {
    btif_hh_cb.status = BTIF_HH_DEV_CONNECTED;
                        // Send set_idle if the peer_device is a keyboard
                        if (check_cod((bt_bdaddr_t*)p_data->conn.bda, COD_HID_KEYBOARD )||
                                    check_cod((bt_bdaddr_t*)p_data->conn.bda, COD_HID_COMBO))
                            BTA_HhSetIdle(p_data->conn.handle, 0);
                        btif_hh_cb.p_curr_dev = btif_hh_find_connected_dev_by_handle(p_data->conn.handle);
                        BTA_HhGetDscpInfo(p_data->conn.handle);//这里涉及到将despinfo 发送到kernel,逻辑简单这里不做具体分析
                        p_dev->dev_status = BTHH_CONN_STATE_CONNECTED;
    
       HAL_CBACK(bt_hh_callbacks, connection_state_cb,&(p_dev->bd_addr), p_dev->dev_status);//上报到JNI 层面
                    

    上面的流程主要就是 上报状态,最后会上报到JNI 层面,最后肯定会通知到framework。

    hogp的流程分析到这里就结束了。


  • 相关阅读:
    java基础-集合笔记
    Spring工具类
    XStream的基本使用
    java之路径问题
    Vue 动态组件渲染问题分析
    watch案例解析(element-ui el-select 无法选中问题剖析)
    v-if案例解析(element-ui form-item 结合 v-if 动态生成rule规则表单元素,表单无法验证问题剖析 )
    Vue 虚拟Dom 及 部分生命周期初探
    Android仿苹果版QQ下拉刷新实现(二) ——贝塞尔曲线开发"鼻涕"下拉粘连效果
    AngularJs(SPA)单页面SEO以及百度统计应用(下)
  • 原文地址:https://www.cnblogs.com/libs-liu/p/9382860.html
Copyright © 2011-2022 走看看