-
导语
该篇文章,主要介绍createBond操作在Android的调用流程,Android7.0为例。
1. 调用流程图
framework->HAL bluedroid stack内部2. 调用详解
- 具体的蓝牙应用APP下发createBond操作;
- 调用到framework层的createBond()方法,该方法通过AIDL进行进程间通信,继续调用;
@RequiresPermission(Manifest.permission.BLUETOOTH_ADMIN)
public boolean createBond() {
if (sService == null) {
Log.e(TAG, "BT not enabled. Cannot create bond to Remote Device");
return false;
}
try {
Log.i(TAG, "createBond() for device " + getAddress() +
" called by pid: " + Process.myPid() +
" tid: " + Process.myTid());
return sService.createBond(this, TRANSPORT_AUTO); //此处为跳转代码
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
private static IBluetooth sService; //IBluetooth AIDL定义
- BluetoothDevice.java中还有一个createBondOutOfBand()方法,该方法用于带外传输(非2.4GHz)。如通过NFC来实现蓝牙MAC和link key的交换,以达到“不用配对”的用户体验;
public boolean createBondOutOfBand(int transport, OobData oobData) {
try {
return sService.createBondOutOfBand(this, transport, oobData);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
- createBond()方法中的transport参数,0代表未知设备类型,1代表BR/EDR,2代表LE;
- createBond()会进入到AdapterService中的createBond()方法;
private static class AdapterServiceBinder extends IBluetooth.Stub {
boolean createBond(BluetoothDevice device, int transport, OobData oobData) {
enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
"Need BLUETOOTH ADMIN permission");
DeviceProperties deviceProp = mRemoteDevices.getDeviceProperties(device);
if (deviceProp != null && deviceProp.getBondState() != BluetoothDevice.BOND_NONE) {
return false;
}
// Pairing is unreliable while scanning, so cancel discovery
// Note, remove this when native stack improves
cancelDiscoveryNative(); //取消扫描操作
Message msg = mBondStateMachine.obtainMessage(BondStateMachine.CREATE_BOND);
msg.obj = device;
msg.arg1 = transport;
if (oobData != null) { //使用带外传输,完成绑定过程
Bundle oobDataBundle = new Bundle();
oobDataBundle.putParcelable(BondStateMachine.OOBDATA, oobData);
msg.setData(oobDataBundle);
}
mBondStateMachine.sendMessage(msg); //发送信息给状态机
return true;
}
- BondStateMachine接收到CREATE_BOND消息会会调用其内部的createBond方法;
private boolean createBond(BluetoothDevice dev, int transport, OobData oobData,
boolean transition) {
if (dev.getBondState() == BluetoothDevice.BOND_NONE) {
infoLog("Bond address is:" + dev);
byte[] addr = Utils.getBytesFromAddress(dev.getAddress());
boolean result;
if (oobData != null) {
result = mAdapterService.createBondOutOfBandNative(addr, transport, oobData);
} else {
result = mAdapterService.createBondNative(addr, transport); //JNI
}
if (!result) {
sendIntent(dev, BluetoothDevice.BOND_NONE,
BluetoothDevice.UNBOND_REASON_REMOVED);
return false;
} else if (transition) {
transitionTo(mPendingCommandState);
}
return true;
}
return false;
}
- 进行JNI调用,接着到createBondNative方法;
static jboolean createBondNative(JNIEnv* env, jobject obj, jbyteArray address, jint transport) {
ALOGV("%s:",__FUNCTION__);
jbyte *addr;
jboolean result = JNI_FALSE;
if (!sBluetoothInterface) return result;
addr = env->GetByteArrayElements(address, NULL);
if (addr == NULL) {
jniThrowIOException(env, EINVAL);
return result;
}
int ret = sBluetoothInterface->create_bond((bt_bdaddr_t *)addr, transport); //调用HAL层接口
env->ReleaseByteArrayElements(address, addr, 0);
result = (ret == BT_STATUS_SUCCESS) ? JNI_TRUE : JNI_FALSE;
return result;
}
- createBondNative方法调用到HAL层的接口;
/** Create Bluetooth Bonding */
int (*create_bond)(const bt_bdaddr_t *bd_addr, int transport);
- 至此会进入BlueDroid,继续进行调用;
static int create_bond(const bt_bdaddr_t *bd_addr, int transport)
{
/* sanity check */
if (interface_ready() == FALSE)
return BT_STATUS_NOT_READY;
return btif_dm_create_bond(bd_addr, transport); //next
}
- 此处的btif_dm_create_bond函数不会进行实际bond操作,只是将事件抛出去,由其他的线程进行处理;
此处的设计很巧妙,因为如果上层所有的蓝牙相关操作事件下发下来,在此就进行处理,在实际编码中必然要加上很多的互斥操作,这就增加了设计的复杂性。
bt_status_t btif_dm_create_bond(const bt_bdaddr_t *bd_addr, int transport)
{
btif_dm_create_bond_cb_t create_bond_cb;
create_bond_cb.transport = transport;
bdcpy(create_bond_cb.bdaddr.address, bd_addr->address);
bdstr_t bdstr;
BTIF_TRACE_EVENT("%s: bd_addr=%s, transport=%d", __FUNCTION__, bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr)), transport);
if (pairing_cb.state != BT_BOND_STATE_NONE)
return BT_STATUS_BUSY;
btif_stats_add_bond_event(bd_addr, BTIF_DM_FUNC_CREATE_BOND, pairing_cb.state);
btif_transfer_context(btif_dm_generic_evt, BTIF_DM_CB_CREATE_BOND,
(char *)&create_bond_cb, sizeof(btif_dm_create_bond_cb_t), NULL);
return BT_STATUS_SUCCESS;
}
- btif_dm_generic_evt函数会接收这个事件并进行后续处理;
case BTIF_DM_CB_CREATE_BOND:
{
pairing_cb.timeout_retries = NUM_TIMEOUT_RETRIES;
btif_dm_create_bond_cb_t *create_bond_cb = (btif_dm_create_bond_cb_t*)p_param;
btif_dm_cb_create_bond(&create_bond_cb->bdaddr, create_bond_cb->transport);
}
break;
- btif_dm_cb_create_bond函数会对绑定事件进行处理,此处对HID设备进行了特别的处理;
static void btif_dm_cb_create_bond(bt_bdaddr_t *bd_addr, tBTA_TRANSPORT transport)
{
BOOLEAN is_hid = check_cod(bd_addr, COD_HID_POINTING);
bond_state_changed(BT_STATUS_SUCCESS, bd_addr, BT_BOND_STATE_BONDING);
#if BLE_INCLUDED == TRUE
int device_type;
int addr_type;
bdstr_t bdstr;
bdaddr_to_string(bd_addr, bdstr, sizeof(bdstr));
if (transport == BT_TRANSPORT_LE)
{
if (!btif_config_get_int((char const *)&bdstr,"DevType", &device_type))
{
btif_config_set_int(bdstr, "DevType", BT_DEVICE_TYPE_BLE);
}
if (btif_storage_get_remote_addr_type(bd_addr, &addr_type) != BT_STATUS_SUCCESS)
{
btif_storage_set_remote_addr_type(bd_addr, BLE_ADDR_PUBLIC);
}
}
if((btif_config_get_int((char const *)&bdstr,"DevType", &device_type) &&
(btif_storage_get_remote_addr_type(bd_addr, &addr_type) == BT_STATUS_SUCCESS) &&
(device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) || (transport == BT_TRANSPORT_LE))
{
BTA_DmAddBleDevice(bd_addr->address, addr_type, device_type);
}
#endif
// --ZafirTab-- 对HID设备的特殊处理
#if BLE_INCLUDED == TRUE
if(is_hid && (device_type & BT_DEVICE_TYPE_BLE) == 0)
#else
if(is_hid)
#endif
{
int status;
status = btif_hh_connect(bd_addr);
if(status != BT_STATUS_SUCCESS)
bond_state_changed(status, bd_addr, BT_BOND_STATE_NONE);
}
else
{
BTA_DmBondByTransport((UINT8 *)bd_addr->address, transport); // --ZafirTab-- next
}
/* Track originator of bond creation */
pairing_cb.is_local_initiated = TRUE;
}
- 此处我们直接看HID设备的bond;
void BTA_DmBondByTransport(BD_ADDR bd_addr, tBTA_TRANSPORT transport)
{
tBTA_DM_API_BOND *p_msg =
(tBTA_DM_API_BOND *)osi_malloc(sizeof(tBTA_DM_API_BOND));
p_msg->hdr.event = BTA_DM_API_BOND_EVT; //抛出的事件
bdcpy(p_msg->bd_addr, bd_addr);
p_msg->transport = transport;
bta_sys_sendmsg(p_msg);
}
- BTA_DmBondByTransport函数也没有对事件进行直接处理,又将事件抛出去了;
BTA_DM_API_BOND_EVT事件对应的是bta_dm_bond操作;
/* action function list */
const tBTA_DM_ACTION bta_dm_action[] =
{
/* device manager local device API events */
bta_dm_enable, /* 0 BTA_DM_API_ENABLE_EVT */
bta_dm_disable, /* 1 BTA_DM_API_DISABLE_EVT */
bta_dm_set_dev_name, /* 2 BTA_DM_API_SET_NAME_EVT */
bta_dm_set_visibility, /* 3 BTA_DM_API_SET_VISIBILITY_EVT */
bta_dm_acl_change, /* 8 BTA_DM_ACL_CHANGE_EVT */
bta_dm_add_device, /* 9 BTA_DM_API_ADD_DEVICE_EVT */
bta_dm_close_acl, /* 10 BTA_DM_API_ADD_DEVICE_EVT */
/* security API events */
bta_dm_bond, /* 11 BTA_DM_API_BOND_EVT */
bta_dm_bond_cancel, /* 12 BTA_DM_API_BOND_CANCEL_EVT */
- bta_dm_bond函数会调用btm_sec_bond_by_transport函数来启动SSP或者SMP;此处分支较多,我们后续以2.1 即BR/EDR继续分析;
tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport,
UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[])
{
tBTM_SEC_DEV_REC *p_dev_rec;
tBTM_STATUS status;
UINT8 *p_features;
UINT8 ii;
tACL_CONN *p= btm_bda_to_acl(bd_addr, transport);
BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x",
bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]);
BTM_TRACE_DEBUG("btm_sec_bond_by_transport: Transport used %d" , transport);
/* Other security process is in progress */
if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE)
{
BTM_TRACE_ERROR ("BTM_SecBond: already busy in state: %s", btm_pair_state_descr(btm_cb.pairing_state));
return(BTM_WRONG_MODE);
}
if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == NULL)
{
return(BTM_NO_RESOURCES);
}
BTM_TRACE_DEBUG ("before update sec_flags=0x%x", p_dev_rec->sec_flags);
/* Finished if connection is active and already paired */
if ( ((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_BR_EDR
&& (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))
#if (BLE_INCLUDED == TRUE)
||((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE
&& (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED))
#endif
)
{
BTM_TRACE_WARNING("BTM_SecBond -> Already Paired");
return(BTM_SUCCESS);
}
/* Tell controller to get rid of the link key if it has one stored */
if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS)
return(BTM_NO_RESOURCES);
/* Save the PIN code if we got a valid one */
if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0))
{
btm_cb.pin_code_len = pin_len;
p_dev_rec->pin_code_length = pin_len;
memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN);
}
memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN);
btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD;
p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE;
p_dev_rec->is_originator = TRUE;
if (trusted_mask)
BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask);
#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE
if (transport == BT_TRANSPORT_LE)
{
btm_ble_init_pseudo_addr (p_dev_rec, bd_addr);
p_dev_rec->sec_flags &= ~ BTM_SEC_LE_MASK;
if (SMP_Pair(bd_addr) == SMP_STARTED)
{
btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE;
p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING;
btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE);
return BTM_CMD_STARTED;
}
btm_cb.pairing_flags = 0;
return(BTM_NO_RESOURCES);
}
#endif
p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED
| BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED);
BTM_TRACE_DEBUG ("after update sec_flags=0x%x", p_dev_rec->sec_flags);
if (!controller_get_interface()->supports_simple_pairing())
{
/* The special case when we authenticate keyboard. Set pin type to fixed */
/* It would be probably better to do it from the application, but it is */
/* complicated */
if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL)
&& (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD)
&& (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED))
{
btm_cb.pin_type_changed = TRUE;
btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED);
}
}
for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++)
{
p_features = p_dev_rec->features[ii];
BTM_TRACE_EVENT(" remote_features page[%1d] = %02x-%02x-%02x-%02x",
ii, p_features[0], p_features[1], p_features[2], p_features[3]);
BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x",
p_features[4], p_features[5], p_features[6], p_features[7]);
}
BTM_TRACE_EVENT ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x", p_dev_rec->sm4, p_dev_rec->hci_handle);
#if BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE
p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN;
#endif
/* If connection already exists... */
if (p && p->hci_handle != BTM_SEC_INVALID_HANDLE)
{
if (!btm_sec_start_authentication (p_dev_rec))
return(BTM_NO_RESOURCES);
btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);
/* Mark lcb as bonding */
l2cu_update_lcb_4_bonding (bd_addr, TRUE);
return(BTM_CMD_STARTED);
}
BTM_TRACE_DEBUG ("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4);
if (!controller_get_interface()->supports_simple_pairing()
|| (p_dev_rec->sm4 == BTM_SM4_KNOWN))
{
if ( btm_sec_check_prefetch_pin (p_dev_rec) )
return (BTM_CMD_STARTED);
}
if ((btm_cb.security_mode == BTM_SEC_MODE_SP ||
btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG ||
btm_cb.security_mode == BTM_SEC_MODE_SC) &&
BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4))
{
/* local is 2.1 and peer is unknown */
if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0)
{
/* we are not accepting connection request from peer
* -> RNR (to learn if peer is 2.1)
* RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */
btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME);
BTM_ReadRemoteDeviceName(bd_addr, NULL, BT_TRANSPORT_BR_EDR);
}
else
{
/* We are accepting connection request from peer */
btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);
}
BTM_TRACE_DEBUG ("State:%s sm4: 0x%x sec_state:%d",
btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state);
return BTM_CMD_STARTED;
}
/* both local and peer are 2.1 */
status = btm_sec_dd_create_conn(p_dev_rec);
if (status != BTM_CMD_STARTED)
{
btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE);
}
return status;
}
- 从btm_sec_dd_create_conn函数继续后续分析;
static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec)
{
tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR);
if (p_lcb && (p_lcb->link_state == LST_CONNECTED || p_lcb->link_state == LST_CONNECTING))
{
BTM_TRACE_WARNING("%s Connection already exists", __func__);
return BTM_CMD_STARTED;
}
/* Make sure an L2cap link control block is available */
if (!p_lcb && (p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE, BT_TRANSPORT_BR_EDR)) == NULL)
{
BTM_TRACE_WARNING ("Security Manager: failed allocate LCB [%02x%02x%02x%02x%02x%02x]",
p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2],
p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]);
return(BTM_NO_RESOURCES);
}
/* set up the control block to indicated dedicated bonding */
btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE;
if (l2cu_create_conn(p_lcb, BT_TRANSPORT_BR_EDR) == FALSE)
{
BTM_TRACE_WARNING ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]",
p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2],
p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]);
l2cu_release_lcb(p_lcb);
return(BTM_NO_RESOURCES);
}
btm_acl_update_busy_level (BTM_BLI_PAGE_EVT);
BTM_TRACE_DEBUG ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]",
p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2],
p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]);
btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ);
return(BTM_CMD_STARTED);
}
- 调用l2cu_create_conn函数发送HCI指令给Controller;
BOOLEAN l2cu_create_conn (tL2C_LCB *p_lcb, tBT_TRANSPORT transport)
{
int xx;
tL2C_LCB *p_lcb_cur = &l2cb.lcb_pool[0];
#if BTM_SCO_INCLUDED == TRUE
BOOLEAN is_sco_active;
#endif
#if (BLE_INCLUDED == TRUE)
tBT_DEVICE_TYPE dev_type;
tBLE_ADDR_TYPE addr_type;
BTM_ReadDevInfo(p_lcb->remote_bd_addr, &dev_type, &addr_type);
if (transport == BT_TRANSPORT_LE)
{
if (!controller_get_interface()->supports_ble())
return FALSE;
p_lcb->ble_addr_type = addr_type;
p_lcb->transport = BT_TRANSPORT_LE;
return (l2cble_create_conn(p_lcb));
}
#endif
/* If there is a connection where we perform as a slave, try to switch roles
for this connection */
for (xx = 0, p_lcb_cur = &l2cb.lcb_pool[0]; xx < MAX_L2CAP_LINKS; xx++, p_lcb_cur++)
{
if (p_lcb_cur == p_lcb)
continue;
if ((p_lcb_cur->in_use) && (p_lcb_cur->link_role == HCI_ROLE_SLAVE))
{
#if BTM_SCO_INCLUDED == TRUE
/* The LMP_switch_req shall be sent only if the ACL logical transport
is in active mode, when encryption is disabled, and all synchronous
logical transports on the same physical link are disabled." */
/* Check if there is any SCO Active on this BD Address */
is_sco_active = btm_is_sco_active_by_bdaddr(p_lcb_cur->remote_bd_addr);
L2CAP_TRACE_API ("l2cu_create_conn - btm_is_sco_active_by_bdaddr() is_sco_active = %s", \
(is_sco_active == TRUE) ? "TRUE":"FALSE");
if (is_sco_active == TRUE)
continue; /* No Master Slave switch not allowed when SCO Active */
#endif
/*4_1_TODO check if btm_cb.devcb.local_features to be used instead */
if (HCI_SWITCH_SUPPORTED(BTM_ReadLocalFeatures()))
{
/* mark this lcb waiting for switch to be completed and
start switch on the other one */
p_lcb->link_state = LST_CONNECTING_WAIT_SWITCH;
p_lcb->link_role = HCI_ROLE_MASTER;
if (BTM_SwitchRole (p_lcb_cur->remote_bd_addr, HCI_ROLE_MASTER, NULL) == BTM_CMD_STARTED)
{
alarm_set_on_queue(p_lcb->l2c_lcb_timer,
L2CAP_LINK_ROLE_SWITCH_TIMEOUT_MS,
l2c_lcb_timer_timeout, p_lcb,
btu_general_alarm_queue);
return (TRUE);
}
}
}
}
p_lcb->link_state = LST_CONNECTING;
return (l2cu_create_conn_after_switch (p_lcb));
}
-
至此Host端的操作流程告一段落,Controller根据HCI指令进行后续的操作,这部分是由蓝牙芯片提供商来设计的,我们在此不做介绍。
-
总结
该篇文章挑选了蓝牙API操作中的createBond进行了详细分析,旨在给读者提供一个分析Android Bluetooth 调用流程的一个思路,也算是抛砖引玉。
其中涉及到AIDL、binder、JNI等相关知识,读者如果有欠缺,可自行学习。
当上层下发的操作被stack正常执行后,stack后返回一个相应的回调给上层,至于这个callback,上层是如何正常接收到的,读者可以沿着上文提到的思路倒推回去,也顺便检测一下自己的代码分析能力。
网友评论