編輯:關於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層定時器的處理邏輯,這些會留給下文。
前言: 前面使用的退出程序用的是finish(),它只能退出當前Activity。如果Activity一多就不能一次性退出了。 1、退出應用程
什麼是ServiceService是Android 的四大組件之一,主要處理一些耗時的後台操作邏輯,或者輪詢操作等需要長期在後台運行的任務。甚至在程序退出之後,可以讓Se
Context的方法getCacheDirgetFilesDirgetExternalCacheDirgetExternalFilesDir特點1:無需權限將assets
在上一篇文章裡,我總結了一下自定義控件需要了解的基礎知識:View的繪制流程——《自定義控件知識儲備-View的繪制流程》。其中,在View的測量