/* * Copyright 2020 HIMSA II K/S - www.himsa.com. * 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 "btm_iso_api.h" #include "device/include/controller.h" #include "main/shim/shim.h" #include "mock_controller.h" #include "mock_hcic_layer.h" using bluetooth::hci::IsoManager; using testing::_; using testing::AnyNumber; using testing::AtLeast; using testing::Eq; using testing::Matcher; using testing::Return; using testing::SaveArg; using testing::StrictMock; using testing::Test; // Iso Manager currently works on top of the legacy HCI layer bool bluetooth::shim::is_gd_shim_enabled() { return false; } namespace bte { class BteInterface { public: virtual void HciSend(BT_HDR* p_msg, uint16_t event) = 0; virtual ~BteInterface() = default; }; class MockBteInterface : public BteInterface { public: MOCK_METHOD((void), HciSend, (BT_HDR * p_msg, uint16_t event), (override)); }; static MockBteInterface* bte_interface = nullptr; static void SetMockBteInterface(MockBteInterface* interface) { bte_interface = interface; } } // namespace bte void bte_main_hci_send(BT_HDR* p_msg, uint16_t event) { bte::bte_interface->HciSend(p_msg, event); osi_free(p_msg); } namespace { class MockCigCallbacks : public bluetooth::hci::iso_manager::CigCallbacks { public: MockCigCallbacks() = default; ~MockCigCallbacks() override = default; MOCK_METHOD((void), OnSetupIsoDataPath, (uint8_t status, uint16_t conn_handle, uint8_t cig_id), (override)); MOCK_METHOD((void), OnRemoveIsoDataPath, (uint8_t status, uint16_t conn_handle, uint8_t cig_id), (override)); MOCK_METHOD((void), OnIsoLinkQualityRead, (uint8_t conn_handle, uint8_t cig_id, uint32_t txUnackedPackets, uint32_t txFlushedPackets, uint32_t txLastSubeventPackets, uint32_t retransmittedPackets, uint32_t crcErrorPackets, uint32_t rxUnreceivedPackets, uint32_t duplicatePackets), (override)); MOCK_METHOD((void), OnCisEvent, (uint8_t event, void* data), (override)); MOCK_METHOD((void), OnCigEvent, (uint8_t event, void* data), (override)); private: DISALLOW_COPY_AND_ASSIGN(MockCigCallbacks); }; class MockBigCallbacks : public bluetooth::hci::iso_manager::BigCallbacks { public: MockBigCallbacks() = default; ~MockBigCallbacks() override = default; MOCK_METHOD((void), OnSetupIsoDataPath, (uint8_t status, uint16_t conn_handle, uint8_t big_id), (override)); MOCK_METHOD((void), OnRemoveIsoDataPath, (uint8_t status, uint16_t conn_handle, uint8_t big_id), (override)); MOCK_METHOD((void), OnBigEvent, (uint8_t event, void* data), (override)); private: DISALLOW_COPY_AND_ASSIGN(MockBigCallbacks); }; } // namespace class IsoManagerTest : public Test { protected: void SetUp() override { bte::SetMockBteInterface(&bte_interface_); hcic::SetMockHcicInterface(&hcic_interface_); controller::SetMockControllerInterface(&controller_interface_); big_callbacks_.reset(new MockBigCallbacks()); cig_callbacks_.reset(new MockCigCallbacks()); EXPECT_CALL(controller_interface_, GetIsoBufferCount()) .Times(AtLeast(1)) .WillRepeatedly(Return(6)); EXPECT_CALL(controller_interface_, GetIsoDataSize()) .Times(AtLeast(1)) .WillRepeatedly(Return(1024)); InitIsoManager(); } void TearDown() override { CleanupIsoManager(); big_callbacks_.reset(); cig_callbacks_.reset(); bte::SetMockBteInterface(nullptr); hcic::SetMockHcicInterface(nullptr); controller::SetMockControllerInterface(nullptr); } virtual void InitIsoManager() { manager_instance_ = IsoManager::GetInstance(); manager_instance_->Start(); manager_instance_->RegisterCigCallbacks(cig_callbacks_.get()); manager_instance_->RegisterBigCallbacks(big_callbacks_.get()); // Default mock SetCigParams action volatile_test_cig_create_cmpl_evt_ = kDefaultCigParamsEvt; ON_CALL(hcic_interface_, SetCigParams) .WillByDefault([this](auto cig_id, auto, base::OnceCallback cb) { uint8_t hci_mock_rsp_buffer [3 + sizeof(uint16_t) * this->volatile_test_cig_create_cmpl_evt_ .conn_handles.size()]; uint8_t* p = hci_mock_rsp_buffer; UINT8_TO_STREAM(p, this->volatile_test_cig_create_cmpl_evt_.status); UINT8_TO_STREAM(p, cig_id); UINT8_TO_STREAM( p, this->volatile_test_cig_create_cmpl_evt_.conn_handles.size()); for (auto handle : this->volatile_test_cig_create_cmpl_evt_.conn_handles) { UINT16_TO_STREAM(p, handle); } std::move(cb).Run( hci_mock_rsp_buffer, 3 + sizeof(uint16_t) * this->volatile_test_cig_create_cmpl_evt_ .conn_handles.size()); return 0; }); // Default mock CreateCis action ON_CALL(hcic_interface_, CreateCis) .WillByDefault([](uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg, base::OnceCallback cb) { for (const EXT_CIS_CREATE_CFG* cis = cis_cfg; num_cis != 0; num_cis--, cis++) { std::vector buf(28); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, cis->cis_conn_handle); UINT24_TO_STREAM(p, 0xEA); // CIG sync delay UINT24_TO_STREAM(p, 0xEB); // CIS sync delay UINT24_TO_STREAM(p, 0xEC); // transport latency mtos UINT24_TO_STREAM(p, 0xED); // transport latency stom UINT8_TO_STREAM(p, 0x01); // phy mtos UINT8_TO_STREAM(p, 0x02); // phy stom UINT8_TO_STREAM(p, 0x01); // nse UINT8_TO_STREAM(p, 0x02); // bn mtos UINT8_TO_STREAM(p, 0x03); // bn stom UINT8_TO_STREAM(p, 0x04); // ft mtos UINT8_TO_STREAM(p, 0x05); // ft stom UINT16_TO_STREAM(p, 0x00FA); // Max PDU mtos UINT16_TO_STREAM(p, 0x00FB); // Max PDU stom UINT16_TO_STREAM(p, 0x0C60); // ISO interval IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT, buf.data(), buf.size()); } }); // Default mock disconnect action ON_CALL(hcic_interface_, Disconnect) .WillByDefault([](uint16_t handle, uint8_t reason) { IsoManager::GetInstance()->HandleDisconnect(handle, reason); }); // Default mock CreateBig HCI action volatile_test_big_params_evt_ = kDefaultBigParamsEvt; ON_CALL(hcic_interface_, CreateBig) .WillByDefault( [this](auto big_handle, bluetooth::hci::iso_manager::big_create_params big_params) { std::vector buf(big_params.num_bis * sizeof(uint16_t) + 18); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT8_TO_STREAM(p, big_handle); ASSERT_TRUE(big_params.num_bis <= volatile_test_big_params_evt_.conn_handles.size()); UINT24_TO_STREAM(p, volatile_test_big_params_evt_.big_sync_delay); UINT24_TO_STREAM( p, volatile_test_big_params_evt_.transport_latency_big); UINT8_TO_STREAM(p, big_params.phy); UINT8_TO_STREAM(p, volatile_test_big_params_evt_.nse); UINT8_TO_STREAM(p, volatile_test_big_params_evt_.bn); UINT8_TO_STREAM(p, volatile_test_big_params_evt_.pto); UINT8_TO_STREAM(p, volatile_test_big_params_evt_.irc); UINT16_TO_STREAM(p, volatile_test_big_params_evt_.max_pdu); UINT16_TO_STREAM(p, volatile_test_big_params_evt_.iso_interval); UINT8_TO_STREAM(p, big_params.num_bis); for (auto i = 0; i < big_params.num_bis; ++i) { UINT16_TO_STREAM(p, volatile_test_big_params_evt_.conn_handles[i]); } IsoManager::GetInstance()->HandleHciEvent( HCI_BLE_CREATE_BIG_CPL_EVT, buf.data(), buf.size()); }); // Default mock TerminateBig HCI action ON_CALL(hcic_interface_, TerminateBig) .WillByDefault([](auto big_handle, uint8_t reason) { std::vector buf(2); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, big_handle); UINT8_TO_STREAM(p, reason); IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, buf.data(), buf.size()); }); // Default mock SetupIsoDataPath HCI action ON_CALL(hcic_interface_, SetupIsoDataPath) .WillByDefault( [](uint16_t iso_handle, uint8_t /* data_path_dir */, uint8_t /* data_path_id */, uint8_t /* codec_id_format */, uint16_t /* codec_id_company */, uint16_t /* codec_id_vendor */, uint32_t /* controller_delay */, std::vector /* codec_conf */, base::OnceCallback cb) { std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, iso_handle); std::move(cb).Run(buf.data(), buf.size()); }); // Default mock RemoveIsoDataPath HCI action ON_CALL(hcic_interface_, RemoveIsoDataPath) .WillByDefault([](uint16_t iso_handle, uint8_t data_path_dir, base::OnceCallback cb) { std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, iso_handle); std::move(cb).Run(buf.data(), buf.size()); }); } virtual void CleanupIsoManager() { manager_instance_->Stop(); manager_instance_ = nullptr; } static const bluetooth::hci::iso_manager::big_create_params kDefaultBigParams; static const bluetooth::hci::iso_manager::cig_create_params kDefaultCigParams; static const bluetooth::hci::iso_manager::cig_create_params kDefaultCigParams2; static const bluetooth::hci::iso_manager::cig_create_cmpl_evt kDefaultCigParamsEvt; static const bluetooth::hci::iso_manager::big_create_cmpl_evt kDefaultBigParamsEvt; static const bluetooth::hci::iso_manager::iso_data_path_params kDefaultIsoDataPathParams; bluetooth::hci::iso_manager::cig_create_cmpl_evt volatile_test_cig_create_cmpl_evt_; bluetooth::hci::iso_manager::big_create_cmpl_evt volatile_test_big_params_evt_; IsoManager* manager_instance_; bte::MockBteInterface bte_interface_; hcic::MockHcicInterface hcic_interface_; controller::MockControllerInterface controller_interface_; std::unique_ptr big_callbacks_; std::unique_ptr cig_callbacks_; }; const bluetooth::hci::iso_manager::cig_create_cmpl_evt IsoManagerTest::kDefaultCigParamsEvt = { .cig_id = 128, .status = 0x00, .conn_handles = std::vector({0x0EFF, 0x00FF}), }; const bluetooth::hci::iso_manager::big_create_cmpl_evt IsoManagerTest::kDefaultBigParamsEvt = { .status = 0x00, .big_id = 0, .big_sync_delay = 0x0080de, .transport_latency_big = 0x00cefe, .phy = 0x02, .nse = 4, .bn = 1, .pto = 0, .irc = 4, .max_pdu = 108, .iso_interval = 6, .conn_handles = std::vector({0x0EFE, 0x0E00}), }; const bluetooth::hci::iso_manager::iso_data_path_params IsoManagerTest::kDefaultIsoDataPathParams = { .data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut, .data_path_id = bluetooth::hci::iso_manager::kIsoDataPathHci, .codec_id_format = 0x06, .codec_id_company = 0, .codec_id_vendor = 0, .controller_delay = 0, .codec_conf = {0x02, 0x01, 0x02}, }; const bluetooth::hci::iso_manager::big_create_params IsoManagerTest::kDefaultBigParams = { .adv_handle = 0x00, .num_bis = 2, .sdu_itv = 0x002710, .max_sdu_size = 108, .max_transport_latency = 0x3c, .rtn = 3, .phy = 0x02, .packing = 0x00, .framing = 0x00, .enc = 0, .enc_code = std::array({0}), }; const bluetooth::hci::iso_manager::cig_create_params IsoManagerTest::kDefaultCigParams = { .sdu_itv_mtos = 0x00002710, .sdu_itv_stom = 0x00002711, .sca = bluetooth::hci::iso_manager::kIsoSca0To20Ppm, .packing = 0x00, .framing = 0x01, .max_trans_lat_stom = 0x000A, .max_trans_lat_mtos = 0x0009, .cis_cfgs = { // CIS #1 { .cis_id = 1, .max_sdu_size_mtos = 0x0028, .max_sdu_size_stom = 0x0027, .phy_mtos = 0x04, .phy_stom = 0x03, .rtn_mtos = 0x02, .rtn_stom = 0x01, }, // CIS #2 { .cis_id = 2, .max_sdu_size_mtos = 0x0029, .max_sdu_size_stom = 0x002A, .phy_mtos = 0x09, .phy_stom = 0x08, .rtn_mtos = 0x07, .rtn_stom = 0x06, }, }, }; const bluetooth::hci::iso_manager::cig_create_params IsoManagerTest::kDefaultCigParams2 = { .sdu_itv_mtos = 0x00002709, .sdu_itv_stom = 0x00002700, .sca = bluetooth::hci::iso_manager::kIsoSca0To20Ppm, .packing = 0x01, .framing = 0x00, .max_trans_lat_stom = 0x000B, .max_trans_lat_mtos = 0x0006, .cis_cfgs = { // CIS #1 { .cis_id = 1, .max_sdu_size_mtos = 0x0022, .max_sdu_size_stom = 0x0022, .phy_mtos = 0x01, .phy_stom = 0x02, .rtn_mtos = 0x02, .rtn_stom = 0x01, }, // CIS #2 { .cis_id = 2, .max_sdu_size_mtos = 0x002A, .max_sdu_size_stom = 0x002B, .phy_mtos = 0x06, .phy_stom = 0x06, .rtn_mtos = 0x07, .rtn_stom = 0x07, }, }, }; class IsoManagerDeathTest : public IsoManagerTest {}; class IsoManagerDeathTestNoInit : public IsoManagerTest { protected: void InitIsoManager() override { /* DO NOTHING */ } void CleanupIsoManager() override { /* DO NOTHING */ } }; class IsoManagerDeathTestNoCleanup : public IsoManagerTest { protected: void CleanupIsoManager() override { /* DO NOTHING */ } }; bool operator==(const EXT_CIS_CFG& x, const EXT_CIS_CFG& y) { return ((x.cis_id == y.cis_id) && (x.max_sdu_size_mtos == y.max_sdu_size_mtos) && (x.max_sdu_size_stom == y.max_sdu_size_stom) && (x.phy_mtos == y.phy_mtos) && (x.phy_stom == y.phy_stom) && (x.rtn_mtos == y.rtn_mtos) && (x.rtn_stom == y.rtn_stom)); } bool operator==( const struct bluetooth::hci::iso_manager::cig_create_params& x, const struct bluetooth::hci::iso_manager::cig_create_params& y) { return ((x.sdu_itv_mtos == y.sdu_itv_mtos) && (x.sdu_itv_stom == y.sdu_itv_stom) && (x.sca == y.sca) && (x.packing == y.packing) && (x.framing == y.framing) && (x.max_trans_lat_stom == y.max_trans_lat_stom) && (x.max_trans_lat_mtos == y.max_trans_lat_mtos) && std::is_permutation(x.cis_cfgs.begin(), x.cis_cfgs.end(), y.cis_cfgs.begin())); } bool operator==( const struct bluetooth::hci::iso_manager::big_create_params& x, const struct bluetooth::hci::iso_manager::big_create_params& y) { return ((x.adv_handle == y.adv_handle) && (x.num_bis == y.num_bis) && (x.sdu_itv == y.sdu_itv) && (x.max_sdu_size == y.max_sdu_size) && (x.max_transport_latency == y.max_transport_latency) && (x.rtn == y.rtn) && (x.phy == y.phy) && (x.packing == y.packing) && (x.framing == y.framing) && (x.enc == y.enc) && (x.enc_code == y.enc_code)); } namespace iso_matchers { MATCHER_P(Eq, value, "") { return (arg == value); } MATCHER_P2(EqPointedArray, value, len, "") { return (!std::memcmp(arg, value, len)); } } // namespace iso_matchers TEST_F(IsoManagerTest, SingletonAccess) { auto* iso_mgr = IsoManager::GetInstance(); ASSERT_EQ(manager_instance_, iso_mgr); } TEST_F(IsoManagerTest, RegisterCallbacks) { auto* iso_mgr = IsoManager::GetInstance(); ASSERT_EQ(manager_instance_, iso_mgr); iso_mgr->RegisterBigCallbacks(new MockBigCallbacks()); iso_mgr->RegisterCigCallbacks(new MockCigCallbacks()); } TEST_F(IsoManagerDeathTestNoInit, RegisterNullBigCallbacks) { IsoManager::GetInstance()->Start(); ASSERT_EXIT(IsoManager::GetInstance()->RegisterBigCallbacks(nullptr), ::testing::KilledBySignal(SIGABRT), "Invalid BIG callbacks"); // Manual cleanup as IsoManagerDeathTest has no 'generic' cleanup IsoManager::GetInstance()->Stop(); } TEST_F(IsoManagerDeathTestNoInit, RegisterNullCigCallbacks) { IsoManager::GetInstance()->Start(); ASSERT_EXIT(IsoManager::GetInstance()->RegisterCigCallbacks(nullptr), ::testing::KilledBySignal(SIGABRT), "Invalid CIG callbacks"); // Manual cleanup as IsoManagerDeathTest has no 'generic' cleanup IsoManager::GetInstance()->Stop(); } // Verify hci layer being called by the Iso Manager TEST_F(IsoManagerTest, CreateCigHciCall) { for (uint8_t i = 220; i != 60; ++i) { EXPECT_CALL(hcic_interface_, SetCigParams(i, iso_matchers::Eq(kDefaultCigParams), _)) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->CreateCig(i, kDefaultCigParams); } } // Check handling create cig request twice with the same CIG id TEST_F(IsoManagerDeathTest, CreateSameCigTwice) { bluetooth::hci::iso_manager::cig_create_cmpl_evt evt; evt.status = 0x01; EXPECT_CALL( *cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); volatile_test_cig_create_cmpl_evt_.cig_id = 127; IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ASSERT_EQ(evt.status, HCI_SUCCESS); // Second call with the same CIG ID should fail ASSERT_EXIT(IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams), ::testing::KilledBySignal(SIGABRT), "already exists"); } // Check for handling invalid length response from the faulty controller TEST_F(IsoManagerDeathTest, CreateCigCallbackInvalidRspPacket) { uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00}; ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&hci_mock_rsp_buffer]( auto, auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); ASSERT_EXIT(IsoManager::GetInstance()->CreateCig(128, kDefaultCigParams), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } // Check for handling invalid length response from the faulty controller TEST_F(IsoManagerDeathTest, CreateCigCallbackInvalidRspPacket2) { uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00, 0x02, 0x01, 0x00}; ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&hci_mock_rsp_buffer]( auto, auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); ASSERT_EXIT(IsoManager::GetInstance()->CreateCig(128, kDefaultCigParams), ::testing::KilledBySignal(SIGABRT), "Invalid CIS count"); } // Check if IsoManager properly handles error responses from HCI layer TEST_F(IsoManagerTest, CreateCigCallbackInvalidStatus) { uint8_t rsp_cig_id = 128; uint8_t rsp_status = 0x01; uint8_t rsp_cis_cnt = 3; uint8_t hci_mock_rsp_buffer[] = {rsp_status, rsp_cig_id, rsp_cis_cnt}; ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&hci_mock_rsp_buffer]( auto, auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); bluetooth::hci::iso_manager::cig_create_cmpl_evt evt; EXPECT_CALL( *cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->CreateCig(rsp_cig_id, kDefaultCigParams); ASSERT_EQ(evt.cig_id, rsp_cig_id); ASSERT_EQ(evt.status, rsp_status); ASSERT_TRUE(evt.conn_handles.empty()); } // Check valid callback response TEST_F(IsoManagerTest, CreateCigCallbackValid) { bluetooth::hci::iso_manager::cig_create_cmpl_evt evt; EXPECT_CALL( *cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnCreateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(evt.status, volatile_test_cig_create_cmpl_evt_.status); ASSERT_EQ(evt.conn_handles.size(), 2u); ASSERT_TRUE( std::is_permutation(evt.conn_handles.begin(), evt.conn_handles.end(), std::vector({0x0EFF, 0x00FF}).begin())); } // Check if CIG reconfigure triggers HCI layer call TEST_F(IsoManagerTest, ReconfigureCigHciCall) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); EXPECT_CALL(hcic_interface_, SetCigParams(volatile_test_cig_create_cmpl_evt_.cig_id, iso_matchers::Eq(kDefaultCigParams), _)) .Times(1); IsoManager::GetInstance()->ReconfigureCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); } // Verify handlidng invalid call - reconfiguring invalid CIG TEST_F(IsoManagerDeathTest, ReconfigureCigWithNoSuchCig) { ASSERT_EXIT(IsoManager::GetInstance()->ReconfigureCig(128, kDefaultCigParams), ::testing::KilledBySignal(SIGABRT), "No such cig"); } TEST_F(IsoManagerDeathTest, ReconfigureCigInvalidRspPacket) { uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00}; IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&hci_mock_rsp_buffer]( auto, auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); ASSERT_EXIT(IsoManager::GetInstance()->ReconfigureCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } TEST_F(IsoManagerDeathTest, ReconfigureCigInvalidRspPacket2) { uint8_t hci_mock_rsp_buffer[] = {0x00, 0x00, 0x02, 0x01, 0x00}; IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&hci_mock_rsp_buffer]( auto, auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); ASSERT_EXIT( IsoManager::GetInstance()->ReconfigureCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams2), ::testing::KilledBySignal(SIGABRT), "Invalid CIS count"); } TEST_F(IsoManagerTest, ReconfigureCigInvalidStatus) { uint8_t rsp_cig_id = 128; uint8_t rsp_status = 0x01; uint8_t rsp_cis_cnt = 3; uint8_t hci_mock_rsp_buffer[] = {rsp_status, rsp_cig_id, rsp_cis_cnt}; IsoManager::GetInstance()->CreateCig(rsp_cig_id, kDefaultCigParams); // Set-up the invalid response ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&hci_mock_rsp_buffer]( auto, auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); bluetooth::hci::iso_manager::cig_create_cmpl_evt evt; EXPECT_CALL( *cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnReconfigureCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->ReconfigureCig(rsp_cig_id, kDefaultCigParams2); ASSERT_EQ(evt.cig_id, rsp_cig_id); ASSERT_EQ(evt.status, rsp_status); ASSERT_TRUE(evt.conn_handles.empty()); } TEST_F(IsoManagerTest, ReconfigureCigValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cig_create_cmpl_evt evt; EXPECT_CALL( *cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnReconfigureCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); // Verify valid reconfiguration request IsoManager::GetInstance()->ReconfigureCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams2); ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(evt.status, volatile_test_cig_create_cmpl_evt_.status); ASSERT_TRUE(std::is_permutation( evt.conn_handles.begin(), evt.conn_handles.end(), volatile_test_cig_create_cmpl_evt_.conn_handles.begin())); } TEST_F(IsoManagerTest, RemoveCigHciCall) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); EXPECT_CALL(hcic_interface_, RemoveCig(volatile_test_cig_create_cmpl_evt_.cig_id, _)) .Times(1); IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id); } TEST_F(IsoManagerDeathTest, RemoveCigWithNoSuchCig) { ASSERT_EXIT(IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id), ::testing::KilledBySignal(SIGABRT), "No such cig"); } TEST_F(IsoManagerDeathTest, RemoveSameCigTwice) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, RemoveCig) .WillByDefault( [this](auto, base::OnceCallback cb) { uint8_t hci_mock_rsp_buffer[2]; uint8_t* p = hci_mock_rsp_buffer; UINT8_TO_STREAM(p, HCI_SUCCESS); UINT8_TO_STREAM(p, this->volatile_test_cig_create_cmpl_evt_.cig_id); std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EXIT(IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id), ::testing::KilledBySignal(SIGABRT), "No such cig"); } TEST_F(IsoManagerDeathTest, RemoveCigInvalidRspPacket) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, RemoveCig) .WillByDefault([](auto, base::OnceCallback cb) { uint8_t hci_mock_rsp_buffer[] = {0x00}; // status byte only std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); ASSERT_EXIT(IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } TEST_F(IsoManagerTest, RemoveCigInvalidStatus) { uint8_t rsp_status = 0x02; uint8_t hci_mock_rsp_buffer[] = {rsp_status, volatile_test_cig_create_cmpl_evt_.cig_id}; IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, RemoveCig) .WillByDefault( [&hci_mock_rsp_buffer]( auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); bluetooth::hci::iso_manager::cig_remove_cmpl_evt evt; ON_CALL(*cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnRemoveCmpl, _)) .WillByDefault([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(evt.status, rsp_status); } TEST_F(IsoManagerTest, RemoveCigValid) { uint8_t hci_mock_rsp_buffer[] = {HCI_SUCCESS, volatile_test_cig_create_cmpl_evt_.cig_id}; IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, RemoveCig) .WillByDefault( [&hci_mock_rsp_buffer]( auto, base::OnceCallback cb) { std::move(cb).Run(hci_mock_rsp_buffer, sizeof(hci_mock_rsp_buffer)); return 0; }); bluetooth::hci::iso_manager::cig_remove_cmpl_evt evt; EXPECT_CALL( *cig_callbacks_, OnCigEvent(bluetooth::hci::iso_manager::kIsoEventCigOnRemoveCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->RemoveCig( volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(evt.cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(evt.status, HCI_SUCCESS); } TEST_F(IsoManagerTest, EstablishCisHciCall) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } EXPECT_CALL(hcic_interface_, CreateCis(2, iso_matchers::EqPointedArray( params.conn_pairs.data(), params.conn_pairs.size() * sizeof(params.conn_pairs.data()[0])), _)) .Times(1); IsoManager::GetInstance()->EstablishCis(params); } TEST_F(IsoManagerDeathTest, EstablishCisWithNoSuchCis) { bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis( params), ::testing::KilledBySignal(SIGABRT), "No such cis"); } TEST_F(IsoManagerDeathTest, ConnectSameCisTwice) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis( params), ::testing::KilledBySignal(SIGABRT), "Already connected"); } TEST_F(IsoManagerDeathTest, EstablishCisInvalidResponsePacket) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, CreateCis) .WillByDefault([this](uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg, base::OnceCallback cb) { for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { std::vector buf(27); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, handle); UINT24_TO_STREAM(p, 0xEA); // CIG sync delay UINT24_TO_STREAM(p, 0xEB); // CIS sync delay UINT24_TO_STREAM(p, 0xEC); // transport latency mtos UINT24_TO_STREAM(p, 0xED); // transport latency stom UINT8_TO_STREAM(p, 0x01); // phy mtos UINT8_TO_STREAM(p, 0x02); // phy stom UINT8_TO_STREAM(p, 0x01); // nse UINT8_TO_STREAM(p, 0x02); // bn mtos UINT8_TO_STREAM(p, 0x03); // bn stom UINT8_TO_STREAM(p, 0x04); // ft mtos UINT8_TO_STREAM(p, 0x05); // ft stom UINT16_TO_STREAM(p, 0x00FA); // Max PDU mtos UINT16_TO_STREAM(p, 0x00FB); // Max PDU stom IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT, buf.data(), buf.size()); } }); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->EstablishCis( params), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } TEST_F(IsoManagerTest, EstablishCisInvalidCommandStatus) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); uint16_t invalid_status = 0x0001; ON_CALL(hcic_interface_, CreateCis) .WillByDefault([invalid_status]( uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg, base::OnceCallback cb) { std::move(cb).Run((uint8_t*)&invalid_status, sizeof(invalid_status)); return 0; }); EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this, invalid_status](uint8_t type, void* data) { bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt = static_cast( data); ASSERT_EQ(evt->status, invalid_status); ASSERT_TRUE( std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), evt->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); // Establish all CISes bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); } TEST_F(IsoManagerTest, EstablishCisInvalidStatus) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); uint8_t invalid_status = 0x01; ON_CALL(hcic_interface_, CreateCis) .WillByDefault([this, invalid_status]( uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg, base::OnceCallback cb) { for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { std::vector buf(28); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, invalid_status); UINT16_TO_STREAM(p, handle); UINT24_TO_STREAM(p, 0xEA); // CIG sync delay UINT24_TO_STREAM(p, 0xEB); // CIS sync delay UINT24_TO_STREAM(p, 0xEC); // transport latency mtos UINT24_TO_STREAM(p, 0xED); // transport latency stom UINT8_TO_STREAM(p, 0x01); // phy mtos UINT8_TO_STREAM(p, 0x02); // phy stom UINT8_TO_STREAM(p, 0x01); // nse UINT8_TO_STREAM(p, 0x02); // bn mtos UINT8_TO_STREAM(p, 0x03); // bn stom UINT8_TO_STREAM(p, 0x04); // ft mtos UINT8_TO_STREAM(p, 0x05); // ft stom UINT16_TO_STREAM(p, 0x00FA); // Max PDU mtos UINT16_TO_STREAM(p, 0x00FB); // Max PDU stom UINT16_TO_STREAM(p, 0x0C60); // ISO interval IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT, buf.data(), buf.size()); } }); EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this, invalid_status](uint8_t type, void* data) { bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt = static_cast( data); ASSERT_EQ(evt->status, invalid_status); ASSERT_TRUE( std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), evt->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); } TEST_F(IsoManagerTest, EstablishCisValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this](uint8_t type, void* data) { bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt = static_cast( data); ASSERT_EQ(evt->status, HCI_SUCCESS); ASSERT_TRUE( std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), evt->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); } TEST_F(IsoManagerTest, ReconnectCisValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); // trigger HCI disconnection event for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->HandleDisconnect(handle, 0x16); } EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this](uint8_t type, void* data) { bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt = static_cast( data); ASSERT_EQ(evt->status, HCI_SUCCESS); ASSERT_TRUE( std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), evt->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); IsoManager::GetInstance()->EstablishCis(params); } TEST_F(IsoManagerTest, DisconnectCisHciCall) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { EXPECT_CALL(hcic_interface_, Disconnect(handle, 0x16)) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle, 0x16); } } TEST_F(IsoManagerDeathTest, DisconnectCisWithNoSuchCis) { for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis( handle, 0x16), ::testing::KilledBySignal(SIGABRT), "No such cis"); } } TEST_F(IsoManagerDeathTest, DisconnectSameCisTwice) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle, 0x16); } for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { ASSERT_EXIT( IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis( handle, 0x16), ::testing::KilledBySignal(SIGABRT), "Not connected"); } } TEST_F(IsoManagerTest, DisconnectCisValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); uint8_t disconnect_reason = 0x16; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { EXPECT_CALL(*cig_callbacks_, OnCisEvent) .WillOnce([this, handle, disconnect_reason](uint8_t event_code, void* data) { ASSERT_EQ(event_code, bluetooth::hci::iso_manager::kIsoEventCisDisconnected); auto* event = static_cast( data); ASSERT_EQ(event->reason, disconnect_reason); ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(event->cis_conn_hdl, handle); }) .RetiresOnSaturation(); IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis( handle, disconnect_reason); } } // Check if we properly ignore not ISO related disconnect events TEST_F(IsoManagerDeathTest, DisconnectCisInvalidResponse) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); // Make the HCI layer send invalid handles in disconnect event ON_CALL(hcic_interface_, Disconnect) .WillByDefault([](uint16_t handle, uint8_t reason) { IsoManager::GetInstance()->HandleDisconnect(handle + 1, reason); }); // We don't expect any calls as these are not ISO handles ON_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDisconnected, _)) .WillByDefault([](uint8_t event_code, void* data) { FAIL(); }); for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle, 0x16); } } TEST_F(IsoManagerTest, CreateBigHciCall) { for (uint8_t i = 220; i != 60; ++i) { EXPECT_CALL(hcic_interface_, CreateBig(i, iso_matchers::Eq(kDefaultBigParams))) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->CreateBig(i, kDefaultBigParams); } } TEST_F(IsoManagerTest, CreateBigValid) { bluetooth::hci::iso_manager::big_create_cmpl_evt evt; evt.status = 0x01; EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->CreateBig(0x01, kDefaultBigParams); ASSERT_EQ(evt.status, HCI_SUCCESS); } TEST_F(IsoManagerDeathTest, CreateBigInvalidResponsePacket) { ON_CALL(hcic_interface_, CreateBig) .WillByDefault( [](auto big_handle, bluetooth::hci::iso_manager::big_create_params big_params) { std::vector buf(18); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, 0x00); UINT8_TO_STREAM(p, big_handle); UINT24_TO_STREAM(p, 0x0080de); // big_sync_delay UINT24_TO_STREAM(p, 0x00cefe); // transport_latency_big UINT8_TO_STREAM(p, big_params.phy); // phy UINT8_TO_STREAM(p, 4); // nse UINT8_TO_STREAM(p, 1); // bn UINT8_TO_STREAM(p, 0); // pto UINT8_TO_STREAM(p, 4); // irc UINT16_TO_STREAM(p, 108); // max_pdu UINT16_TO_STREAM(p, 6); // iso_interval UINT8_TO_STREAM(p, 0); // num BISes IsoManager::GetInstance()->HandleHciEvent( HCI_BLE_CREATE_BIG_CPL_EVT, buf.data(), buf.size()); }); ASSERT_EXIT(IsoManager::GetInstance()->CreateBig(0x01, kDefaultBigParams), ::testing::KilledBySignal(SIGABRT), "Invalid bis count"); } TEST_F(IsoManagerDeathTest, CreateBigInvalidResponsePacket2) { ON_CALL(hcic_interface_, CreateBig) .WillByDefault( [](auto big_handle, bluetooth::hci::iso_manager::big_create_params big_params) { std::vector buf(18); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, 0x00); UINT8_TO_STREAM(p, big_handle); UINT24_TO_STREAM(p, 0x0080de); // big_sync_delay UINT24_TO_STREAM(p, 0x00cefe); // transport_latency_big UINT8_TO_STREAM(p, big_params.phy); // phy UINT8_TO_STREAM(p, 4); // nse UINT8_TO_STREAM(p, 1); // bn UINT8_TO_STREAM(p, 0); // pto UINT8_TO_STREAM(p, 4); // irc UINT16_TO_STREAM(p, 108); // max_pdu UINT16_TO_STREAM(p, 6); // iso_interval UINT8_TO_STREAM(p, big_params.num_bis); IsoManager::GetInstance()->HandleHciEvent( HCI_BLE_CREATE_BIG_CPL_EVT, buf.data(), buf.size()); }); ASSERT_EXIT(IsoManager::GetInstance()->CreateBig(0x01, kDefaultBigParams), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } TEST_F(IsoManagerTest, CreateBigInvalidStatus) { bluetooth::hci::iso_manager::big_create_cmpl_evt evt; evt.status = 0x00; EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); ON_CALL(hcic_interface_, CreateBig) .WillByDefault( [](auto big_handle, bluetooth::hci::iso_manager::big_create_params big_params) { std::vector buf(big_params.num_bis * sizeof(uint16_t) + 18); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, 0x01); UINT8_TO_STREAM(p, big_handle); UINT24_TO_STREAM(p, 0x0080de); // big_sync_delay UINT24_TO_STREAM(p, 0x00cefe); // transport_latency_big UINT8_TO_STREAM(p, big_params.phy); // phy UINT8_TO_STREAM(p, 4); // nse UINT8_TO_STREAM(p, 1); // bn UINT8_TO_STREAM(p, 0); // pto UINT8_TO_STREAM(p, 4); // irc UINT16_TO_STREAM(p, 108); // max_pdu UINT16_TO_STREAM(p, 6); // iso_interval UINT8_TO_STREAM(p, big_params.num_bis); static uint8_t conn_hdl = 0x01; for (auto i = 0; i < big_params.num_bis; ++i) { UINT16_TO_STREAM(p, conn_hdl++); } IsoManager::GetInstance()->HandleHciEvent( HCI_BLE_CREATE_BIG_CPL_EVT, buf.data(), buf.size()); }); IsoManager::GetInstance()->CreateBig(0x01, kDefaultBigParams); ASSERT_EQ(evt.status, 0x01); ASSERT_EQ(evt.big_id, 0x01); ASSERT_EQ(evt.conn_handles.size(), kDefaultBigParams.num_bis); } TEST_F(IsoManagerDeathTest, CreateSameBigTwice) { bluetooth::hci::iso_manager::big_create_cmpl_evt evt; evt.status = 0x01; EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->CreateBig(0x01, kDefaultBigParams); ASSERT_EQ(evt.status, HCI_SUCCESS); ASSERT_EQ(evt.big_id, 0x01); ASSERT_EQ(evt.conn_handles.size(), kDefaultBigParams.num_bis); } TEST_F(IsoManagerTest, TerminateBigHciCall) { const uint8_t big_id = 0x22; const uint8_t reason = 0x16; // Terminated by local host IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); EXPECT_CALL(hcic_interface_, TerminateBig(big_id, reason)).Times(1); IsoManager::GetInstance()->TerminateBig(big_id, reason); } TEST_F(IsoManagerDeathTest, TerminateSameBigTwice) { const uint8_t big_id = 0x22; const uint8_t reason = 0x16; // Terminated by local host IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, _)); IsoManager::GetInstance()->TerminateBig(big_id, reason); ASSERT_EXIT(IsoManager::GetInstance()->TerminateBig(big_id, reason), ::testing::KilledBySignal(SIGABRT), "No such big"); } TEST_F(IsoManagerDeathTest, TerminateBigNoSuchBig) { const uint8_t big_id = 0x01; const uint8_t reason = 0x16; // Terminated by local host EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnCreateCmpl, _)); IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); ASSERT_EXIT(IsoManager::GetInstance()->TerminateBig(big_id + 1, reason), ::testing::KilledBySignal(SIGABRT), "No such big"); } TEST_F(IsoManagerDeathTest, TerminateBigInvalidResponsePacket) { ON_CALL(hcic_interface_, TerminateBig) .WillByDefault([](auto big_handle, uint8_t reason) { std::vector buf(1); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, reason); IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, buf.data(), buf.size()); }); const uint8_t big_id = 0x22; const uint8_t reason = 0x16; // Terminated by local host IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); ASSERT_EXIT(IsoManager::GetInstance()->TerminateBig(big_id, reason), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } TEST_F(IsoManagerDeathTest, TerminateBigInvalidResponsePacket2) { const uint8_t big_id = 0x22; const uint8_t reason = 0x16; // Terminated by local host ON_CALL(hcic_interface_, TerminateBig) .WillByDefault([](auto big_handle, uint8_t reason) { std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, reason); IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, buf.data(), buf.size()); }); IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); ASSERT_EXIT(IsoManager::GetInstance()->TerminateBig(big_id, reason), ::testing::KilledBySignal(SIGABRT), "Invalid packet length"); } TEST_F(IsoManagerTest, TerminateBigInvalidResponseBigId) { const uint8_t big_id = 0x22; const uint8_t reason = 0x16; // Terminated by local host ON_CALL(hcic_interface_, TerminateBig) .WillByDefault([](auto big_handle, uint8_t reason) { std::vector buf(2); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, reason); UINT8_TO_STREAM(p, big_handle + 1); IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, buf.data(), buf.size()); }); IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); ASSERT_EXIT(IsoManager::GetInstance()->TerminateBig(big_id, reason), ::testing::KilledBySignal(SIGABRT), "No such big"); } TEST_F(IsoManagerTest, TerminateBigValid) { const uint8_t big_id = 0x22; const uint8_t reason = 0x16; // Terminated by local host bluetooth::hci::iso_manager::big_terminate_cmpl_evt evt; IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, _)) .WillOnce([&evt](uint8_t type, void* data) { evt = *static_cast( data); return 0; }); IsoManager::GetInstance()->TerminateBig(big_id, reason); ASSERT_EQ(evt.big_id, big_id); ASSERT_EQ(evt.reason, reason); } TEST_F(IsoManagerTest, SetupIsoDataPathValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); bluetooth::hci::iso_manager::iso_data_path_params path_params = kDefaultIsoDataPathParams; // Setup data paths for all CISes path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionIn; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { EXPECT_CALL(*cig_callbacks_, OnSetupIsoDataPath(HCI_SUCCESS, handle, volatile_test_cig_create_cmpl_evt_.cig_id)) .Times(1) .RetiresOnSaturation(); path_params.data_path_dir = (bluetooth::hci::iso_manager::kIsoDataPathDirectionIn + handle) % 2; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); } // Setup data paths for all BISes path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut; for (auto& handle : volatile_test_big_params_evt_.conn_handles) { std::cerr << "setting up BIS data path on conn_hdl: " << int{handle}; EXPECT_CALL(*big_callbacks_, OnSetupIsoDataPath(HCI_SUCCESS, handle, volatile_test_big_params_evt_.big_id)) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); } } TEST_F(IsoManagerTest, SetupIsoDataPathTwice) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Establish CISes bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); // Setup data paths for all CISes twice bluetooth::hci::iso_manager::iso_data_path_params path_params = kDefaultIsoDataPathParams; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); // Should be possible to reconfigure IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); } IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); // Setup data paths for all BISes twice for (auto& handle : volatile_test_big_params_evt_.conn_handles) { IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); // Should be possible to reconfigure IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); } } TEST_F(IsoManagerTest, SetupIsoDataPathInvalidStatus) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); bluetooth::hci::iso_manager::iso_data_path_params path_params = kDefaultIsoDataPathParams; uint8_t setup_datapath_rsp_status = HCI_SUCCESS; ON_CALL(hcic_interface_, SetupIsoDataPath) .WillByDefault([&setup_datapath_rsp_status]( uint16_t iso_handle, uint8_t, uint8_t, uint8_t, uint16_t, uint16_t, uint32_t, std::vector, base::OnceCallback cb) { std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, setup_datapath_rsp_status); UINT16_TO_STREAM(p, iso_handle); std::move(cb).Run(buf.data(), buf.size()); }); // Try to setup data paths for all CISes path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionIn; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { // Mock the response with status != HCI_SUCCESS EXPECT_CALL(*cig_callbacks_, OnSetupIsoDataPath(0x11, handle, volatile_test_cig_create_cmpl_evt_.cig_id)) .Times(1) .RetiresOnSaturation(); setup_datapath_rsp_status = 0x11; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); // It should be possible to retry on the same handle after the first // failure EXPECT_CALL(*cig_callbacks_, OnSetupIsoDataPath(HCI_SUCCESS, handle, volatile_test_cig_create_cmpl_evt_.cig_id)) .Times(1) .RetiresOnSaturation(); setup_datapath_rsp_status = HCI_SUCCESS; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); } // Try to setup data paths for all BISes path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut; for (auto& handle : volatile_test_big_params_evt_.conn_handles) { EXPECT_CALL( *big_callbacks_, OnSetupIsoDataPath(0x11, handle, volatile_test_big_params_evt_.big_id)) .Times(1) .RetiresOnSaturation(); setup_datapath_rsp_status = 0x11; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); EXPECT_CALL(*big_callbacks_, OnSetupIsoDataPath(HCI_SUCCESS, handle, volatile_test_big_params_evt_.big_id)) .Times(1) .RetiresOnSaturation(); setup_datapath_rsp_status = HCI_SUCCESS; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); } } TEST_F(IsoManagerTest, RemoveIsoDataPathValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); // Establish all CISes before setting up their data paths bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); bluetooth::hci::iso_manager::iso_data_path_params path_params = kDefaultIsoDataPathParams; // Setup and remove data paths for all CISes path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionIn; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); EXPECT_CALL(*cig_callbacks_, OnRemoveIsoDataPath(HCI_SUCCESS, handle, volatile_test_cig_create_cmpl_evt_.cig_id)) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->RemoveIsoDataPath(handle, path_params.data_path_dir); } // Setup and remove data paths for all BISes path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut; for (auto& handle : volatile_test_big_params_evt_.conn_handles) { std::cerr << "setting up BIS data path on conn_hdl: " << int{handle}; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); EXPECT_CALL(*big_callbacks_, OnRemoveIsoDataPath(HCI_SUCCESS, handle, volatile_test_big_params_evt_.big_id)) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->RemoveIsoDataPath(handle, path_params.data_path_dir); } } TEST_F(IsoManagerDeathTest, RemoveIsoDataPathNoSuchPath) { // Check on CIS IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); uint16_t iso_handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; ASSERT_EXIT( IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut), ::testing::KilledBySignal(SIGABRT), "path not set"); IsoManager::GetInstance()->EstablishCis({.conn_pairs = {{iso_handle, 1}}}); ASSERT_EXIT( IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut), ::testing::KilledBySignal(SIGABRT), "path not set"); // Check on BIS iso_handle = volatile_test_big_params_evt_.conn_handles[0]; IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); ASSERT_EXIT( IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut), ::testing::KilledBySignal(SIGABRT), "path not set"); } TEST_F(IsoManagerDeathTest, RemoveIsoDataPathTwice) { // Check on CIS IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); uint16_t iso_handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({.conn_pairs = {{iso_handle, 1}}}); IsoManager::GetInstance()->SetupIsoDataPath(iso_handle, kDefaultIsoDataPathParams); IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, kDefaultIsoDataPathParams.data_path_dir); ASSERT_EXIT( IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut), ::testing::KilledBySignal(SIGABRT), "path not set"); // Check on BIS iso_handle = volatile_test_big_params_evt_.conn_handles[0]; IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); IsoManager::GetInstance()->SetupIsoDataPath(iso_handle, kDefaultIsoDataPathParams); IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, kDefaultIsoDataPathParams.data_path_dir); ASSERT_EXIT( IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, bluetooth::hci::iso_manager::kIsoDataPathDirectionOut), ::testing::KilledBySignal(SIGABRT), "path not set"); } // Check if HCI status other than HCI_SUCCESS is being propagated to the caller TEST_F(IsoManagerTest, RemoveIsoDataPathInvalidStatus) { // Mock invalid status response uint8_t remove_datapath_rsp_status = 0x12; ON_CALL(hcic_interface_, RemoveIsoDataPath) .WillByDefault([&remove_datapath_rsp_status]( uint16_t iso_handle, uint8_t data_path_dir, base::OnceCallback cb) { std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, remove_datapath_rsp_status); UINT16_TO_STREAM(p, iso_handle); std::move(cb).Run(buf.data(), buf.size()); }); // Check on CIS IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); uint16_t iso_handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({.conn_pairs = {{iso_handle, 1}}}); IsoManager::GetInstance()->SetupIsoDataPath(iso_handle, kDefaultIsoDataPathParams); EXPECT_CALL(*cig_callbacks_, OnRemoveIsoDataPath(remove_datapath_rsp_status, iso_handle, volatile_test_cig_create_cmpl_evt_.cig_id)) .Times(1); IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, kDefaultIsoDataPathParams.data_path_dir); // Check on BIS iso_handle = volatile_test_big_params_evt_.conn_handles[0]; IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); IsoManager::GetInstance()->SetupIsoDataPath(iso_handle, kDefaultIsoDataPathParams); EXPECT_CALL(*big_callbacks_, OnRemoveIsoDataPath(remove_datapath_rsp_status, iso_handle, volatile_test_big_params_evt_.big_id)) .Times(1); IsoManager::GetInstance()->RemoveIsoDataPath( iso_handle, kDefaultIsoDataPathParams.data_path_dir); } TEST_F(IsoManagerTest, SendIsoDataCigValid) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { bluetooth::hci::iso_manager::iso_data_path_params path_params = kDefaultIsoDataPathParams; path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionOut; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); for (uint8_t num_pkts = 2; num_pkts != 0; num_pkts--) { constexpr uint8_t data_len = 108; EXPECT_CALL(bte_interface_, HciSend) .WillOnce([handle, data_len](BT_HDR* p_msg, uint16_t event) { uint8_t* p = p_msg->data; uint16_t msg_handle; uint16_t iso_load_len; ASSERT_TRUE((event & MSG_STACK_TO_HC_HCI_ISO) != 0); ASSERT_NE(p_msg, nullptr); ASSERT_EQ(p_msg->len, data_len + ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) ? 12 : 8)); // Verify packet internals STREAM_TO_UINT16(msg_handle, p); ASSERT_EQ(msg_handle, handle); STREAM_TO_UINT16(iso_load_len, p); ASSERT_EQ( iso_load_len, data_len + ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) ? 8 : 4)); if (p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) { STREAM_SKIP_UINT16(p); // skip ts LSB halfword STREAM_SKIP_UINT16(p); // skip ts MSB halfword } STREAM_SKIP_UINT16(p); // skip seq_nb uint16_t msg_data_len; STREAM_TO_UINT16(msg_data_len, p); ASSERT_EQ(msg_data_len, data_len); }) .RetiresOnSaturation(); std::vector data_vec(data_len, 0); IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); } } } TEST_F(IsoManagerTest, SendIsoDataBigValid) { IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); for (auto& handle : volatile_test_big_params_evt_.conn_handles) { IsoManager::GetInstance()->SetupIsoDataPath(handle, kDefaultIsoDataPathParams); for (uint8_t num_pkts = 2; num_pkts != 0; num_pkts--) { constexpr uint8_t data_len = 108; EXPECT_CALL(bte_interface_, HciSend) .WillOnce([handle, data_len](BT_HDR* p_msg, uint16_t event) { uint8_t* p = p_msg->data; uint16_t msg_handle; uint16_t iso_load_len; ASSERT_TRUE((event & MSG_STACK_TO_HC_HCI_ISO) != 0); ASSERT_NE(p_msg, nullptr); ASSERT_EQ(p_msg->len, data_len + ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) ? 12 : 8)); // Verify packet internals STREAM_TO_UINT16(msg_handle, p); ASSERT_EQ(msg_handle, handle); STREAM_TO_UINT16(iso_load_len, p); ASSERT_EQ( iso_load_len, data_len + ((p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) ? 8 : 4)); uint16_t msg_data_len; uint16_t msg_dummy; if (p_msg->layer_specific & BT_ISO_HDR_CONTAINS_TS) { STREAM_TO_UINT16(msg_dummy, p); // skip ts LSB halfword STREAM_TO_UINT16(msg_dummy, p); // skip ts MSB halfword } STREAM_TO_UINT16(msg_dummy, p); // skip seq_nb STREAM_TO_UINT16(msg_data_len, p); ASSERT_EQ(msg_data_len, data_len); }) .RetiresOnSaturation(); std::vector data_vec(data_len, 0); IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); } } } TEST_F(IsoManagerTest, SendIsoDataNoCredits) { uint8_t num_buffers = controller_interface_.GetIsoBufferCount(); std::vector data_vec(108, 0); // Check on CIG IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); IsoManager::GetInstance()->SetupIsoDataPath( volatile_test_cig_create_cmpl_evt_.conn_handles[0], kDefaultIsoDataPathParams); /* Try sending twice as much data as we can ignoring the credit limits and * expect the redundant packets to be ignored and not propagated down to the * HCI. */ EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < (2 * num_buffers); i++) { IsoManager::GetInstance()->SendIsoData( volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } // Return all credits for this one handle uint8_t mock_rsp[5]; uint8_t* p = mock_rsp; UINT8_TO_STREAM(p, 1); UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]); UINT16_TO_STREAM(p, num_buffers); IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp)); // Check on BIG IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); IsoManager::GetInstance()->SetupIsoDataPath( volatile_test_big_params_evt_.conn_handles[0], kDefaultIsoDataPathParams); /* Try sending twice as much data as we can ignoring the credit limits and * expect the redundant packets to be ignored and not propagated down to the * HCI. */ EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers); for (uint8_t i = 0; i < (2 * num_buffers); i++) { IsoManager::GetInstance()->SendIsoData( volatile_test_big_params_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } } TEST_F(IsoManagerTest, SendIsoDataCreditsReturned) { uint8_t num_buffers = controller_interface_.GetIsoBufferCount(); std::vector data_vec(108, 0); // Check on CIG IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); IsoManager::GetInstance()->SetupIsoDataPath( volatile_test_cig_create_cmpl_evt_.conn_handles[0], kDefaultIsoDataPathParams); /* Try sending twice as much data as we can, ignoring the credits limit and * expect the redundant packets to be ignored and not propagated down to the * HCI. */ EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < (2 * num_buffers); i++) { IsoManager::GetInstance()->SendIsoData( volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } // Return all credits for this one handle uint8_t mock_rsp[5]; uint8_t* p = mock_rsp; UINT8_TO_STREAM(p, 1); UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]); UINT16_TO_STREAM(p, num_buffers); IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp)); // Expect some more events go down the HCI EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < (2 * num_buffers); i++) { IsoManager::GetInstance()->SendIsoData( volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } // Return all credits for this one handle p = mock_rsp; UINT8_TO_STREAM(p, 1); UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]); UINT16_TO_STREAM(p, num_buffers); IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp)); // Check on BIG IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); IsoManager::GetInstance()->SetupIsoDataPath( volatile_test_big_params_evt_.conn_handles[0], kDefaultIsoDataPathParams); /* Try sending twice as much data as we can, ignoring the credits limit and * expect the redundant packets to be ignored and not propagated down to the * HCI. */ EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < (2 * num_buffers); i++) { IsoManager::GetInstance()->SendIsoData( volatile_test_big_params_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } // Return all credits for this one handle p = mock_rsp; UINT8_TO_STREAM(p, 1); UINT16_TO_STREAM(p, volatile_test_big_params_evt_.conn_handles[0]); UINT16_TO_STREAM(p, num_buffers); IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp)); // Expect some more events go down the HCI EXPECT_CALL(bte_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < (2 * num_buffers); i++) { IsoManager::GetInstance()->SendIsoData( volatile_test_big_params_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } } TEST_F(IsoManagerDeathTest, SendIsoDataWithNoDataPath) { std::vector data_vec(108, 0); // Check on CIG IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); bluetooth::hci::iso_manager::cis_establish_params params; for (auto& conn_handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { params.conn_pairs.push_back({conn_handle, 1}); } IsoManager::GetInstance()->EstablishCis(params); EXPECT_CALL(bte_interface_, HciSend).Times(0); ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData( volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(), data_vec.size()), ::testing::KilledBySignal(SIGABRT), "Data path not set"); // Check on BIG IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); EXPECT_CALL(bte_interface_, HciSend).Times(0); ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData( volatile_test_big_params_evt_.conn_handles[0], data_vec.data(), data_vec.size()), ::testing::KilledBySignal(SIGABRT), "Data path not set"); } TEST_F(IsoManagerDeathTest, SendIsoDataWithNoCigBigHandle) { std::vector data_vec(108, 0); ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData(134, data_vec.data(), data_vec.size()), ::testing::KilledBySignal(SIGABRT), "No such iso"); } TEST_F(IsoManagerDeathTest, SendIsoDataWithNoCigConnected) { std::vector data_vec(108, 0); IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; ASSERT_EXIT(IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()), ::testing::KilledBySignal(SIGABRT), "CIS not established"); } TEST_F(IsoManagerTest, HandleDisconnectNoSuchHandle) { // Don't expect any callbacks when connection handle is not for ISO. EXPECT_CALL(*cig_callbacks_, OnCigEvent).Times(0); EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0); EXPECT_CALL(*big_callbacks_, OnBigEvent).Times(0); IsoManager::GetInstance()->HandleDisconnect(123, 16); } TEST_F(IsoManagerTest, HandleDisconnectValidCig) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); EXPECT_CALL(*big_callbacks_, OnBigEvent).Times(0); EXPECT_CALL(*cig_callbacks_, OnCigEvent).Times(0); EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0); // Expect disconnect event exactly once EXPECT_CALL(*cig_callbacks_, OnCisEvent) .WillOnce([this, handle](uint8_t event_code, void* data) { ASSERT_EQ(event_code, bluetooth::hci::iso_manager::kIsoEventCisDisconnected); auto* event = static_cast( data); ASSERT_EQ(event->reason, 16); ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_EQ(event->cis_conn_hdl, handle); }); IsoManager::GetInstance()->HandleDisconnect(handle, 16); } TEST_F(IsoManagerTest, HandleDisconnectDisconnectedCig) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); EXPECT_CALL(*big_callbacks_, OnBigEvent).Times(0); EXPECT_CALL(*cig_callbacks_, OnCigEvent).Times(0); EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0); // Expect disconnect event exactly once EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDisconnected, _)) .Times(1) .RetiresOnSaturation(); IsoManager::GetInstance()->HandleDisconnect(handle, 16); // This one was once connected - expect no events IsoManager::GetInstance()->HandleDisconnect(handle, 16); // This one was never connected - expect no events handle = volatile_test_cig_create_cmpl_evt_.conn_handles[1]; IsoManager::GetInstance()->HandleDisconnect(handle, 16); } TEST_F(IsoManagerTest, HandleIsoData) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDataAvailable, _)) .Times(1); std::vector dummy_msg(18); uint8_t* p = dummy_msg.data(); UINT16_TO_STREAM(p, BT_EVT_TO_BTU_HCI_ISO); UINT16_TO_STREAM(p, 10); // .len UINT16_TO_STREAM(p, 0); // .offset UINT16_TO_STREAM(p, 0); // .layer_specific UINT16_TO_STREAM(p, handle); IsoManager::GetInstance()->HandleIsoData(dummy_msg.data()); } /* This test case simulates HCI thread scheduling events on the main thread, * without knowing the we are already shutting down the stack and Iso Manager * is already stopped. */ TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleIsoData) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); // Stop iso manager before trying to call the HCI callbacks IsoManager::GetInstance()->Stop(); EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDataAvailable, _)) .Times(0); // Expect no assert on this call - should be gracefully ignored std::vector dummy_msg(18); uint8_t* p = dummy_msg.data(); UINT16_TO_STREAM(p, BT_EVT_TO_BTU_HCI_ISO); UINT16_TO_STREAM(p, 10); // .len UINT16_TO_STREAM(p, 0); // .offset UINT16_TO_STREAM(p, 0); // .layer_specific UINT16_TO_STREAM(p, handle); IsoManager::GetInstance()->HandleIsoData(dummy_msg.data()); } /* This test case simulates HCI thread scheduling events on the main thread, * without knowing the we are already shutting down the stack and Iso Manager * is already stopped. */ TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleDisconnect) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); // Stop iso manager before trying to call the HCI callbacks IsoManager::GetInstance()->Stop(); // Expect no event when callback is being called on a stopped iso manager EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0); // Expect no assert on this call - should be gracefully ignored IsoManager::GetInstance()->HandleDisconnect(handle, 16); } /* This test case simulates HCI thread scheduling events on the main thread, * without knowing the we are already shutting down the stack and Iso Manager * is already stopped. */ TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleNumComplDataPkts) { uint8_t num_buffers = controller_interface_.GetIsoBufferCount(); IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); // Stop iso manager before trying to call the HCI callbacks IsoManager::GetInstance()->Stop(); // Expect no assert on this call - should be gracefully ignored uint8_t mock_rsp[5]; uint8_t* p = mock_rsp; UINT8_TO_STREAM(p, 1); UINT16_TO_STREAM(p, handle); UINT16_TO_STREAM(p, num_buffers); IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp)); } /* This test case simulates HCI thread scheduling events on the main thread, * without knowing the we are already shutting down the stack and Iso Manager * is already stopped. */ TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleHciEvent) { const uint8_t big_id = 0x22; IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); // Stop iso manager before trying to call the HCI callbacks IsoManager::GetInstance()->Stop(); EXPECT_CALL( *big_callbacks_, OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, _)) .Times(0); // Expect no assert on this call - should be gracefully ignored std::vector buf(2); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, big_id); UINT8_TO_STREAM(p, 16); // Terminated by local host IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT, buf.data(), buf.size()); } TEST_F(IsoManagerTest, HandleIsoDataSameSeqNb) { IsoManager::GetInstance()->CreateCig( volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->EstablishCis({{{handle, 1}}}); EXPECT_CALL( *cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDataAvailable, _)) .Times(2); std::vector dummy_msg(18); uint8_t* p = dummy_msg.data(); UINT16_TO_STREAM(p, BT_EVT_TO_BTU_HCI_ISO); UINT16_TO_STREAM(p, 10); // .len UINT16_TO_STREAM(p, 0); // .offset UINT16_TO_STREAM(p, 0); // .layer_specific UINT16_TO_STREAM(p, handle); IsoManager::GetInstance()->HandleIsoData(dummy_msg.data()); IsoManager::GetInstance()->HandleIsoData(dummy_msg.data()); }