Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android藍牙源碼分析——Gatt寫設備

Android藍牙源碼分析——Gatt寫設備

編輯:關於Android編程

BluetoothGatt中的writeCharacteristic的實現在GattService中,如下:

void writeCharacteristic(int clientIf, String address, int handle, int writeType, int authReq, byte[] value) {
    gattClientWriteCharacteristicNative(connId, handle, writeType, authReq, value);
}

這個gattClientWriteCharacteristicNative的實現在com_android_bluetooth_gatt.cpp中,

static void gattClientWriteCharacteristicNative(JNIEnv* env, jobject object,
    jint conn_id, jint handle, jint write_type, jint auth_req, jbyteArray value) {
    ......
    sGattIf->client->write_characteristic(conn_id, handle, write_type, auth_req,
                                          std::move(vect_val));
}

這個sGattIf的client是定義在btif_gatt_client.c中的btgattClientInterface,這裡調到了btif_gattc_write_char函數,

static bt_status_t btif_gattc_write_char(int conn_id, btgatt_srvc_id_t* srvc_id,
                                         btgatt_gatt_id_t* char_id, int write_type,
                                         int len, int auth_req, char* p_value)
{
    btif_gattc_cb_t btif_cb;
    btif_cb.conn_id = (uint16_t) conn_id;
    btif_cb.auth_req = (uint8_t) auth_req;
    btif_cb.write_type = (uint8_t) write_type;
    btif_cb.len = len > BTGATT_MAX_ATTR_LEN ? BTGATT_MAX_ATTR_LEN : len;
    memcpy(&btif_cb.srvc_id, srvc_id, sizeof(btgatt_srvc_id_t));
    memcpy(&btif_cb.char_id, char_id, sizeof(btgatt_gatt_id_t));
    memcpy(btif_cb.value, p_value, btif_cb.len);
    return btif_transfer_context(btgattc_handle_event, BTIF_GATTC_WRITE_CHAR,
                                 (char*) &btif_cb, sizeof(btif_gattc_cb_t), NULL);
}

這裡發送到btif task中,由btgattc_handle_event處理,事件為BTIF_GATTC_WRITE_CHAR,如下:

case BTIF_GATTC_WRITE_CHAR:
    btif_to_bta_srvc_id(&in_char_id.srvc_id, &p_cb->srvc_id);
    btif_to_bta_gatt_id(&in_char_id.char_id, &p_cb->char_id);

    BTA_GATTC_WriteCharValue(p_cb->conn_id, &in_char_id,
                             p_cb->write_type,
                             p_cb->len,
                             p_cb->value,
                             p_cb->auth_req);
    break;

再來看看BTA_GATTC_WriteCharValue的實現,如下:

void BTA_GATTC_WriteCharValue ( UINT16 conn_id,
                                tBTA_GATTC_CHAR_ID *p_char_id,
                                tBTA_GATTC_WRITE_TYPE  write_type,
                                UINT16 len,
                                UINT8 *p_value,
                                tBTA_GATT_AUTH_REQ auth_req)
{
    tBTA_GATTC_API_WRITE  *p_buf;

    if ((p_buf = (tBTA_GATTC_API_WRITE *) GKI_getbuf((UINT16)(sizeof(tBTA_GATTC_API_WRITE) + len))) != NULL)
    {
        memset(p_buf, 0, sizeof(tBTA_GATTC_API_WRITE) + len);

        p_buf->hdr.event = BTA_GATTC_API_WRITE_EVT;
        p_buf->hdr.layer_specific = conn_id;
        p_buf->auth_req = auth_req;

        memcpy(&p_buf->srvc_id, &p_char_id->srvc_id, sizeof(tBTA_GATT_SRVC_ID));
        memcpy(&p_buf->char_id, &p_char_id->char_id, sizeof(tBTA_GATT_ID));

        p_buf->write_type = write_type;
        p_buf->len = len;

        if (p_value && len > 0)
        {
            p_buf->p_value = (UINT8 *)(p_buf + 1);
            memcpy(p_buf->p_value, p_value, len);
        }

        bta_sys_sendmsg(p_buf);
    }
    return;
}

這裡看來真正的寫是在btu_task中,這裡發送的事件為BTA_GATTC_API_WRITE_EVT。如下:

enum
{
    BTA_GATTC_API_OPEN_EVT   = BTA_SYS_EVT_START(BTA_ID_GATTC),
    BTA_GATTC_INT_OPEN_FAIL_EVT,
    BTA_GATTC_API_CANCEL_OPEN_EVT,
    BTA_GATTC_INT_CANCEL_OPEN_OK_EVT,

