字符有:PIN码或配对密钥不正确,无法与
对应的字符串为:bluetooth_pairing_pin_error_message
frameworks/base/packages/SettingsLib/src/com/android/settingslib/bluetooth/BluetoothEventManager.java
/**
* Called when we have reached the unbonded state.
*
* @param reason one of the error reasons from
* BluetoothDevice.UNBOND_REASON_*
*/
private void showUnbondMessage(Context context, String name, int reason) {
int errorMsg;
switch(reason) {
case BluetoothDevice.UNBOND_REASON_AUTH_FAILED:
errorMsg = R.string.bluetooth_pairing_pin_error_message;
break;
case BluetoothDevice.UNBOND_REASON_AUTH_REJECTED:
errorMsg = R.string.bluetooth_pairing_rejected_error_message;
break;
case BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN:
errorMsg = R.string.bluetooth_pairing_device_down_error_message;
break;
case BluetoothDevice.UNBOND_REASON_DISCOVERY_IN_PROGRESS:
case BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT:
case BluetoothDevice.UNBOND_REASON_REPEATED_ATTEMPTS:
case BluetoothDevice.UNBOND_REASON_REMOTE_AUTH_CANCELED:
errorMsg = R.string.bluetooth_pairing_error_message;
break;
default:
Log.w(TAG, "showUnbondMessage: Not displaying any message for reason: " + reason);
return;
}
Utils.showError(context, name, errorMsg);
}
}
继续查看调用showUnbondMessage的位置:
private class BondStateChangedHandler implements Handler {
public void onReceive(Context context, Intent intent,
BluetoothDevice device) {
if (device == null) {
Log.e(TAG, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE");
return;
}
int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
BluetoothDevice.ERROR);
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(device);
if (cachedDevice == null) {
Log.w(TAG, "CachedBluetoothDevice for device " + device +
" not found, calling readPairedDevices().");
if (readPairedDevices()) {
cachedDevice = mDeviceManager.findDevice(device);
}
if (cachedDevice == null) {
Log.w(TAG, "Got bonding state changed for " + device +
", but we have no record of that device.");
cachedDevice = mDeviceManager.addDevice(mLocalAdapter, mProfileManager, device);
dispatchDeviceAdded(cachedDevice);
}
}
synchronized (mCallbacks) {
for (BluetoothCallback callback : mCallbacks) {
callback.onDeviceBondStateChanged(cachedDevice, bondState);
}
}
cachedDevice.onBondingStateChanged(bondState);
if (bondState == BluetoothDevice.BOND_NONE) {
/* Check if we need to remove other Hearing Aid devices */
if (cachedDevice.getHiSyncId() != BluetoothHearingAid.HI_SYNC_ID_INVALID) {
mDeviceManager.onDeviceUnpaired(cachedDevice);
}
int reason = intent.getIntExtra(BluetoothDevice.EXTRA_REASON,
BluetoothDevice.ERROR);
showUnbondMessage(context, cachedDevice.getName(), reason);
}
}
该错误的提示已异常为:BluetoothDevice.UNBOND_REASON_AUTH_FAILED
找到蓝牙服务的位置, 根据蓝牙状态机的显示有:
packages/apps/Bluetooth/src/com/android/bluetooth/btservice/BondStateMachine.java
private int getUnbondReasonFromHALCode(int reason) {
if (reason == AbstractionLayer.BT_STATUS_SUCCESS) {
return BluetoothDevice.BOND_SUCCESS;
} else if (reason == AbstractionLayer.BT_STATUS_RMT_DEV_DOWN) {
return BluetoothDevice.UNBOND_REASON_REMOTE_DEVICE_DOWN;
} else if (reason == AbstractionLayer.BT_STATUS_AUTH_FAILURE) {
return BluetoothDevice.UNBOND_REASON_AUTH_FAILED;
} else if (reason == AbstractionLayer.BT_STATUS_AUTH_REJECTED) {
return BluetoothDevice.UNBOND_REASON_AUTH_REJECTED;
} else if (reason == AbstractionLayer.BT_STATUS_AUTH_TIMEOUT) {
return BluetoothDevice.UNBOND_REASON_AUTH_TIMEOUT;
}
/* default */
return BluetoothDevice.UNBOND_REASON_REMOVED;
}
可以知道是:AbstractionLayer.BT_STATUS_AUTH_FAILURE这个异常导致;
此处有蓝牙状态变化的处理函数:
private class PendingCommandState extends State {
@Override
public void enter() {
infoLog("Entering PendingCommandState State");
BluetoothDevice dev = (BluetoothDevice) getCurrentMessage().obj;
}
@Override
public synchronized boolean processMessage(Message msg) {
BluetoothDevice dev = (BluetoothDevice) msg.obj;
DeviceProperties devProp = mRemoteDevices.getDeviceProperties(dev);
boolean result = false;
if (mDevices.contains(dev) && msg.what != CANCEL_BOND
&& msg.what != BONDING_STATE_CHANGE && msg.what != SSP_REQUEST
&& msg.what != PIN_REQUEST) {
deferMessage(msg);
return true;
}
switch (msg.what) {
case CREATE_BOND:
OobData oobData = null;
if (msg.getData() != null) {
oobData = msg.getData().getParcelable(OOBDATA);
}
result = createBond(dev, msg.arg1, oobData, false);
break;
case REMOVE_BOND:
result = removeBond(dev, false);
break;
case CANCEL_BOND:
result = cancelBond(dev);
break;
case BONDING_STATE_CHANGE:
int newState = msg.arg1;
int reason = getUnbondReasonFromHALCode(msg.arg2);
sendIntent(dev, newState, reason);
if (newState != BluetoothDevice.BOND_BONDING) {
// check if bond none is received from device which
// was in pairing state otherwise don't transition to
// stable state.
if (newState == BluetoothDevice.BOND_NONE &&
!mDevices.contains(dev) && mDevices.size() != 0) {
infoLog("not transitioning to stable state");
break;
}
/* this is either none/bonded, remove and transition */
result = !mDevices.remove(dev);
if (mDevices.isEmpty()) {
// Whenever mDevices is empty, then we need to
// set result=false. Else, we will end up adding
// the device to the list again. This prevents us
// from pairing with a device that we just unpaired
result = false;
transitionTo(mStableState);
}
if (newState == BluetoothDevice.BOND_NONE) {
mAdapterService.setPhonebookAccessPermission(dev,
BluetoothDevice.ACCESS_UNKNOWN);
mAdapterService.setMessageAccessPermission(dev,
BluetoothDevice.ACCESS_UNKNOWN);
mAdapterService.setSimAccessPermission(dev,
BluetoothDevice.ACCESS_UNKNOWN);
// Set the profile Priorities to undefined
clearProfilePriority(dev);
}
} else if (!mDevices.contains(dev)) {
result = true;
}
break;
case SSP_REQUEST:
int passkey = msg.arg1;
int variant = msg.arg2;
if (devProp == null)
{
Log.e(TAG,"Received msg from an unknown device");
return false;
}
sendDisplayPinIntent(devProp.getAddress(), passkey, variant);
break;
case PIN_REQUEST:
BluetoothClass btClass = dev.getBluetoothClass();
int btDeviceClass = btClass.getDeviceClass();
if (devProp == null)
{
Log.e(TAG,"Received msg from an unknown device");
return false;
}
if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass
== BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) {
// Its a keyboard. Follow the HID spec recommendation of creating the
// passkey and displaying it to the user. If the keyboard doesn't follow
// the spec recommendation, check if the keyboard has a fixed PIN zero
// and pair.
//TODO: Maintain list of devices that have fixed pin
// Generate a variable 6-digit PIN in range of 100000-999999
// This is not truly random but good enough.
int pin = 100000 + (int) Math.floor((Math.random() * (999999 - 100000)));
sendDisplayPinIntent(devProp.getAddress(), pin,
BluetoothDevice.PAIRING_VARIANT_DISPLAY_PIN);
break;
}
if (msg.arg2 == 1) { // Minimum 16 digit pin required here
sendDisplayPinIntent(devProp.getAddress(), 0,
BluetoothDevice.PAIRING_VARIANT_PIN_16_DIGITS);
} else {
// In PIN_REQUEST, there is no passkey to display.So do not send the
// EXTRA_PAIRING_KEY type in the intent( 0 in SendDisplayPinIntent() )
sendDisplayPinIntent(devProp.getAddress(), 0,
BluetoothDevice.PAIRING_VARIANT_PIN);
}
break;
default:
Log.e(TAG, "Received unhandled event:" + msg.what);
return false;
}
if (result) {
mDevices.add(dev);
}
return true;
}
}
根据该函数的调用位置可以知道是BONDING_STATE_CHANGE变化时候的提示;
继续追踪可以找到是蓝牙协议调用层:
vendor/qcom/opensource/commonsys/system/bt/btif/src/btif_dm.cc
/*******************************************************************************
*
* Function btif_dm_auth_cmpl_evt
*
* Description Executes authentication complete event in btif context
*
* Returns void
*
******************************************************************************/
static void btif_dm_auth_cmpl_evt(tBTA_DM_AUTH_CMPL* p_auth_cmpl) {
/* Save link key, if not temporary */
bt_status_t status = BT_STATUS_FAIL;
bt_bond_state_t state = BT_BOND_STATE_NONE;
if (p_auth_cmpl->success) {
// Do not call bond_state_changed_cb yet. Wait until remote service
// discovery is complete
} else {
// Map the HCI fail reason to bt status
switch (p_auth_cmpl->fail_reason) {
case HCI_ERR_PAGE_TIMEOUT:
case HCI_ERR_LMP_RESPONSE_TIMEOUT:
if (pairing_cb.timeout_retries) {
BTIF_TRACE_WARNING("%s() - Pairing timeout; retrying (%d) ...",
__func__, pairing_cb.timeout_retries);
--pairing_cb.timeout_retries;
btif_dm_cb_create_bond(bd_addr, BTA_TRANSPORT_UNKNOWN);
return;
}
/* Fall-through */
case HCI_ERR_CONNECTION_TOUT:
status = BT_STATUS_RMT_DEV_DOWN;
break;
case HCI_ERR_PAIRING_NOT_ALLOWED:
btif_storage_remove_bonded_device(&bd_addr);
status = BT_STATUS_AUTH_REJECTED;
break;
/* Dont fail the bonding for key missing error as stack retry security */
case HCI_ERR_KEY_MISSING:
btif_storage_remove_bonded_device(&bd_addr);
if (p_auth_cmpl->is_sm4_dev) {
return;
} else {
BTIF_TRACE_WARNING("%s() legacy remote ,move bond state to none", __FUNCTION__);
}
/* map the auth failure codes, so we can retry pairing if necessary */
case HCI_ERR_AUTH_FAILURE:
btif_storage_remove_bonded_device(&bd_addr);
case HCI_ERR_HOST_REJECT_SECURITY:
case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE:
case HCI_ERR_UNIT_KEY_USED:
case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED:
case HCI_ERR_INSUFFCIENT_SECURITY:
case HCI_ERR_PEER_USER:
case HCI_ERR_UNSPECIFIED:
case HCI_ERR_REPEATED_ATTEMPTS:
BTIF_TRACE_DEBUG(" %s() Authentication fail reason %d",
__FUNCTION__, p_auth_cmpl->fail_reason);
if (pairing_cb.autopair_attempts == 1) {
/* Create the Bond once again */
BTIF_TRACE_WARNING("%s() auto pair failed. Reinitiate Bond",
__func__);
btif_dm_cb_create_bond(bd_addr, BTA_TRANSPORT_UNKNOWN);
return;
} else {
/* if autopair attempts are more than 1, or not attempted */
status = BT_STATUS_AUTH_FAILURE;
}
break;
default:
status = BT_STATUS_FAIL;
}
查看注释上写明:
/* if autopair attempts are more than 1, or not attempted */
status = BT_STATUS_AUTH_FAILURE;
另外还有一处调用:
/*******************************************************************************
*
* Function btif_dm_ble_auth_cmpl_evt
*
* Description Executes authentication complete event in btif context
*
* Returns void
*
******************************************************************************/
static void btif_dm_ble_auth_cmpl_evt(tBTA_DM_AUTH_CMPL* p_auth_cmpl) {
/* Save link key, if not temporary */
bt_status_t status = BT_STATUS_FAIL;
bt_bond_state_t state = BT_BOND_STATE_NONE;
RawAddress bd_addr = p_auth_cmpl->bd_addr;
/* Clear OOB data */
memset(&oob_cb, 0, sizeof(oob_cb));
if ((p_auth_cmpl->success == true) && (p_auth_cmpl->key_present)) {
/* store keys */
}
if (p_auth_cmpl->success) {
status = BT_STATUS_SUCCESS;
state = BT_BOND_STATE_BONDED;
int addr_type;
RawAddress bdaddr = p_auth_cmpl->bd_addr;
if (btif_storage_get_remote_addr_type(&bdaddr, &addr_type) !=
BT_STATUS_SUCCESS)
btif_storage_set_remote_addr_type(&bdaddr, p_auth_cmpl->addr_type);
/* Test for temporary bonding */
if (btm_get_bond_type_dev(p_auth_cmpl->bd_addr) == BOND_TYPE_TEMPORARY) {
BTIF_TRACE_DEBUG("%s: sending BT_BOND_STATE_NONE for Temp pairing",
__func__);
btif_storage_remove_bonded_device(&bdaddr);
state = BT_BOND_STATE_NONE;
} else {
btif_dm_save_ble_bonding_keys();
BTA_GATTC_Refresh(bd_addr);
if(!p_auth_cmpl->smp_over_br)
btif_dm_get_remote_services_by_transport(&bd_addr, BTA_GATT_TRANSPORT_LE);
else
btif_dm_get_remote_services(bd_addr);
}
} else {
/*Map the HCI fail reason to bt status */
switch (p_auth_cmpl->fail_reason) {
case BTA_DM_AUTH_SMP_PAIR_AUTH_FAIL:
case BTA_DM_AUTH_SMP_CONFIRM_VALUE_FAIL:
case BTA_DM_AUTH_SMP_UNKNOWN_ERR:
case BTA_DM_AUTH_SMP_CONN_TOUT:
btif_dm_remove_ble_bonding_keys();
status = BT_STATUS_AUTH_FAILURE;
break;
case BTA_DM_AUTH_SMP_PAIR_NOT_SUPPORT:
status = BT_STATUS_AUTH_REJECTED;
break;
default:
btif_dm_remove_ble_bonding_keys();
status = BT_STATUS_FAIL;
break;
}
}
bond_state_changed(status, bd_addr, state);
}
以上两种方式,我们初步可以判断是BLE协议和传统蓝牙协议;
而PC端与设备端调用我们初步判断是传统蓝牙协议,故执行btif_dm_auth_cmpl_evt
case BTA_DM_AUTH_CMPL_EVT:
btif_dm_auth_cmpl_evt(&p_data->auth_cmpl);
break;
case BTA_DM_BLE_AUTH_CMPL_EVT:
BTIF_TRACE_DEBUG("BTA_DM_BLE_AUTH_CMPL_EVT. ");
btif_dm_ble_auth_cmpl_evt(&p_data->auth_cmpl);
break;
后续可以抓取log一步步跟踪尝试。
临时修改方案,当Auth异常时候,不显示其Toast显示,直接以log信息提示即可。