summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Łukasz Rymanowski <rlukasz@google.com> 2022-07-01 15:49:06 +0000
committer Grzegorz Kołodziejczyk <gkolodziejczyk@google.com> 2022-07-15 21:27:20 +0000
commite3929d792d50f701cbaf24d70a0f72a02c63f2d1 (patch)
tree72437c0ac5759406b7b0faf3f0308272e50019ba
parent82b06cbec63d2f437a75e1bc929014553d16888b (diff)
eatt: Handle L2CAP collision on EATT
When Android is a Central and collision is detected, Android will send another request to the remote device after local request is completed. If Android is a Peripheral, it will let Central to setup EATT, however for PTS test case, it will handle collision assumint local CID limit equal 5. This can be enabled with PTS_EattPeripheralCollionSupport=true Bug: 236216322 Bug: 237746679 Test: atest BluetoothInstrumentationTests Test: PTS Tag: #stability Change-Id: Iada26e11de5b1e2631389e280417a4616d3795cd
-rw-r--r--system/conf/bt_stack.conf3
-rw-r--r--system/internal_include/stack_config.h1
-rw-r--r--system/main/stack_config.cc8
-rw-r--r--system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc3
-rw-r--r--system/profile/avrcp/tests/avrcp_device_test.cc3
-rw-r--r--system/stack/Android.bp3
-rw-r--r--system/stack/eatt/eatt.cc6
-rw-r--r--system/stack/eatt/eatt_impl.h126
-rw-r--r--system/stack/test/btm/stack_btm_test.cc3
-rw-r--r--system/stack/test/eatt/eatt_test.cc17
-rw-r--r--system/stack/test/gatt/mock_gatt_utils_ref.cc3
-rw-r--r--system/stack/test/gatt/stack_gatt_test.cc2
-rw-r--r--system/stack/test/stack_smp_test.cc3
-rw-r--r--system/test/common/stack_config.cc3
14 files changed, 151 insertions, 33 deletions
diff --git a/system/conf/bt_stack.conf b/system/conf/bt_stack.conf
index 785b2919aa..942dfdcd1e 100644
--- a/system/conf/bt_stack.conf
+++ b/system/conf/bt_stack.conf
@@ -53,6 +53,9 @@ TRC_HID_DEV=2
# Start EATT on unecrypted link
#PTS_ConnectEattUnencrypted=true
+# Force EATT implementation to connect EATT as a peripheral for collision test case
+#PTS_EattPeripheralCollionSupport=true
+
# Disable BR/EDR discovery after LE pairing to avoid cross key derivation errors
#PTS_DisableSDPOnLEPair=true
diff --git a/system/internal_include/stack_config.h b/system/internal_include/stack_config.h
index a3d185dc60..53dd186a70 100644
--- a/system/internal_include/stack_config.h
+++ b/system/internal_include/stack_config.h
@@ -37,6 +37,7 @@ typedef struct {
bool (*get_pts_connect_eatt_unconditionally)(void);
bool (*get_pts_connect_eatt_before_encryption)(void);
bool (*get_pts_unencrypt_broadcast)(void);
+ bool (*get_pts_eatt_peripheral_collision_support)(void);
config_t* (*get_all)(void);
} stack_config_t;
diff --git a/system/main/stack_config.cc b/system/main/stack_config.cc
index e805cf7e90..90c293ed0f 100644
--- a/system/main/stack_config.cc
+++ b/system/main/stack_config.cc
@@ -38,6 +38,8 @@ const char* PTS_CONNECT_EATT_UNCONDITIONALLY =
"PTS_ConnectEattUncondictionally";
const char* PTS_CONNECT_EATT_UNENCRYPTED = "PTS_ConnectEattUnencrypted";
const char* PTS_BROADCAST_UNENCRYPTED = "PTS_BroadcastUnencrypted";
+const char* PTS_EATT_PERIPHERAL_COLLISION_SUPPORT =
+ "PTS_EattPeripheralCollionSupport";
static std::unique_ptr<config_t> config;
} // namespace
@@ -135,6 +137,11 @@ static bool get_pts_unencrypt_broadcast(void) {
PTS_BROADCAST_UNENCRYPTED, false);
}
+static bool get_pts_eatt_peripheral_collision_support(void) {
+ return config_get_bool(*config, CONFIG_DEFAULT_SECTION,
+ PTS_EATT_PERIPHERAL_COLLISION_SUPPORT, false);
+}
+
static config_t* get_all(void) { return config.get(); }
const stack_config_t interface = {get_trace_config_enabled,
@@ -148,6 +155,7 @@ const stack_config_t interface = {get_trace_config_enabled,
get_pts_connect_eatt_unconditionally,
get_pts_connect_eatt_before_encryption,
get_pts_unencrypt_broadcast,
+ get_pts_eatt_peripheral_collision_support,
get_all};
const stack_config_t* stack_config_get_interface(void) { return &interface; }
diff --git a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
index 4389833471..441bf5c1d0 100644
--- a/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
+++ b/system/profile/avrcp/tests/avrcp_device_fuzz/avrcp_device_fuzz.cc
@@ -59,7 +59,8 @@ const stack_config_t interface = {nullptr, get_pts_avrcp_test,
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
- nullptr, nullptr};
+ nullptr, nullptr,
+ nullptr};
void Callback(uint8_t, bool, std::unique_ptr<::bluetooth::PacketBuilder>) {}
diff --git a/system/profile/avrcp/tests/avrcp_device_test.cc b/system/profile/avrcp/tests/avrcp_device_test.cc
index f6dd75b0da..2bbc39ad4e 100644
--- a/system/profile/avrcp/tests/avrcp_device_test.cc
+++ b/system/profile/avrcp/tests/avrcp_device_test.cc
@@ -55,7 +55,8 @@ const stack_config_t interface = {nullptr, get_pts_avrcp_test,
nullptr, nullptr,
nullptr, nullptr,
nullptr, nullptr,
- nullptr, nullptr};
+ nullptr, nullptr,
+ nullptr};
// TODO (apanicke): All the tests below are just basic positive unit tests.
// Add more tests to increase code coverage.
diff --git a/system/stack/Android.bp b/system/stack/Android.bp
index cdb4ae71cd..3297591b24 100644
--- a/system/stack/Android.bp
+++ b/system/stack/Android.bp
@@ -640,6 +640,7 @@ cc_test {
"packages/modules/Bluetooth/system/utils/include",
],
srcs: crypto_toolbox_srcs + [
+ ":TestCommonMainHandler",
":TestMockStackBtm",
"gatt/gatt_db.cc",
"gatt/gatt_sr_hash.cc",
@@ -742,6 +743,7 @@ cc_test {
"test/common/mock_l2cap_layer.cc",
"test/gatt/mock_gatt_utils_ref.cc",
"test/eatt/eatt_test.cc",
+ ":TestCommonMainHandler",
":TestCommonStackConfig",
],
shared_libs: [
@@ -1036,6 +1038,7 @@ cc_test {
],
srcs: [
":OsiCompatSources",
+ ":TestCommonMainHandler",
":TestCommonStackConfig",
":TestMockBta",
":TestMockBtif",
diff --git a/system/stack/eatt/eatt.cc b/system/stack/eatt/eatt.cc
index 1aa79f0f9f..a023026309 100644
--- a/system/stack/eatt/eatt.cc
+++ b/system/stack/eatt/eatt.cc
@@ -45,6 +45,7 @@ struct EattExtension::impl {
reg_info_.pL2CA_DisconnectInd_Cb = eatt_disconnect_ind;
reg_info_.pL2CA_Error_Cb = eatt_error_cb;
reg_info_.pL2CA_DataInd_Cb = eatt_data_ind;
+ reg_info_.pL2CA_CreditBasedCollisionInd_Cb = eatt_collision_ind;
if (L2CA_RegisterLECoc(BT_PSM_EATT, reg_info_, BTM_SEC_NONE, {}) == 0) {
LOG(ERROR) << __func__ << " cannot register EATT";
@@ -94,6 +95,11 @@ struct EattExtension::impl {
p_cfg);
}
+ static void eatt_collision_ind(const RawAddress& bd_addr) {
+ auto p_eatt_impl = GetImplInstance();
+ if (p_eatt_impl) p_eatt_impl->eatt_l2cap_collision_ind(bd_addr);
+ }
+
static void eatt_error_cb(uint16_t lcid, uint16_t reason) {
auto p_eatt_impl = GetImplInstance();
if (p_eatt_impl) p_eatt_impl->eatt_l2cap_error_cb(lcid, reason);
diff --git a/system/stack/eatt/eatt_impl.h b/system/stack/eatt/eatt_impl.h
index 385ec430b6..0a2c2afd1d 100644
--- a/system/stack/eatt/eatt_impl.h
+++ b/system/stack/eatt/eatt_impl.h
@@ -33,6 +33,7 @@
#include "stack/btm/btm_sec.h"
#include "stack/gatt/gatt_int.h"
#include "stack/include/bt_hdr.h"
+#include "stack/include/btu.h" // do_in_main_thread
#include "stack/l2cap/l2c_int.h"
#include "types/raw_address.h"
@@ -50,9 +51,9 @@ class eatt_device {
tGATT_TCB* eatt_tcb_;
std::map<uint16_t, std::shared_ptr<EattChannel>> eatt_channels;
-
+ bool collision;
eatt_device(const RawAddress& bd_addr, uint16_t mtu, uint16_t mps)
- : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr) {
+ : rx_mtu_(mtu), rx_mps_(mps), eatt_tcb_(nullptr), collision(false) {
bda_ = bd_addr;
}
};
@@ -175,6 +176,49 @@ struct eatt_impl {
LOG(INFO) << __func__ << " Channel connected CID " << loghex(cid);
}
+
+ /* Android let Central to create EATT (PTS initiates EATT). Some PTS test
+ * cases wants Android to do it anyway (Android initiates EATT).
+ */
+ if (stack_config_get_interface()
+ ->get_pts_eatt_peripheral_collision_support()) {
+ connect_eatt_wrap(eatt_dev);
+ }
+ }
+
+ void eatt_retry_after_collision_if_needed(eatt_device* eatt_dev) {
+ if (!eatt_dev->collision) {
+ LOG_DEBUG("No collision.");
+ return;
+ }
+ /* We are here, because remote device wanted to create channels when
+ * Android proceed its own EATT creation. How to handle it is described
+ * here: BT Core 5.3, Volume 3, Part G, 5.4
+ */
+ LOG_INFO(
+ "EATT collision detected. If we are Central we will retry right "
+ "away");
+
+ eatt_dev->collision = false;
+ uint8_t role = L2CA_GetBleConnRole(eatt_dev->bda_);
+ if (role == HCI_ROLE_CENTRAL) {
+ LOG_INFO("Retrying EATT setup due to previous collision for device %s",
+ eatt_dev->bda_.ToString().c_str());
+ connect_eatt_wrap(eatt_dev);
+ } else if (stack_config_get_interface()
+ ->get_pts_eatt_peripheral_collision_support()) {
+ /* This is only for the PTS. Android does not setup EATT when is a
+ * peripheral.
+ */
+ bt_status_t status = do_in_main_thread_delayed(
+ FROM_HERE,
+ base::BindOnce(&eatt_impl::connect_eatt_wrap, base::Unretained(this),
+ std::move(eatt_dev)),
+ base::TimeDelta::FromMilliseconds(500));
+
+ LOG_INFO("Scheduled peripheral connect eatt for device with status: %d",
+ (int)status);
+ }
}
void eatt_l2cap_connect_cfm(const RawAddress& bda, uint16_t lcid,
@@ -209,6 +253,7 @@ struct eatt_impl {
eatt_dev->eatt_tcb_->eatt++;
LOG(INFO) << __func__ << " Channel connected CID " << loghex(lcid);
+ eatt_retry_after_collision_if_needed(eatt_dev);
}
void eatt_l2cap_reconfig_completed(const RawAddress& bda, uint16_t lcid,
@@ -237,6 +282,17 @@ struct eatt_impl {
channel->EattChannelSetState(EattChannelState::EATT_CHANNEL_OPENED);
}
+ void eatt_l2cap_collision_ind(const RawAddress& bda) {
+ eatt_device* eatt_dev = find_device_by_address(bda);
+ if (!eatt_dev) {
+ LOG_ERROR("Device %s not available anymore:", bda.ToString().c_str());
+ return;
+ }
+ /* Remote wanted to setup channels as well. Let's retry remote's request
+ * when we are done with ours.*/
+ eatt_dev->collision = true;
+ }
+
void eatt_l2cap_error_cb(uint16_t lcid, uint16_t reason) {
LOG(INFO) << __func__ << " cid: " << loghex(lcid) << " reason "
<< loghex(reason);
@@ -266,6 +322,8 @@ struct eatt_impl {
<< static_cast<uint8_t>(channel->state_);
break;
}
+
+ eatt_retry_after_collision_if_needed(eatt_dev);
}
void eatt_l2cap_disconnect_ind(uint16_t lcid, bool please_confirm) {
@@ -317,7 +375,22 @@ struct eatt_impl {
return eatt_dev;
}
- void connect_eatt(eatt_device* eatt_dev) {
+ void connect_eatt_wrap(eatt_device* eatt_dev) {
+ if (stack_config_get_interface()
+ ->get_pts_eatt_peripheral_collision_support()) {
+ /* For PTS case, lets assume we support only 5 channels */
+ LOG_INFO("Number of existing channels %d",
+ (int)eatt_dev->eatt_channels.size());
+ connect_eatt(eatt_dev, L2CAP_CREDIT_BASED_MAX_CIDS -
+ (int)eatt_dev->eatt_channels.size());
+ return;
+ }
+
+ connect_eatt(eatt_dev);
+ }
+
+ void connect_eatt(eatt_device* eatt_dev,
+ uint8_t num_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS) {
/* Let us use maximum possible mps */
if (eatt_dev->rx_mps_ == EATT_MIN_MTU_MPS)
eatt_dev->rx_mps_ = controller_get_interface()->get_acl_data_size_ble();
@@ -326,9 +399,12 @@ struct eatt_impl {
.mtu = eatt_dev->rx_mtu_,
.mps = eatt_dev->rx_mps_,
.credits = L2CAP_LE_CREDIT_DEFAULT,
- .number_of_channels = L2CAP_CREDIT_BASED_MAX_CIDS,
+ .number_of_channels = num_of_channels,
};
+ LOG_INFO("Connecting device %s, cnt count %d",
+ eatt_dev->bda_.ToString().c_str(), num_of_channels);
+
/* Warning! CIDs in Android are unique across the ACL connections */
std::vector<uint16_t> connecting_cids =
L2CA_ConnectCreditBasedReq(psm_, eatt_dev->bda_, &local_coc_cfg);
@@ -615,7 +691,7 @@ struct eatt_impl {
return;
}
- connect_eatt(eatt_dev);
+ connect_eatt_wrap(eatt_dev);
}
void disconnect_channel(uint16_t cid) { L2CA_DisconnectReq(cid); }
@@ -659,6 +735,7 @@ struct eatt_impl {
}
eatt_dev->eatt_tcb_->eatt = 0;
eatt_dev->eatt_tcb_ = nullptr;
+ eatt_dev->collision = false;
}
void connect(const RawAddress& bd_addr) {
@@ -670,8 +747,8 @@ struct eatt_impl {
return;
}
- LOG(INFO) << __func__ << " device " << bd_addr << " role"
- << (role == HCI_ROLE_CENTRAL ? "central" : "peripheral");
+ LOG_INFO("Device %s, role %s", bd_addr.ToString().c_str(),
+ (role == HCI_ROLE_CENTRAL ? "central" : "peripheral"));
if (eatt_dev) {
/* We are reconnecting device we know that support EATT.
@@ -685,31 +762,30 @@ struct eatt_impl {
return;
}
- connect_eatt(eatt_dev);
+ connect_eatt_wrap(eatt_dev);
return;
}
/* This is needed for L2CAP test cases */
if (stack_config_get_interface()->get_pts_connect_eatt_unconditionally()) {
- /* For PTS just start connecting EATT right away, */
+ /* For PTS just start connecting EATT right away */
eatt_device* eatt_dev = add_eatt_device(bd_addr);
- connect_eatt(eatt_dev);
- } else {
- if (gatt_profile_get_eatt_support(bd_addr)) {
- LOG_DEBUG("Eatt is supported for device %s",
- bd_addr.ToString().c_str());
- supported_features_cb(role, bd_addr,
- BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
- return;
- }
+ connect_eatt_wrap(eatt_dev);
+ return;
+ }
- /* If we don't know yet, read GATT server supported features. */
- if (gatt_cl_read_sr_supp_feat_req(
- bd_addr, base::BindOnce(&eatt_impl::supported_features_cb,
- base::Unretained(this), role)) == false) {
- LOG_INFO("Read server supported features failed for device %s",
- bd_addr.ToString().c_str());
- }
+ if (gatt_profile_get_eatt_support(bd_addr)) {
+ LOG_DEBUG("Eatt is supported for device %s", bd_addr.ToString().c_str());
+ supported_features_cb(role, bd_addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK);
+ return;
+ }
+
+ /* If we don't know yet, read GATT server supported features. */
+ if (gatt_cl_read_sr_supp_feat_req(
+ bd_addr, base::BindOnce(&eatt_impl::supported_features_cb,
+ base::Unretained(this), role)) == false) {
+ LOG_INFO("Read server supported features failed for device %s",
+ bd_addr.ToString().c_str());
}
}
diff --git a/system/stack/test/btm/stack_btm_test.cc b/system/stack/test/btm/stack_btm_test.cc
index ae8bd9359a..e278e44869 100644
--- a/system/stack/test/btm/stack_btm_test.cc
+++ b/system/stack/test/btm/stack_btm_test.cc
@@ -74,6 +74,7 @@ bool get_pts_force_eatt_for_notifications(void) { return false; }
bool get_pts_connect_eatt_unconditionally(void) { return false; }
bool get_pts_connect_eatt_before_encryption(void) { return false; }
bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
config_t* get_all(void) { return nullptr; }
const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
@@ -92,6 +93,8 @@ stack_config_t mock_stack_config{
.get_pts_connect_eatt_before_encryption =
get_pts_connect_eatt_before_encryption,
.get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+ .get_pts_eatt_peripheral_collision_support =
+ get_pts_eatt_peripheral_collision_support,
.get_all = get_all,
};
const stack_config_t* stack_config_get_interface(void) {
diff --git a/system/stack/test/eatt/eatt_test.cc b/system/stack/test/eatt/eatt_test.cc
index 98d5a10d9e..fbd060fdad 100644
--- a/system/stack/test/eatt/eatt_test.cc
+++ b/system/stack/test/eatt/eatt_test.cc
@@ -62,7 +62,8 @@ const RawAddress test_address({0x11, 0x11, 0x11, 0x11, 0x11, 0x11});
class EattTest : public testing::Test {
protected:
- void ConnectDeviceEattSupported(int num_of_accepted_connections) {
+ void ConnectDeviceEattSupported(int num_of_accepted_connections,
+ bool collision = false) {
ON_CALL(gatt_interface_, ClientReadSupportedFeatures)
.WillByDefault(
[](const RawAddress& addr,
@@ -80,6 +81,14 @@ class EattTest : public testing::Test {
eatt_instance_->Connect(test_address);
+ if (collision) {
+ EXPECT_CALL(l2cap_interface_,
+ ConnectCreditBasedReq(BT_PSM_EATT, test_address, _))
+ .Times(1);
+
+ l2cap_app_info_.pL2CA_CreditBasedCollisionInd_Cb(test_address);
+ }
+
int i = 0;
for (uint16_t cid : test_local_cids) {
EattChannel* channel =
@@ -509,4 +518,10 @@ TEST_F(EattTest, DoubleDisconnect) {
/* Force second disconnect */
eatt_instance_->Disconnect(test_address);
}
+
+TEST_F(EattTest, TestCollisionHandling) {
+ ConnectDeviceEattSupported(0, true /* collision*/);
+ ConnectDeviceEattSupported(5, true /* collision*/);
+}
+
} // namespace
diff --git a/system/stack/test/gatt/mock_gatt_utils_ref.cc b/system/stack/test/gatt/mock_gatt_utils_ref.cc
index cb01fd6d80..d416e98906 100644
--- a/system/stack/test/gatt/mock_gatt_utils_ref.cc
+++ b/system/stack/test/gatt/mock_gatt_utils_ref.cc
@@ -21,9 +21,6 @@
#include "types/raw_address.h"
#include "utils/include/bt_utils.h"
-/** stack/btu/btu_task.cc, indirect reference, gatt_utils.cc -> libosi */
-bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-
/** stack/gatt/connection_manager.cc */
namespace connection_manager {
bool background_connect_remove(uint8_t app_id, const RawAddress& address) {
diff --git a/system/stack/test/gatt/stack_gatt_test.cc b/system/stack/test/gatt/stack_gatt_test.cc
index 7e977713ff..38d6cc6647 100644
--- a/system/stack/test/gatt/stack_gatt_test.cc
+++ b/system/stack/test/gatt/stack_gatt_test.cc
@@ -33,8 +33,6 @@ std::map<std::string, int> mock_function_count_map;
void LogMsg(uint32_t trace_set_mask, const char* fmt_str, ...) {}
-bluetooth::common::MessageLoopThread* get_main_thread() { return nullptr; }
-
class StackGattTest : public ::testing::Test {};
namespace {
diff --git a/system/stack/test/stack_smp_test.cc b/system/stack/test/stack_smp_test.cc
index 2a20b6e1f0..abf10b9edf 100644
--- a/system/stack/test/stack_smp_test.cc
+++ b/system/stack/test/stack_smp_test.cc
@@ -49,6 +49,7 @@ bool get_pts_force_eatt_for_notifications(void) { return false; }
bool get_pts_connect_eatt_unconditionally(void) { return false; }
bool get_pts_connect_eatt_before_encryption(void) { return false; }
bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
config_t* get_all(void) { return nullptr; }
const packet_fragmenter_t* packet_fragmenter_get_interface() { return nullptr; }
@@ -67,6 +68,8 @@ stack_config_t mock_stack_config{
.get_pts_connect_eatt_before_encryption =
get_pts_connect_eatt_before_encryption,
.get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+ .get_pts_eatt_peripheral_collision_support =
+ get_pts_eatt_peripheral_collision_support,
.get_all = get_all,
};
const stack_config_t* stack_config_get_interface(void) {
diff --git a/system/test/common/stack_config.cc b/system/test/common/stack_config.cc
index aed9525489..becbabd496 100644
--- a/system/test/common/stack_config.cc
+++ b/system/test/common/stack_config.cc
@@ -34,6 +34,7 @@ bool get_pts_force_eatt_for_notifications(void) { return false; }
bool get_pts_connect_eatt_unconditionally(void) { return false; }
bool get_pts_connect_eatt_before_encryption(void) { return false; }
bool get_pts_unencrypt_broadcast(void) { return false; }
+bool get_pts_eatt_peripheral_collision_support(void) { return false; }
struct config_t;
config_t* get_all(void) { return nullptr; }
struct packet_fragmenter_t;
@@ -54,6 +55,8 @@ stack_config_t mock_stack_config{
.get_pts_connect_eatt_before_encryption =
get_pts_connect_eatt_before_encryption,
.get_pts_unencrypt_broadcast = get_pts_unencrypt_broadcast,
+ .get_pts_eatt_peripheral_collision_support =
+ get_pts_eatt_peripheral_collision_support,
.get_all = get_all,
};