    BTA_GATTC_API_READ_EVT,
    BTA_GATTC_API_WRITE_EVT,
    ......
};

可見這些事件都屬於BTA_ID_GATTC的子系統,所以在btu_task中的事件處理函數為bta_gattc_main.c中的bta_gattc_hdl_event。奇怪的是在這個函數中沒找到這個事件的處理分支,而是走到了默認處理邏輯中,如下:

tBTA_GATTC_CLCB *p_clcb = bta_gattc_find_clcb_by_conn_id(p_msg->layer_specific);

if (p_clcb != NULL)
{
    rt = bta_gattc_sm_execute(p_clcb, p_msg->event, (tBTA_GATTC_DATA *) p_msg);
}

這裡的意思是先通過layer_specific找到p_clcb,再進狀態機。這個layer_specific其實就是connection id,是clientIf和address生成的一個連接id。在我們write character之前已經初始化gatt過了,所以這裡肯定注冊過對應的clcb,我們直接進入狀態機好了,這個bta_gattc_sm_execute定義在bta_gattc_main.c中,如下:

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_st_tbl如下:

/* state table */
const tBTA_GATTC_ST_TBL bta_gattc_st_tbl[] =
{
    bta_gattc_st_idle,
    bta_gattc_st_w4_conn,
    bta_gattc_st_connected,
    bta_gattc_st_discover
};

對應的狀態為:

enum
{
    BTA_GATTC_IDLE_ST = 0,      /* Idle  */
    BTA_GATTC_W4_CONN_ST,       /* Wait for connection -  (optional) */
    BTA_GATTC_CONN_ST,          /* connected state */
    BTA_GATTC_DISCOVER_ST       /* discover is in progress */
};

假如當前狀態是已連接,那麼對應的狀態表為bta_gattc_st_connected,如下:

