/* * Copyright 2020 HIMSA II K/S - www.himsa.dk. * Represented by EHIMA - www.ehima.com * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include "bta/test/common/fake_osi.h" #include "hci/controller_interface_mock.h" #include "stack/include/bt_hdr.h" #include "stack/include/bt_psm_types.h" #include "stack/include/l2cdefs.h" #include "stack/test/common/mock_btif_storage.h" #include "stack/test/common/mock_btm_api_layer.h" #include "stack/test/common/mock_eatt.h" #include "stack/test/common/mock_gatt_layer.h" #include "stack/test/common/mock_l2cap_layer.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_stack_l2cap_interface.h" #include "types/raw_address.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" using testing::_; using testing::DoAll; using testing::MockFunction; using testing::NiceMock; using testing::NotNull; using testing::Return; using testing::SaveArg; using testing::SaveArgPointee; using testing::StrictMock; using bluetooth::eatt::EattChannel; using bluetooth::eatt::EattChannelState; using namespace bluetooth; #define BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK 0x01 extern struct fake_osi_alarm_set_on_mloop fake_osi_alarm_set_on_mloop_; /* Needed for testing context */ static tGATT_TCB test_tcb; void btif_storage_add_eatt_supported(const RawAddress& /*addr*/) { return; } void gatt_consolidate(const RawAddress& /*identity_addr*/, const RawAddress& /*rpa*/) {} void gatt_data_process(tGATT_TCB& /*tcb*/, uint16_t /*cid*/, BT_HDR* /*p_buf*/) { return; } tGATT_TCB* gatt_find_tcb_by_addr(const RawAddress& /*bda*/, tBT_TRANSPORT /*transport*/) { log::info(""); return &test_tcb; } namespace { const RawAddress test_address({0x11, 0x11, 0x11, 0x11, 0x11, 0x11}); std::vector test_local_cids{61, 62, 63, 64, 65}; class EattTest : public ::testing::Test { protected: void ConnectDeviceEattSupported(int num_of_accepted_connections, bool collision = false) { ON_CALL(gatt_interface_, ClientReadSupportedFeatures) .WillByDefault([](const RawAddress& addr, base::OnceCallback cb) { std::move(cb).Run(addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK); return true; }); EXPECT_CALL(gatt_interface_, GetEattSupport).WillRepeatedly([](const RawAddress& /*addr*/) { return true; }); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) .WillOnce(Return(test_local_cids)); ON_CALL(mock_stack_l2cap_interface_, L2CA_DisconnectReq(test_local_cids[0])) .WillByDefault(Return(true)); eatt_instance_->Connect(test_address); if (collision) { /* Collision should be handled only if all channels has been rejected in * first place.*/ if (num_of_accepted_connections == 0) { EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_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 = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel != nullptr); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_PENDING); if (i < num_of_accepted_connections) { l2cap_app_info_.pL2CA_CreditBasedConnectCfm_Cb( test_address, cid, EATT_MIN_MTU_MPS, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_CONN_OK); connected_cids_.push_back(cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->tx_mtu_ == EATT_MIN_MTU_MPS); } else { l2cap_app_info_.pL2CA_Error_Cb(cid, static_cast(tL2CAP_CONN::L2CAP_CONN_NO_RESOURCES)); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel == nullptr); } i++; } ASSERT_TRUE(test_tcb.eatt == num_of_accepted_connections); } void ConnectDeviceBothSides(int num_of_accepted_connections, std::vector& incoming_cids) { base::OnceCallback eatt_supp_feat_cb; ON_CALL(gatt_interface_, ClientReadSupportedFeatures) .WillByDefault( [&eatt_supp_feat_cb](const RawAddress& /*addr*/, base::OnceCallback cb) { eatt_supp_feat_cb = std::move(cb); return true; }); // Return false to trigger supported features request ON_CALL(gatt_interface_, GetEattSupport).WillByDefault([](const RawAddress& /*addr*/) { return false; }); std::vector test_local_cids{61, 62, 63, 64, 65}; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) .WillOnce(Return(test_local_cids)); eatt_instance_->Connect(test_address); // Let the remote connect while we are trying to connect EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedRsp(test_address, 1, incoming_cids, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_CONN_OK, _)) .WillOnce(Return(true)); l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1); // Respond to feature request scheduled by the connect request ASSERT_TRUE(eatt_supp_feat_cb); if (eatt_supp_feat_cb) { std::move(eatt_supp_feat_cb).Run(test_address, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK); } int i = 0; for (uint16_t cid : test_local_cids) { EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel != nullptr); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_PENDING); if (i < num_of_accepted_connections) { l2cap_app_info_.pL2CA_CreditBasedConnectCfm_Cb( test_address, cid, EATT_MIN_MTU_MPS, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_CONN_OK); connected_cids_.push_back(cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->tx_mtu_ == EATT_MIN_MTU_MPS); } else { l2cap_app_info_.pL2CA_Error_Cb(cid, static_cast(tL2CAP_CONN::L2CAP_CONN_NO_RESOURCES)); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel == nullptr); } i++; } // Check the incoming CIDs as well for (auto cid : incoming_cids) { EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_NE(nullptr, channel); ASSERT_EQ(channel->state_, EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->tx_mtu_ == EATT_MIN_MTU_MPS); } ASSERT_EQ(test_tcb.eatt, num_of_accepted_connections + incoming_cids.size()); } void DisconnectEattByPeer(void) { for (uint16_t cid : connected_cids_) { l2cap_app_info_.pL2CA_DisconnectInd_Cb(cid, true); } ASSERT_EQ(0, test_tcb.eatt); } void DisconnectEattDevice(std::vector cids) { EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_DisconnectReq(_)).Times(cids.size()); eatt_instance_->Disconnect(test_address); ASSERT_EQ(0, test_tcb.eatt); } void SetUp() override { bluetooth::testing::stack::l2cap::set_interface(&mock_stack_l2cap_interface_); tL2CAP_APPL_INFO l2cap_callbacks{}; le_buffer_size_.le_data_packet_length_ = 128; le_buffer_size_.total_num_le_packets_ = 24; EXPECT_CALL(controller_, GetLeBufferSize).WillRepeatedly(Return(le_buffer_size_)); bluetooth::l2cap::SetMockInterface(&l2cap_interface_); bluetooth::manager::SetMockBtmApiInterface(&btm_api_interface_); bluetooth::manager::SetMockBtifStorageInterface(&btif_storage_interface_); bluetooth::gatt::SetMockGattInterface(&gatt_interface_); bluetooth::hci::testing::mock_controller_ = &controller_; // Clear the static memory for each test case memset(&test_tcb, 0, sizeof(test_tcb)); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_RegisterLECoc(BT_PSM_EATT, _, _, _)) .WillOnce(DoAll(SaveArg<1>(&l2cap_app_info_), ::testing::ReturnArg<0>())); ON_CALL(btif_storage_interface_, LoadBondedEatt).WillByDefault([]() { return; }); hci_role_ = HCI_ROLE_CENTRAL; EXPECT_CALL(l2cap_interface_, LeCreditDefault()).WillRepeatedly(DoAll(Return(0xfff))); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_GetBleConnRole(_)) .WillRepeatedly(DoAll(Return(hci_role_))); eatt_instance_ = EattExtension::GetInstance(); eatt_instance_->Start(); Test::SetUp(); } void TearDown() override { com::android::bluetooth::flags::provider_->reset_flags(); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_DeregisterLECoc(BT_PSM_EATT)).Times(1); eatt_instance_->Stop(); eatt_instance_ = nullptr; hci_role_ = HCI_ROLE_CENTRAL; connected_cids_.clear(); bluetooth::gatt::SetMockGattInterface(nullptr); bluetooth::l2cap::SetMockInterface(nullptr); bluetooth::testing::stack::l2cap::reset_interface(); bluetooth::manager::SetMockBtifStorageInterface(nullptr); bluetooth::manager::SetMockBtmApiInterface(nullptr); bluetooth::hci::testing::mock_controller_ = nullptr; Test::TearDown(); } tL2CAP_APPL_INFO reg_info_; bluetooth::manager::MockBtifStorageInterface btif_storage_interface_; bluetooth::manager::MockBtmApiInterface btm_api_interface_; bluetooth::l2cap::MockL2capInterface l2cap_interface_; bluetooth::testing::stack::l2cap::Mock mock_stack_l2cap_interface_; bluetooth::gatt::MockGattInterface gatt_interface_; bluetooth::hci::testing::MockControllerInterface controller_; bluetooth::hci::LeBufferSize le_buffer_size_; tL2CAP_APPL_INFO l2cap_app_info_; EattExtension* eatt_instance_; std::vector connected_cids_; tHCI_ROLE hci_role_ = HCI_ROLE_CENTRAL; }; TEST_F(EattTest, ConnectSucceed) { ConnectDeviceEattSupported(1); DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, IncomingEattConnectionByUnknownDevice) { std::vector incoming_cids{71, 72, 73, 74, 75}; ON_CALL(btm_api_interface_, IsEncrypted) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return true; }); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedRsp(test_address, 1, incoming_cids, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_CONN_OK, _)) .WillOnce(Return(true)); l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1); DisconnectEattDevice(incoming_cids); } TEST_F(EattTest, IncomingEattConnectionByKnownDevice) { hci_role_ = HCI_ROLE_PERIPHERAL; ON_CALL(btm_api_interface_, IsEncrypted) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return true; }); ON_CALL(gatt_interface_, ClientReadSupportedFeatures) .WillByDefault([](const RawAddress& addr, base::OnceCallback cb) { std::move(cb).Run(addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK); return true; }); ON_CALL(gatt_interface_, GetEattSupport).WillByDefault([](const RawAddress& /*addr*/) { return true; }); eatt_instance_->Connect(test_address); std::vector incoming_cids{71, 72, 73, 74, 75}; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedRsp(test_address, 1, incoming_cids, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_CONN_OK, _)) .WillOnce(Return(true)); l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1); DisconnectEattDevice(incoming_cids); hci_role_ = HCI_ROLE_CENTRAL; } TEST_F(EattTest, IncomingEattConnectionByKnownDeviceEncryptionOff) { hci_role_ = HCI_ROLE_PERIPHERAL; ON_CALL(btm_api_interface_, IsEncrypted) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return false; }); ON_CALL(btm_api_interface_, IsLinkKeyKnown) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return true; }); ON_CALL(gatt_interface_, ClientReadSupportedFeatures) .WillByDefault([](const RawAddress& addr, base::OnceCallback cb) { std::move(cb).Run(addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK); return true; }); ON_CALL(gatt_interface_, GetEattSupport).WillByDefault([](const RawAddress& /*addr*/) { return true; }); eatt_instance_->Connect(test_address); std::vector incoming_cids{71, 72, 73, 74, 75}; EXPECT_CALL( mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedRsp(test_address, 1, _, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_INSUFFICIENT_ENCRYP, _)) .WillOnce(Return(true)); l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1); hci_role_ = HCI_ROLE_CENTRAL; } TEST_F(EattTest, IncomingEattConnectionByUnknownDeviceEncryptionOff) { std::vector incoming_cids{71, 72, 73, 74, 75}; ON_CALL(btm_api_interface_, IsEncrypted) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return false; }); ON_CALL(btm_api_interface_, IsLinkKeyKnown) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return false; }); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedRsp( test_address, 1, _, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_INSUFFICIENT_AUTHENTICATION, _)) .WillOnce(Return(true)); l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1); } TEST_F(EattTest, ReconnectInitiatedByRemoteSucceed) { ConnectDeviceEattSupported(1); DisconnectEattDevice(connected_cids_); std::vector incoming_cids{71, 72, 73, 74, 75}; ON_CALL(btm_api_interface_, IsEncrypted) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return true; }); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedRsp(test_address, 1, incoming_cids, tL2CAP_LE_RESULT_CODE::L2CAP_LE_RESULT_CONN_OK, _)) .WillOnce(Return(true)); l2cap_app_info_.pL2CA_CreditBasedConnectInd_Cb(test_address, incoming_cids, BT_PSM_EATT, EATT_MIN_MTU_MPS, 1); DisconnectEattDevice(incoming_cids); } TEST_F(EattTest, ConnectInitiatedWhenRemoteConnects) { ON_CALL(btm_api_interface_, IsEncrypted) .WillByDefault( [](const RawAddress& /*addr*/, tBT_TRANSPORT /*transport*/) { return true; }); std::vector incoming_cids{71, 72, 73, 74}; ConnectDeviceBothSides(1, incoming_cids); std::vector disconnecting_cids; disconnecting_cids.insert(disconnecting_cids.end(), incoming_cids.begin(), incoming_cids.end()); disconnecting_cids.insert(disconnecting_cids.end(), connected_cids_.begin(), connected_cids_.end()); DisconnectEattDevice(disconnecting_cids); } TEST_F(EattTest, ConnectSucceedMultipleChannels) { ConnectDeviceEattSupported(5); DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, ConnectFailedEattNotSupported) { ON_CALL(gatt_interface_, ClientReadSupportedFeatures) .WillByDefault([](const RawAddress& addr, base::OnceCallback cb) { std::move(cb).Run(addr, 0); return true; }); ON_CALL(gatt_interface_, GetEattSupport).WillByDefault([](const RawAddress& /*addr*/) { return false; }); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) .Times(0); eatt_instance_->Connect(test_address); ASSERT_TRUE(eatt_instance_->IsEattSupportedByPeer(test_address) == false); } TEST_F(EattTest, ConnectFailedSlaveOnTheLink) { EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) .Times(0); hci_role_ = HCI_ROLE_PERIPHERAL; eatt_instance_->Connect(test_address); /* Back to default btm role */ hci_role_ = HCI_ROLE_CENTRAL; } TEST_F(EattTest, DisonnectByPeerSucceed) { ConnectDeviceEattSupported(2); uint16_t cid = connected_cids_[0]; EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); DisconnectEattByPeer(); channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel == nullptr); } TEST_F(EattTest, ReconfigAllSucceed) { ConnectDeviceEattSupported(3); std::vector cids; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ReconfigCreditBasedConnsReq(_, _, _)) .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); uint16_t new_mtu = 300; eatt_instance_->ReconfigureAll(test_address, new_mtu); ASSERT_TRUE(cids.size() == connected_cids_.size()); tL2CAP_LE_CFG_INFO cfg = {.result = tL2CAP_CFG_RESULT::L2CAP_CFG_OK, .mtu = new_mtu}; for (uint16_t cid : cids) { l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, true, &cfg); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->rx_mtu_ == new_mtu); } DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, ReconfigAllFailed) { ConnectDeviceEattSupported(4); std::vector cids; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ReconfigCreditBasedConnsReq(_, _, _)) .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); uint16_t new_mtu = 300; eatt_instance_->ReconfigureAll(test_address, new_mtu); ASSERT_TRUE(cids.size() == connected_cids_.size()); tL2CAP_LE_CFG_INFO cfg = {.result = tL2CAP_CFG_RESULT::L2CAP_CFG_FAILED_NO_REASON, .mtu = new_mtu}; for (uint16_t cid : cids) { l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, true, &cfg); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->rx_mtu_ != new_mtu); } DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, ReconfigSingleSucceed) { ConnectDeviceEattSupported(2); std::vector cids; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ReconfigCreditBasedConnsReq(_, _, _)) .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); uint16_t new_mtu = 300; eatt_instance_->Reconfigure(test_address, connected_cids_[1], new_mtu); ASSERT_EQ(1U, cids.size()); tL2CAP_LE_CFG_INFO cfg = {.result = tL2CAP_CFG_RESULT::L2CAP_CFG_OK, .mtu = new_mtu}; auto it = std::find(connected_cids_.begin(), connected_cids_.end(), cids[0]); ASSERT_TRUE(it != connected_cids_.end()); l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cids[0], true, &cfg); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cids[0]); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->rx_mtu_ == new_mtu); DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, ReconfigSingleFailed) { ConnectDeviceEattSupported(2); std::vector cids; EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ReconfigCreditBasedConnsReq(_, _, _)) .WillOnce(DoAll(SaveArg<1>(&cids), Return(true))); uint16_t new_mtu = 300; eatt_instance_->ReconfigureAll(test_address, new_mtu); ASSERT_TRUE(cids.size() == connected_cids_.size()); tL2CAP_LE_CFG_INFO cfg = {.result = tL2CAP_CFG_RESULT::L2CAP_CFG_FAILED_NO_REASON, .mtu = new_mtu}; auto it = std::find(connected_cids_.begin(), connected_cids_.end(), cids[0]); ASSERT_TRUE(it != connected_cids_.end()); l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cids[0], true, &cfg); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cids[0]); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->rx_mtu_ != new_mtu); DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, ReconfigPeerSucceed) { ConnectDeviceEattSupported(3); uint16_t new_mtu = 300; tL2CAP_LE_CFG_INFO cfg = {.result = tL2CAP_CFG_RESULT::L2CAP_CFG_OK, .mtu = new_mtu}; for (uint16_t cid : connected_cids_) { l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, false, &cfg); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->tx_mtu_ == new_mtu); } DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, ReconfigPeerFailed) { ConnectDeviceEattSupported(2); uint16_t new_mtu = 300; tL2CAP_LE_CFG_INFO cfg = {.result = tL2CAP_CFG_RESULT::L2CAP_CFG_FAILED_NO_REASON, .mtu = new_mtu}; for (uint16_t cid : connected_cids_) { l2cap_app_info_.pL2CA_CreditBasedReconfigCompleted_Cb(test_address, cid, false, &cfg); EattChannel* channel = eatt_instance_->FindEattChannelByCid(test_address, cid); ASSERT_TRUE(channel->state_ == EattChannelState::EATT_CHANNEL_OPENED); ASSERT_TRUE(channel->tx_mtu_ != new_mtu); } DisconnectEattDevice(connected_cids_); } TEST_F(EattTest, DoubleDisconnect) { ConnectDeviceEattSupported(1); DisconnectEattDevice(connected_cids_); /* Force second disconnect */ eatt_instance_->Disconnect(test_address); } TEST_F(EattTest, TestCollisionHandling) { ConnectDeviceEattSupported(0, true /* collision*/); ConnectDeviceEattSupported(5, true /* collision*/); } TEST_F(EattTest, ChannelUnavailableWhileOpening) { // arrange ON_CALL(gatt_interface_, ClientReadSupportedFeatures) .WillByDefault([](const RawAddress& addr, base::OnceCallback cb) { std::move(cb).Run(addr, BLE_GATT_SVR_SUP_FEAT_EATT_BITMASK); return true; }); ON_CALL(gatt_interface_, GetEattSupport).WillByDefault(Return(true)); // expect EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_ConnectCreditBasedReq(BT_PSM_EATT, test_address, _)) .WillOnce(Return(std::vector{61})); // act: start eatt_instance_->Connect(test_address); auto available_channel_for_request = eatt_instance_->GetChannelAvailableForClientRequest(test_address); auto available_channel_for_indication = eatt_instance_->GetChannelAvailableForIndication(test_address); // assert ASSERT_EQ(available_channel_for_request, nullptr); ASSERT_EQ(available_channel_for_indication, nullptr); } TEST_F(EattTest, ChannelUnavailableWhileReconfiguring) { // arrange ON_CALL(mock_stack_l2cap_interface_, L2CA_ReconfigCreditBasedConnsReq(_, _, _)) .WillByDefault(Return(true)); ConnectDeviceEattSupported(/* num_of_accepted_connections = */ 1); // act: reconfigure, then get available channels eatt_instance_->Reconfigure(test_address, connected_cids_[0], 300); auto available_channel_for_request = eatt_instance_->GetChannelAvailableForClientRequest(test_address); auto available_channel_for_indication = eatt_instance_->GetChannelAvailableForIndication(test_address); // assert ASSERT_EQ(available_channel_for_request, nullptr); ASSERT_EQ(available_channel_for_indication, nullptr); } TEST_F(EattTest, DisconnectChannelOnIndicationConfirmationTimeout) { com::android::bluetooth::flags::provider_->gatt_disconnect_fix(true); ConnectDeviceEattSupported(1); eatt_instance_->StartIndicationConfirmationTimer(test_address, test_local_cids[0]); EXPECT_CALL(mock_stack_l2cap_interface_, L2CA_DisconnectReq(test_local_cids[0])).Times(1); fake_osi_alarm_set_on_mloop_.cb(fake_osi_alarm_set_on_mloop_.data); } } // namespace