/* * 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 "hci/controller_interface_mock.h" #include "hci/hci_packets.h" #include "hci/include/hci_layer.h" #include "mock_hcic_layer.h" #include "osi/include/allocator.h" #include "stack/btm/btm_dev.h" #include "stack/include/bt_hdr.h" #include "stack/include/bt_types.h" #include "stack/include/hci_error_code.h" #include "stack/include/hcidefs.h" #include "test/mock/mock_main_shim_entry.h" #include "test/mock/mock_main_shim_hci_layer.h" // TODO(b/369381361) Enfore -Wmissing-prototypes #pragma GCC diagnostic ignored "-Wmissing-prototypes" 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; // for function pointer testing purpose bool IsIsoActive = false; tBTM_SEC_DEV_REC* btm_find_dev_by_handle(uint16_t /* handle */) { return nullptr; } void BTM_LogHistory(const std::string& /* tag */, const RawAddress& /* bd_addr */, const std::string& /* msg */, const std::string& /* extra */) {} namespace bluetooth::shim { class IsoInterface { public: virtual void HciSend(BT_HDR* packet) = 0; virtual ~IsoInterface() = default; }; class MockIsoInterface : public IsoInterface { public: MOCK_METHOD((void), HciSend, (BT_HDR * p_msg), (override)); }; static MockIsoInterface* iso_interface = nullptr; static void SetMockIsoInterface(MockIsoInterface* interface) { iso_interface = interface; } static void set_data_cb(base::Callback /* send_data_cb */) { FAIL() << __func__ << " should never be called"; } static void transmit_command(const BT_HDR* /* command */, command_complete_cb /* complete_callback */, command_status_cb /* status_cb */, void* /* context */) { FAIL() << __func__ << " should never be called"; } static void transmit_downward(void* data, uint16_t /* iso_Data_size */) { iso_interface->HciSend((BT_HDR*)data); osi_free(data); } static hci_t interface = {.set_data_cb = set_data_cb, .transmit_command = transmit_command, .transmit_downward = transmit_downward}; } // namespace bluetooth::shim namespace { class MockCigCallbacks : public bluetooth::hci::iso_manager::CigCallbacks { public: MockCigCallbacks() = default; MockCigCallbacks(const MockCigCallbacks&) = delete; MockCigCallbacks& operator=(const MockCigCallbacks&) = delete; ~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)); }; class MockBigCallbacks : public bluetooth::hci::iso_manager::BigCallbacks { public: MockBigCallbacks() = default; MockBigCallbacks(const MockBigCallbacks&) = delete; MockBigCallbacks& operator=(const MockBigCallbacks&) = delete; ~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)); }; } // namespace class IsoManagerTest : public Test { protected: void SetUp() override { bluetooth::shim::SetMockIsoInterface(&iso_interface_); hcic::SetMockHcicInterface(&hcic_interface_); bluetooth::shim::testing::hci_layer_set_interface(&bluetooth::shim::interface); bluetooth::hci::testing::mock_controller_ = &controller_; big_callbacks_.reset(new MockBigCallbacks()); cig_callbacks_.reset(new MockCigCallbacks()); IsIsoActive = false; iso_sizes_.total_num_le_packets_ = 6; iso_sizes_.le_data_packet_length_ = 1024; ON_CALL(controller_, GetControllerIsoBufferSize()).WillByDefault(Return(iso_sizes_)); InitIsoManager(); } void TearDown() override { CleanupIsoManager(); big_callbacks_.reset(); cig_callbacks_.reset(); bluetooth::shim::SetMockIsoInterface(nullptr); hcic::SetMockHcicInterface(nullptr); bluetooth::shim::testing::hci_layer_set_interface(nullptr); bluetooth::hci::testing::mock_controller_ = nullptr; } virtual void InitIsoManager() { manager_instance_ = IsoManager::GetInstance(); manager_instance_->Start(); manager_instance_->RegisterCigCallbacks(cig_callbacks_.get()); manager_instance_->RegisterBigCallbacks(big_callbacks_.get()); manager_instance_->RegisterOnIsoTrafficActiveCallback(iso_active_callback); // 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_; bluetooth::shim::MockIsoInterface iso_interface_; hcic::MockHcicInterface hcic_interface_; bluetooth::hci::testing::MockControllerInterface controller_; bluetooth::hci::LeBufferSize iso_sizes_; std::unique_ptr big_callbacks_; std::unique_ptr cig_callbacks_; void (*iso_active_callback)(bool) = [](bool active) { IsIsoActive = active; }; }; const bluetooth::hci::iso_manager::cig_create_cmpl_evt IsoManagerTest::kDefaultCigParamsEvt = { .status = 0x00, .cig_id = 128, .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(big_callbacks_.get()); iso_mgr->RegisterCigCallbacks(cig_callbacks_.get()); iso_mgr->RegisterOnIsoTrafficActiveCallback(iso_active_callback); } 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())); } TEST_F(IsoManagerTest, CreateCigLateArrivingCallback) { // Catch the callback base::OnceCallback iso_cb; ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&](auto /* cig_id */, auto, base::OnceCallback cb) { iso_cb = std::move(cb); }); IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Stop the IsoManager before calling the callback IsoManager::GetInstance()->Stop(); // Call the callback and expect no call EXPECT_CALL(*cig_callbacks_, OnCigEvent(_, _)).Times(0); ASSERT_FALSE(iso_cb.is_null()); uint8_t hci_mock_rsp_buffer[3 + sizeof(uint16_t) * volatile_test_cig_create_cmpl_evt_.conn_handles.size()]; uint8_t* p = hci_mock_rsp_buffer; UINT8_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.status); UINT8_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.cig_id); UINT8_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles.size()); for (auto handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { UINT16_TO_STREAM(p, handle); } std::move(iso_cb).Run( hci_mock_rsp_buffer, 3 + sizeof(uint16_t) * volatile_test_cig_create_cmpl_evt_.conn_handles.size()); } // 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, ReconfigureCigLateArrivingCallback) { IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Catch the callback base::OnceCallback iso_cb; ON_CALL(hcic_interface_, SetCigParams) .WillByDefault( [&](auto /* cig_id */, auto, base::OnceCallback cb) { iso_cb = std::move(cb); }); IsoManager::GetInstance()->ReconfigureCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams2); // Stop the IsoManager before calling the callback IsoManager::GetInstance()->Stop(); // Call the callback and expect no call EXPECT_CALL(*cig_callbacks_, OnCigEvent(_, _)).Times(0); ASSERT_FALSE(iso_cb.is_null()); uint8_t hci_mock_rsp_buffer[3 + sizeof(uint16_t) * volatile_test_cig_create_cmpl_evt_.conn_handles.size()]; uint8_t* p = hci_mock_rsp_buffer; UINT8_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.status); UINT8_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.cig_id); UINT8_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles.size()); for (auto handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { UINT16_TO_STREAM(p, handle); } std::move(iso_cb).Run( hci_mock_rsp_buffer, 3 + sizeof(uint16_t) * volatile_test_cig_create_cmpl_evt_.conn_handles.size()); } 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, RemoveCigForceNoSuchCig) { 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, true); } 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}; ASSERT_EQ(IsIsoActive, false); IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ASSERT_EQ(IsIsoActive, true); 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); ASSERT_EQ(IsIsoActive, false); } 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/connecting/cancelled"); } 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) { uint8_t cig_id = volatile_test_cig_create_cmpl_evt_.cig_id; IsoManager::GetInstance()->CreateCig(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, cig_id](uint8_t /* type */, void* data) { bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt = static_cast(data); ASSERT_EQ(evt->status, invalid_status); ASSERT_EQ(evt->cig_id, cig_id); 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, EstablishCisLateArrivingCallback) { IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); // Catch the callback base::OnceCallback iso_cb; EXT_CIS_CREATE_CFG cis_create_cfg; uint8_t cis_num = 0; ON_CALL(hcic_interface_, CreateCis) .WillByDefault([&](uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg, base::OnceCallback cb) { cis_create_cfg = *cis_cfg; cis_num = num_cis; iso_cb = std::move(cb); }); // 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); // Stop the IsoManager before calling the callback IsoManager::GetInstance()->Stop(); // Call the callback and expect no call EXPECT_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(0); ASSERT_FALSE(iso_cb.is_null()); // Command complete with error will trigger the callback without // injecting any additional HCI events std::vector buf(1); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, 0x01); // status std::move(iso_cb).Run(buf.data(), buf.size()); } TEST_F(IsoManagerTest, CancelPendingCreateCis_EstablishedThenDisconnected) { /** * Verify the HCI Disconnect command will cancel pending CIS creation. * As the Core is not strict about event order, in this scenario HCI CIS Established event comes * before HCI Disconnection Complete event. * * Scenario: * 1. Issue the HCI LE Create CIS command. * 2. Issue HCI Disconnect command with CIS connection handle parameter before the HCI CIS * Established event is received. * 3. Verify the kIsoEventCisEstablishCmpl event is generated once HCI CIS Established event is * received with Operation Cancelled By Local Host error. * 4. Verify the kIsoEventCisDisconnected event is generated once HCI Disconnection Complete event * is received. */ IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, CreateCis) .WillByDefault([](uint8_t, const EXT_CIS_CREATE_CFG*, base::OnceCallback /* cb */) { /* We override default mock. Nothing to do here */ }); ON_CALL(hcic_interface_, Disconnect).WillByDefault([](uint16_t, uint8_t) { /* We override default mock. Nothing to do here */ }); EXPECT_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this](uint8_t /* type */, void* data) { auto* event = static_cast(data); ASSERT_EQ(event->status, HCI_ERR_CANCELLED_BY_LOCAL_HOST); ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_TRUE(std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), event->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); EXPECT_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDisconnected, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this](uint8_t /* type */, void* data) { auto* event = static_cast(data); ASSERT_EQ(event->reason, HCI_ERR_CONN_CAUSE_LOCAL_HOST); ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_TRUE(std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), event->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); EXPECT_CALL(hcic_interface_, CreateCis).Times(1); // 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); EXPECT_CALL(hcic_interface_, Disconnect).Times(kDefaultCigParams.cis_cfgs.size()); /* Cancel pending HCI LE Create CIS command */ for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->DisconnectCis(handle, HCI_ERR_CONN_CAUSE_LOCAL_HOST); } for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { std::vector buf(28, 0); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_ERR_CANCELLED_BY_LOCAL_HOST); UINT16_TO_STREAM(p, handle); /* inject HCI LE CIS Established event */ IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT, buf.data(), buf.size()); /* followed by HCI Disconnection Complete event */ IsoManager::GetInstance()->HandleDisconnect(handle, HCI_ERR_CONN_CAUSE_LOCAL_HOST); } } TEST_F(IsoManagerTest, CancelPendingCreateCis_DisconnectedThenEstablished) { /** * Verify the HCI Disconnect command will cancel pending CIS creation. * As the Core is not strict about event order, in this scenario HCI Disconnection Complete event * comes before HCI CIS Established event. * * Scenario: * 1. Issue the HCI LE Create CIS command. * 2. Issue HCI Disconnect command with CIS connection handle parameter before the HCI CIS * Established event is received. * 3. Verify the kIsoEventCisEstablishCmpl event is generated once HCI CIS Established event is * received with Operation Cancelled By Local Host error. * 4. Verify the kIsoEventCisDisconnected event is generated once HCI Disconnection Complete event * is received. */ IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); ON_CALL(hcic_interface_, CreateCis) .WillByDefault([](uint8_t, const EXT_CIS_CREATE_CFG*, base::OnceCallback /* cb */) { /* We override default mock. Nothing to do here */ }); ON_CALL(hcic_interface_, Disconnect).WillByDefault([](uint16_t, uint8_t) { /* We override default mock. Nothing to do here */ }); EXPECT_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this](uint8_t /* type */, void* data) { auto* event = static_cast(data); ASSERT_EQ(event->status, HCI_ERR_CANCELLED_BY_LOCAL_HOST); ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_TRUE(std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), event->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); EXPECT_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDisconnected, _)) .Times(kDefaultCigParams.cis_cfgs.size()) .WillRepeatedly([this](uint8_t /* type */, void* data) { auto* event = static_cast(data); ASSERT_EQ(event->reason, HCI_ERR_CONN_CAUSE_LOCAL_HOST); ASSERT_EQ(event->cig_id, volatile_test_cig_create_cmpl_evt_.cig_id); ASSERT_TRUE(std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(), volatile_test_cig_create_cmpl_evt_.conn_handles.end(), event->cis_conn_hdl) != volatile_test_cig_create_cmpl_evt_.conn_handles.end()); }); EXPECT_CALL(hcic_interface_, CreateCis).Times(1); // 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); EXPECT_CALL(hcic_interface_, Disconnect).Times(kDefaultCigParams.cis_cfgs.size()); /* Cancel pending HCI LE Create CIS command */ for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->DisconnectCis(handle, HCI_ERR_CONN_CAUSE_LOCAL_HOST); } for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { /* inject HCI Disconnection Complete event */ IsoManager::GetInstance()->HandleDisconnect(handle, HCI_ERR_CONN_CAUSE_LOCAL_HOST); std::vector buf(28, 0); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_ERR_CANCELLED_BY_LOCAL_HOST); UINT16_TO_STREAM(p, handle); /* followed by inject HCI LE CIS Established event */ IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT, buf.data(), buf.size()); } } 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), "Bis count is 0"); } 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; ASSERT_EQ(IsIsoActive, false); IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams); ASSERT_EQ(IsIsoActive, true); 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); ASSERT_EQ(IsIsoActive, false); } 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, SetupIsoDataPathLateArrivingCallback) { 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; // Catch the callback base::OnceCallback iso_cb; ON_CALL(hcic_interface_, SetupIsoDataPath) .WillByDefault([&iso_cb](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) { iso_cb = std::move(cb); }); // Setup and remove data paths for all CISes path_params.data_path_dir = bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput; auto& handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); // Stop the IsoManager before calling the callback IsoManager::GetInstance()->Stop(); // Call the callback and expect no call EXPECT_CALL(*cig_callbacks_, OnSetupIsoDataPath(_, handle, _)).Times(0); ASSERT_FALSE(iso_cb.is_null()); std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, handle); std::move(iso_cb).Run(buf.data(), buf.size()); } 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::kRemoveIsoDataPathDirectionInput; 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, RemoveIsoDataPathLateArrivingCallback) { 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::kRemoveIsoDataPathDirectionInput; auto& handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); // Catch the callback base::OnceCallback iso_cb; ON_CALL(hcic_interface_, RemoveIsoDataPath) .WillByDefault([&iso_cb](uint16_t /* iso_handle */, uint8_t /* data_path_dir */, base::OnceCallback cb) { iso_cb = std::move(cb); }); IsoManager::GetInstance()->RemoveIsoDataPath(handle, path_params.data_path_dir); // Stop the IsoManager before calling the callback IsoManager::GetInstance()->Stop(); // Call the callback and expect no call EXPECT_CALL(*cig_callbacks_, OnRemoveIsoDataPath(HCI_SUCCESS, handle, volatile_test_cig_create_cmpl_evt_.cig_id)) .Times(0); ASSERT_FALSE(iso_cb.is_null()); std::vector buf(3); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, handle); std::move(iso_cb).Run(buf.data(), buf.size()); } TEST_F(IsoManagerTest, 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]; IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); EXPECT_CALL(iso_interface_, HciSend).Times(0); } 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(iso_interface_, HciSend) .WillOnce([handle, data_len](BT_HDR* p_msg) { uint8_t* p = p_msg->data; uint16_t msg_handle; uint16_t iso_load_len; 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, SendReceiveIsoDataSequenceNumberCheck) { 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); constexpr uint8_t data_len = 108; uint16_t seq_num = 0xFFFF; EXPECT_CALL(iso_interface_, HciSend) .WillRepeatedly([handle, data_len, &seq_num](BT_HDR* p_msg) { uint8_t* p = p_msg->data; uint16_t msg_handle; uint16_t iso_load_len; 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 } // store the seq_nb STREAM_TO_UINT16(seq_num, p); uint16_t msg_data_len; STREAM_TO_UINT16(msg_data_len, p); ASSERT_EQ(msg_data_len, data_len); }) .RetiresOnSaturation(); // Send Iso data and verify the sequence number std::vector data_vec(data_len, 0); IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); ASSERT_NE(0xFFFF, seq_num); // Check the receiving iso packet // EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(1); EXPECT_CALL(*cig_callbacks_, OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDataAvailable, _)) .WillOnce([](uint8_t /*evt_code*/, void* event) { bluetooth::hci::iso_manager::cis_data_evt* cis_data_evt = static_cast(event); // Make sure no event lost is reported due to seq_nb being shared between two // directions ASSERT_EQ(cis_data_evt->evt_lost, 0); }); 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()); } } 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(iso_interface_, HciSend) .WillOnce([handle, data_len](BT_HDR* p_msg) { uint8_t* p = p_msg->data; uint16_t msg_handle; uint16_t iso_load_len; 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_.GetControllerIsoBufferSize().total_num_le_packets_; 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(iso_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 IsoManager::GetInstance()->HandleNumComplDataPkts( volatile_test_cig_create_cmpl_evt_.conn_handles[0], num_buffers); // 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(iso_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_.GetControllerIsoBufferSize().total_num_le_packets_; 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(iso_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 IsoManager::GetInstance()->HandleNumComplDataPkts( volatile_test_cig_create_cmpl_evt_.conn_handles[0], num_buffers); // Expect some more events go down the HCI EXPECT_CALL(iso_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 IsoManager::GetInstance()->HandleNumComplDataPkts( volatile_test_cig_create_cmpl_evt_.conn_handles[0], num_buffers); // 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(iso_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 IsoManager::GetInstance()->HandleNumComplDataPkts(volatile_test_big_params_evt_.conn_handles[0], num_buffers); // Expect some more events go down the HCI EXPECT_CALL(iso_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(IsoManagerTest, SendIsoDataCreditsReturnedByDisconnection) { uint8_t num_buffers = controller_.GetControllerIsoBufferSize().total_num_le_packets_; 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); for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->SetupIsoDataPath(handle, kDefaultIsoDataPathParams); } /* Sending lot of ISO data to first ISO and getting all the credits */ EXPECT_CALL(iso_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < num_buffers; i++) { IsoManager::GetInstance()->SendIsoData(volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } /* Return all credits by disconnecting CIS */ IsoManager::GetInstance()->HandleDisconnect(volatile_test_cig_create_cmpl_evt_.conn_handles[0], 16); /* Try to send ISO data on the second ISO. Expect credits being available.*/ EXPECT_CALL(iso_interface_, HciSend).Times(num_buffers).RetiresOnSaturation(); for (uint8_t i = 0; i < num_buffers; i++) { IsoManager::GetInstance()->SendIsoData(volatile_test_cig_create_cmpl_evt_.conn_handles[1], 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(iso_interface_, HciSend).Times(0); IsoManager::GetInstance()->SendIsoData(volatile_test_cig_create_cmpl_evt_.conn_handles[0], data_vec.data(), data_vec.size()); // Check on BIG IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); EXPECT_CALL(iso_interface_, HciSend).Times(0); IsoManager::GetInstance()->SendIsoData(volatile_test_big_params_evt_.conn_handles[0], data_vec.data(), data_vec.size()); } 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(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, HandleDisconnectLateArrivingCallback) { 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(0); // Expect no callback on late arriving event IsoManager::GetInstance()->Stop(); 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_.GetControllerIsoBufferSize().total_num_le_packets_; 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 IsoManager::GetInstance()->HandleNumComplDataPkts(handle, num_buffers); } /* 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()); } /* This test makes sure we do not crash when calling into a non-started Iso Manager */ TEST_F(IsoManagerDeathTestNoCleanup, HandleApiCallsWhenStopped) { IsoManager::GetInstance()->Stop(); IsoManager::GetInstance()->RegisterCigCallbacks(cig_callbacks_.get()); IsoManager::GetInstance()->RegisterBigCallbacks(big_callbacks_.get()); IsoManager::GetInstance()->RegisterOnIsoTrafficActiveCallback(iso_active_callback); IsoManager::GetInstance()->CreateCig(volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams); IsoManager::GetInstance()->ReconfigureCig(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); bluetooth::hci::iso_manager::iso_data_path_params path_params = kDefaultIsoDataPathParams; for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { path_params.data_path_dir = bluetooth::hci::iso_manager::kIsoDataPathDirectionIn; IsoManager::GetInstance()->SetupIsoDataPath(handle, path_params); path_params.data_path_dir = bluetooth::hci::iso_manager::kRemoveIsoDataPathDirectionInput; IsoManager::GetInstance()->RemoveIsoDataPath(handle, path_params.data_path_dir); } auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->ReadIsoLinkQuality(handle); std::vector data_vec(108, 0); IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(), data_vec.size()); for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) { IsoManager::GetInstance()->IsoManager::GetInstance()->DisconnectCis(handle, 0x16); } IsoManager::GetInstance()->RemoveCig(volatile_test_cig_create_cmpl_evt_.cig_id); (void)IsoManager::GetInstance()->GetNumberOfActiveIso(); IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id, kDefaultBigParams); IsoManager::GetInstance()->TerminateBig(volatile_test_big_params_evt_.big_id, 0x16); } 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()); } TEST_F(IsoManagerTest, ReadIsoLinkQualityLateArrivingCallback) { 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 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); // Catch the callback base::OnceCallback iso_cb; ON_CALL(hcic_interface_, ReadIsoLinkQuality) .WillByDefault([&iso_cb](uint16_t /* iso_handle */, base::OnceCallback cb) { iso_cb = std::move(cb); }); auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0]; IsoManager::GetInstance()->ReadIsoLinkQuality(handle); // Stop the IsoManager before calling the callback IsoManager::GetInstance()->Stop(); // Call the callback and expect no call EXPECT_CALL(*cig_callbacks_, OnIsoLinkQualityRead(handle, _, _, _, _, _, _, _, _)).Times(0); ASSERT_FALSE(iso_cb.is_null()); std::vector buf(31); uint8_t* p = buf.data(); UINT8_TO_STREAM(p, HCI_SUCCESS); UINT16_TO_STREAM(p, handle); UINT32_TO_STREAM(p, 0); UINT32_TO_STREAM(p, 0); UINT32_TO_STREAM(p, 0); UINT32_TO_STREAM(p, 0); UINT32_TO_STREAM(p, 0); UINT32_TO_STREAM(p, 0); UINT32_TO_STREAM(p, 0); std::move(iso_cb).Run(buf.data(), buf.size()); }