/* state table for open state */
static const UINT8 bta_gattc_st_connected[][BTA_GATTC_NUM_COLS] =
{
/* Event                            Action 1                            Next state */
/* BTA_GATTC_API_OPEN_EVT           */   {BTA_GATTC_OPEN,               BTA_GATTC_CONN_ST},
/* BTA_GATTC_INT_OPEN_FAIL_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_CANCEL_OPEN_EVT    */   {BTA_GATTC_CANCEL_OPEN_ERROR, BTA_GATTC_CONN_ST},
/* BTA_GATTC_INT_CANCEL_OPEN_OK_EVT */   {BTA_GATTC_IGNORE,            BTA_GATTC_CONN_ST},

/* BTA_GATTC_API_READ_EVT           */   {BTA_GATTC_READ,               BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_WRITE_EVT          */   {BTA_GATTC_WRITE,              BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_EXEC_EVT           */   {BTA_GATTC_EXEC,               BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_CFG_MTU_EVT        */   {BTA_GATTC_CFG_MTU,            BTA_GATTC_CONN_ST},

/* BTA_GATTC_API_CLOSE_EVT          */   {BTA_GATTC_CLOSE,              BTA_GATTC_IDLE_ST},

/* BTA_GATTC_API_SEARCH_EVT         */   {BTA_GATTC_SEARCH,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_CONFIRM_EVT        */   {BTA_GATTC_CONFIRM,            BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_READ_MULTI_EVT     */   {BTA_GATTC_READ_MULTI,         BTA_GATTC_CONN_ST},
/* BTA_GATTC_API_REFRESH_EVT        */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},

/* BTA_GATTC_INT_CONN_EVT           */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_INT_DISCOVER_EVT       */   {BTA_GATTC_START_DISCOVER,     BTA_GATTC_DISCOVER_ST},
/* BTA_GATTC_DISCOVER_CMPL_EVT       */  {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_OP_CMPL_EVT            */   {BTA_GATTC_OP_CMPL,            BTA_GATTC_CONN_ST},

/* BTA_GATTC_INT_DISCONN_EVT        */   {BTA_GATTC_CLOSE,              BTA_GATTC_IDLE_ST},

/* ===> for cache loading, saving   */
/* BTA_GATTC_START_CACHE_EVT        */   {BTA_GATTC_CACHE_OPEN,         BTA_GATTC_DISCOVER_ST},
/* BTA_GATTC_CI_CACHE_OPEN_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_CI_CACHE_LOAD_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST},
/* BTA_GATTC_CI_CACHE_SAVE_EVT      */   {BTA_GATTC_IGNORE,             BTA_GATTC_CONN_ST}
};

我們的事件是BTA_GATTC_API_WRITE_EVT,取低8位,為5,所以對應的是BTA_GATTC_WRITE,下一個狀態為BTA_GATTC_CONN_ST。我們看BTA_GATTC_WRITE對應的函數,到bta_gattc_action中查,在bta_gattc_main.c中:

/* action function list */
const tBTA_GATTC_ACTION bta_gattc_action[] =
{
    bta_gattc_open,
    bta_gattc_open_fail,
    bta_gattc_open_error,
    bta_gattc_cancel_open,
    bta_gattc_cancel_open_ok,
    bta_gattc_cancel_open_error,
    bta_gattc_conn,
    bta_gattc_start_discover,
    bta_gattc_disc_cmpl,

    bta_gattc_q_cmd,
    bta_gattc_close,
    bta_gattc_close_fail,
    bta_gattc_read,
    bta_gattc_write,

    ......
};

為bta_gattc_write函數,在bta_gattc_act.c中,如下:

void bta_gattc_write(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data)
{
    UINT16              handle = 0;
    tGATT_VALUE         attr = {0};
    tBTA_GATTC_OP_CMPL  op_cmpl;
    tBTA_GATT_STATUS    status = BTA_GATT_OK;

    if (bta_gattc_enqueue(p_clcb, p_data))
    {
        if ((handle = bta_gattc_id2handle(p_clcb->p_srcb,
                                          &p_data->api_write.srvc_id,
                                          &p_data->api_write.char_id,
                                          p_data->api_write.p_descr_type)) == 0)
        {
            status = BTA_GATT_ERROR;
        }
        else
        {
            attr.handle= handle;
            attr.offset = p_data->api_write.offset;
            attr.len    = p_data->api_write.len;
            attr.auth_req = p_data->api_write.auth_req;

            if (p_data->api_write.p_value)
                memcpy(attr.value, p_data->api_write.p_value, p_data->api_write.len);

            status = GATTC_Write(p_clcb->bta_conn_id, p_data->api_write.write_type, &attr);
        }

        /* write fail */
        if (status != BTA_GATT_OK)
        {
            memset(&op_cmpl, 0, sizeof(tBTA_GATTC_OP_CMPL));

            op_cmpl.status  = status;
            op_cmpl.op_code = GATTC_OPTYPE_WRITE;
            op_cmpl.p_cmpl  = NULL;

            bta_gattc_sm_execute(p_clcb, BTA_GATTC_OP_CMPL_EVT, (tBTA_GATTC_DATA *)&op_cmpl);
        }
    }
}

這裡首先調用bta_gattc_enqueue將請求放入隊列,如果成功就校驗要寫的character是否有效,如果有效,則調用GATTC_Write真正的寫了。

先看這個bta_gattc_enqueue的邏輯,如下:

BOOLEAN bta_gattc_enqueue(tBTA_GATTC_CLCB *p_clcb, tBTA_GATTC_DATA *p_data) {
    if (p_clcb->p_q_cmd == NULL) {
        p_clcb->p_q_cmd = p_data;
    } else {
        APPL_TRACE_ERROR("already has a pending command!!");
        /* skip the callback now. ----- need to send callback ? */
    }
    return (p_clcb->p_q_cmd != NULL) ? TRUE : FALSE;
}

這裡邏輯很簡單,不是什麼隊列,就是相當於一個單例,一次只能有一個命令在執行。如果之前的結果還沒完又來一個命令,則這裡直接返回了,連回調都收不到,所以我們所有GATT操作要串行化。

我們看GATTC_Write函數的實現,在gatt_api.c中,如下:

tGATT_STATUS GATTC_Write (UINT16 conn_id, tGATT_WRITE_TYPE type, tGATT_VALUE *p_write)
{
    tGATT_STATUS status = GATT_SUCCESS;
    tGATT_CLCB      *p_clcb;
    tGATT_VALUE     *p;
    tGATT_IF        gatt_if=GATT_GET_GATT_IF(conn_id);
    UINT8           tcb_idx = GATT_GET_TCB_IDX(conn_id);
    tGATT_TCB       *p_tcb = gatt_get_tcb_by_idx(tcb_idx);
    tGATT_REG       *p_reg = gatt_get_regcb(gatt_if);

    if ((p_clcb = gatt_clcb_alloc(conn_id)) != NULL )
    {
        p_clcb->operation  = GATTC_OPTYPE_WRITE;
        p_clcb->op_subtype = type;
        p_clcb->auth_req = p_write->auth_req;

        if (( p_clcb->p_attr_buf = (UINT8 *)GKI_getbuf((UINT16)sizeof(tGATT_VALUE))) != NULL)
        {
            memcpy(p_clcb->p_attr_buf, (void *)p_write, sizeof(tGATT_VALUE));

            p =  (tGATT_VALUE *)p_clcb->p_attr_buf;
            if (type == GATT_WRITE_PREPARE)
            {
                p_clcb->start_offset = p_write->offset;
                p->offset = 0;
            }

            if (gatt_security_check_start(p_clcb) == FALSE)
            {
                status = GATT_NO_RESOURCES;
            }
        }
        else
        {
            status = GATT_NO_RESOURCES;
        }

        if (status == GATT_NO_RESOURCES)
            gatt_clcb_dealloc(p_clcb);
    }
    else
    {
        status = GATT_NO_RESOURCES;
    }
    return status;
}

這裡出現了一堆錯誤GATT_NO_RESOURCES,值為128,平時貌似沒見過,所以我們這裡不關心,繼續看gatt_security_check_start,這裡主要是做安全檢查,檢查通過才會真正開始寫,在gatt_auth.c中,如下:

BOOLEAN gatt_security_check_start(tGATT_CLCB *p_clcb)
{
    tGATT_TCB           *p_tcb = p_clcb->p_tcb;
    tGATT_SEC_ACTION    gatt_sec_act;
    tBTM_BLE_SEC_ACT    btm_ble_sec_act;
    BOOLEAN             status = TRUE;
    tBTM_STATUS         btm_status;
    tGATT_SEC_ACTION    sec_act_old =  gatt_get_sec_act(p_tcb);

    gatt_sec_act = gatt_determine_sec_act(p_clcb);

    if (sec_act_old == GATT_SEC_NONE)
        gatt_set_sec_act(p_tcb, gatt_sec_act);

    switch (gatt_sec_act )
    {
        ......
        default:
            gatt_sec_check_complete(TRUE, p_clcb, gatt_sec_act);
            break;
    }

    if (status == FALSE)
    {
        gatt_set_sec_act(p_tcb, GATT_SEC_NONE);
        gatt_set_ch_state(p_tcb, GATT_CH_OPEN);
    }

    return status;
}

由於我們寫無需權限驗證,所以這裡gatt_determine_sec_act返回的就是GATT_SET_OK,所以走到了default分支的gatt_sec_check_complete,如下:

void gatt_sec_check_complete(BOOLEAN sec_check_ok, tGATT_CLCB   *p_clcb, UINT8 sec_act)
{
    if (p_clcb && p_clcb->p_tcb && GKI_queue_is_empty(&p_clcb->p_tcb->pending_enc_clcb))
        gatt_set_sec_act(p_clcb->p_tcb, GATT_SEC_NONE);

    if (!sec_check_ok)
    {
        gatt_end_operation(p_clcb, GATT_AUTH_FAIL, NULL);
    }
    else if (p_clcb->operation == GATTC_OPTYPE_WRITE)
    {
        gatt_act_write(p_clcb, sec_act);
    }
    else if (p_clcb->operation == GATTC_OPTYPE_READ)
    {
        gatt_act_read(p_clcb, p_clcb->counter);
    }
}

這裡終於准備寫了,走到的是gatt_act_write,在gatt_cl.c中,如下:

void gatt_act_write (tGATT_CLCB *p_clcb, UINT8 sec_act)
{
    tGATT_TCB           *p_tcb = p_clcb->p_tcb;
    UINT8               rt = GATT_SUCCESS, op_code = 0;
    tGATT_VALUE         *p_attr = (tGATT_VALUE *)p_clcb->p_attr_buf;

    if (p_attr)
    {
        switch (p_clcb->op_subtype)
        {
            ......

            case GATT_WRITE:
                if (p_attr->len <= (p_tcb->payload_size - GATT_HDR_SIZE))
                {
                    p_clcb->s_handle = p_attr->handle;

                    rt = gatt_send_write_msg(p_tcb,
                                             p_clcb->clcb_idx,
                                             GATT_REQ_WRITE,
                                             p_attr->handle,
                                             p_attr->len,
                                             0,
                                             p_attr->value);
                }
                else /* prepare write for long attribute */
                {
                    gatt_send_prepare_write(p_tcb, p_clcb);
                }
                break;

            ......

            default:
                rt = GATT_INTERNAL_ERROR;
                GATT_TRACE_ERROR("Unknown write type: %d", p_clcb->op_subtype);
                break;
        }
    }
    else
        rt = GATT_INTERNAL_ERROR;

    ......
}

這裡我們看普通寫,如果要寫的長度小,就直接走gatt_send_write_msg,否則走gatt_send_prepare_write,其實最後都是調的gatt_send_write_msg,如下:

UINT8 gatt_send_write_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code,
                           UINT16 handle, UINT16 len,
                           UINT16 offset, UINT8 *p_data)
{
    tGATT_CL_MSG     msg;

    msg.attr_value.handle = handle;
    msg.attr_value.len = len;
    msg.attr_value.offset = offset;

    memcpy (msg.attr_value.value, p_data, len);

    /* write by handle */
    return attp_send_cl_msg(p_tcb, clcb_idx, op_code, &msg);
}

這裡邏輯很簡單,繼續往下走,在att_protocol.c中:

tGATT_STATUS attp_send_cl_msg (tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 op_code, tGATT_CL_MSG *p_msg)
{
    tGATT_STATUS     status = GATT_NO_RESOURCES;
    BT_HDR          *p_cmd = NULL;
    UINT16          offset = 0, handle;

    if (p_tcb != NULL)
    {
        switch (op_code)
        {
        ......
        case GATT_REQ_PREPARE_WRITE:
            offset = p_msg->attr_value.offset;
            /* fall through */
        case GATT_REQ_WRITE:
        case GATT_CMD_WRITE:
        case GATT_SIGN_CMD_WRITE:
            if (GATT_HANDLE_IS_VALID (p_msg->attr_value.handle))
            {
                p_cmd = attp_build_value_cmd (p_tcb->payload_size,
                                              op_code, p_msg->attr_value.handle,
                                              offset,
                                              p_msg->attr_value.len,
                                              p_msg->attr_value.value);
            }
            else
                status = GATT_ILLEGAL_PARAMETER;
            break;
        ......

        default:
            break;
        }

        if (p_cmd != NULL)
            status = attp_cl_send_cmd(p_tcb, clcb_idx, op_code, p_cmd);

    }

    return status;
}

由於我們的op_code是GATT_REQ_WRITE,所以這裡先通過attp_build_value_cmd封裝好了cmd,然後調用attp_cl_send_cmd發送出去。之所以這麼做是因為這個任務也許不能馬上執行,所以需要丟到一個隊列中。這個cmd就相當於隊列中的一個task。我們看這個cmd是怎麼丟到隊列中的:

tGATT_STATUS attp_cl_send_cmd(tGATT_TCB *p_tcb, UINT16 clcb_idx, UINT8 cmd_code, BT_HDR *p_cmd)
{
    tGATT_STATUS att_ret = GATT_SUCCESS;

    if (p_tcb != NULL)
    {
        cmd_code &= ~GATT_AUTH_SIGN_MASK;

        /* no pending request or value confirmation */
        if (p_tcb->pending_cl_req == p_tcb->next_slot_inq ||
            cmd_code == GATT_HANDLE_VALUE_CONF)
        {
            att_ret = attp_send_msg_to_l2cap(p_tcb, p_cmd);
            if (att_ret == GATT_CONGESTED || att_ret == GATT_SUCCESS)
            {
                /* do not enq cmd if handle value confirmation or set request */
                if (cmd_code != GATT_HANDLE_VALUE_CONF && cmd_code != GATT_CMD_WRITE)
                {
                    gatt_start_rsp_timer (clcb_idx);
                    gatt_cmd_enq(p_tcb, clcb_idx, FALSE, cmd_code, NULL);
                }
            }
            else
                att_ret = GATT_INTERNAL_ERROR;
        }
        else
        {
            att_ret = GATT_CMD_STARTED;
            gatt_cmd_enq(p_tcb, clcb_idx, TRUE, cmd_code, p_cmd);
        }
    }
    else
        att_ret = GATT_ERROR;

    return att_ret;
}

這裡的意思是如果當前沒有別的待處理的請求的話就直接調用attp_send_msg_to_l2cap給請求丟到L2CAP層處理了,否則加到等待隊列中。如果是丟到L2CAP層的請求則還要通過gatt_start_rsp_timer 啟動一個定時器來檢查回調。

到這裡還有兩個問題沒有討論到,一個是L2CAP層怎麼處理寫設備請求的,另一個是GKI層定時器的處理邏輯,這些會留給下文。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved