diff --git a/system/stack/acl/btm_acl.cc b/system/stack/acl/btm_acl.cc index 05130f4fab870df9c101df7e31b1eb7e1d29ec17..e8d71088124925faff1632c8e671a05704b647d3 100644 --- a/system/stack/acl/btm_acl.cc +++ b/system/stack/acl/btm_acl.cc @@ -2708,8 +2708,37 @@ void acl_disconnect_from_handle(uint16_t handle, tHCI_STATUS reason, acl_disconnect_after_role_switch(handle, reason, comment); } +// BLUETOOTH CORE SPECIFICATION Version 5.4 | Vol 4, Part E +// 7.1.6 Disconnect command +// Only a subset of reasons are valid and will be accepted +// by the controller +bool is_disconnect_reason_valid(const tHCI_REASON& reason) { + switch (reason) { + case HCI_ERR_AUTH_FAILURE: + case HCI_ERR_PEER_USER: + case HCI_ERR_REMOTE_LOW_RESOURCE: + case HCI_ERR_REMOTE_POWER_OFF: + case HCI_ERR_UNSUPPORTED_REM_FEATURE: + case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED: + case HCI_ERR_UNACCEPT_CONN_INTERVAL: + return true; + default: + break; + } + return false; +} + void acl_disconnect_after_role_switch(uint16_t conn_handle, tHCI_STATUS reason, std::string comment) { + if (!is_disconnect_reason_valid(reason)) { + LOG_WARN( + "Controller will not accept invalid reason parameter:%s" + " instead sending:%s", + hci_error_code_text(reason).c_str(), + hci_error_code_text(HCI_ERR_PEER_USER).c_str()); + reason = HCI_ERR_PEER_USER; + } + tACL_CONN* p_acl = internal_.acl_get_connection_from_handle(conn_handle); if (p_acl == nullptr) { LOG_ERROR("Sending disconnect for unknown acl:%hu PLEASE FIX", conn_handle); diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc index 829b714ef1cce024e2a43e6ee5c74824fb115ae9..3ec8fb58afe92d85ddf1bb866ae323905e6d43f2 100644 --- a/system/stack/test/btm/stack_btm_test.cc +++ b/system/stack/test/btm/stack_btm_test.cc @@ -493,3 +493,23 @@ TEST_F(StackBtmWithInitFreeTest, wipe_secrets_and_remove) { wipe_secrets_and_remove(device_record); } + +bool is_disconnect_reason_valid(const tHCI_REASON& reason); +TEST_F(StackBtmWithInitFreeTest, is_disconnect_reason_valid) { + std::set<tHCI_REASON> valid_reason_set{ + HCI_ERR_AUTH_FAILURE, + HCI_ERR_PEER_USER, + HCI_ERR_REMOTE_LOW_RESOURCE, + HCI_ERR_REMOTE_POWER_OFF, + HCI_ERR_UNSUPPORTED_REM_FEATURE, + HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED, + HCI_ERR_UNACCEPT_CONN_INTERVAL, + }; + for (unsigned u = 0; u < 256; u++) { + const tHCI_REASON reason = static_cast<tHCI_REASON>(u); + if (valid_reason_set.count(reason)) + ASSERT_TRUE(is_disconnect_reason_valid(reason)); + else + ASSERT_FALSE(is_disconnect_reason_valid(reason)); + } +}