blob: 3c7a1f88f7a0cf5ca04fd8d5ab3a8d7779587dcb [file] [log] [blame]
/*
* 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 <gmock/gmock.h>
#include <gtest/gtest.h>
#include "btm_iso_api.h"
#include "hci/include/hci_layer.h"
#include "main/shim/hci_layer.h"
#include "mock_controller.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_hci_layer.h"
using bluetooth::hci::IsoManager;
using testing::_;
using testing::AnyNumber;
using testing::AtLeast;
using testing::Eq;
using testing::Matcher;
using testing::Return;
using testing::SaveArg;
using testing::StrictMock;
using testing::Test;
// 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<void(const base::Location&, BT_HDR*)> /* 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_);
controller::SetMockControllerInterface(&controller_interface_);
bluetooth::shim::testing::hci_layer_set_interface(
&bluetooth::shim::interface);
big_callbacks_.reset(new MockBigCallbacks());
cig_callbacks_.reset(new MockCigCallbacks());
IsIsoActive = false;
EXPECT_CALL(controller_interface_, GetIsoBufferCount())
.Times(AtLeast(1))
.WillRepeatedly(Return(6));
EXPECT_CALL(controller_interface_, GetIsoDataSize())
.Times(AtLeast(1))
.WillRepeatedly(Return(1024));
InitIsoManager();
}
void TearDown() override {
CleanupIsoManager();
big_callbacks_.reset();
cig_callbacks_.reset();
bluetooth::shim::SetMockIsoInterface(nullptr);
hcic::SetMockHcicInterface(nullptr);
controller::SetMockControllerInterface(nullptr);
bluetooth::shim::testing::hci_layer_set_interface(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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> cb) {
for (const EXT_CIS_CREATE_CFG* cis = cis_cfg; num_cis != 0;
num_cis--, cis++) {
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<uint8_t> /* codec_conf */,
base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
std::vector<uint8_t> 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<void(uint8_t*, uint16_t)> cb) {
std::vector<uint8_t> 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_;
controller::MockControllerInterface controller_interface_;
std::unique_ptr<MockBigCallbacks> big_callbacks_;
std::unique_ptr<MockCigCallbacks> 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<uint16_t>({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<uint16_t>({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<uint8_t, 16>({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<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
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<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
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<uint16_t>({0x0EFF, 0x00FF}).begin()));
}
TEST_F(IsoManagerTest, CreateCigLateArrivingCallback) {
// Catch the callback
base::OnceCallback<void(uint8_t*, uint16_t)> iso_cb;
ON_CALL(hcic_interface_, SetCigParams)
.WillByDefault([&](auto cig_id, auto,
base::OnceCallback<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
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<bluetooth::hci::iso_manager::cig_create_cmpl_evt*>(
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<void(uint8_t*, uint16_t)> iso_cb;
ON_CALL(hcic_interface_, SetCigParams)
.WillByDefault([&](auto cig_id, auto,
base::OnceCallback<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<bluetooth::hci::iso_manager::cig_remove_cmpl_evt*>(
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<void(uint8_t*, uint16_t)> 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<bluetooth::hci::iso_manager::cig_remove_cmpl_evt*>(
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 or connecting");
}
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<void(uint8_t*, uint16_t)> cb) {
for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
std::vector<uint8_t> 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<void(uint8_t*, uint16_t)> 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<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
data);
ASSERT_EQ(evt->status, invalid_status);
ASSERT_TRUE(
std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(),
volatile_test_cig_create_cmpl_evt_.conn_handles.end(),
evt->cis_conn_hdl) !=
volatile_test_cig_create_cmpl_evt_.conn_handles.end());
});
// Establish all CISes
bluetooth::hci::iso_manager::cis_establish_params params;
for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
params.conn_pairs.push_back({handle, 1});
}
IsoManager::GetInstance()->EstablishCis(params);
}
TEST_F(IsoManagerTest, EstablishCisInvalidStatus) {
IsoManager::GetInstance()->CreateCig(
volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
uint8_t invalid_status = 0x01;
ON_CALL(hcic_interface_, CreateCis)
.WillByDefault([this, invalid_status](
uint8_t num_cis, const EXT_CIS_CREATE_CFG* cis_cfg,
base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
std::vector<uint8_t> buf(28);
uint8_t* p = buf.data();
UINT8_TO_STREAM(p, invalid_status);
UINT16_TO_STREAM(p, handle);
UINT24_TO_STREAM(p, 0xEA); // CIG sync delay
UINT24_TO_STREAM(p, 0xEB); // CIS sync delay
UINT24_TO_STREAM(p, 0xEC); // transport latency mtos
UINT24_TO_STREAM(p, 0xED); // transport latency stom
UINT8_TO_STREAM(p, 0x01); // phy mtos
UINT8_TO_STREAM(p, 0x02); // phy stom
UINT8_TO_STREAM(p, 0x01); // nse
UINT8_TO_STREAM(p, 0x02); // bn mtos
UINT8_TO_STREAM(p, 0x03); // bn stom
UINT8_TO_STREAM(p, 0x04); // ft mtos
UINT8_TO_STREAM(p, 0x05); // ft stom
UINT16_TO_STREAM(p, 0x00FA); // Max PDU mtos
UINT16_TO_STREAM(p, 0x00FB); // Max PDU stom
UINT16_TO_STREAM(p, 0x0C60); // ISO interval
IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_CIS_EST_EVT,
buf.data(), buf.size());
}
});
EXPECT_CALL(
*cig_callbacks_,
OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _))
.Times(kDefaultCigParams.cis_cfgs.size())
.WillRepeatedly([this, invalid_status](uint8_t type, void* data) {
bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt =
static_cast<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
data);
ASSERT_EQ(evt->status, invalid_status);
ASSERT_TRUE(
std::find(volatile_test_cig_create_cmpl_evt_.conn_handles.begin(),
volatile_test_cig_create_cmpl_evt_.conn_handles.end(),
evt->cis_conn_hdl) !=
volatile_test_cig_create_cmpl_evt_.conn_handles.end());
});
// Establish all CISes before setting up their data paths
bluetooth::hci::iso_manager::cis_establish_params params;
for (auto& handle : volatile_test_cig_create_cmpl_evt_.conn_handles) {
params.conn_pairs.push_back({handle, 1});
}
IsoManager::GetInstance()->EstablishCis(params);
}
TEST_F(IsoManagerTest, EstablishCisValid) {
IsoManager::GetInstance()->CreateCig(
volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
EXPECT_CALL(
*cig_callbacks_,
OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisEstablishCmpl, _))
.Times(kDefaultCigParams.cis_cfgs.size())
.WillRepeatedly([this](uint8_t type, void* data) {
bluetooth::hci::iso_manager::cis_establish_cmpl_evt* evt =
static_cast<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
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<void(uint8_t*, uint16_t)> 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<void(uint8_t*, uint16_t)> 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<uint8_t> 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, 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<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
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<bluetooth::hci::iso_manager::cis_disconnected_evt*>(
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<bluetooth::hci::iso_manager::big_create_cmpl_evt*>(
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<uint8_t> 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),
"num_bis != 0. 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<uint8_t> 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<bluetooth::hci::iso_manager::big_create_cmpl_evt*>(
data);
return 0;
});
ON_CALL(hcic_interface_, CreateBig)
.WillByDefault(
[](auto big_handle,
bluetooth::hci::iso_manager::big_create_params big_params) {
std::vector<uint8_t> 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<bluetooth::hci::iso_manager::big_create_cmpl_evt*>(
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<uint8_t> 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<uint8_t> 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<uint8_t> 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<bluetooth::hci::iso_manager::big_terminate_cmpl_evt*>(
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<uint8_t>,
base::OnceCallback<void(uint8_t*, uint16_t)> cb) {
std::vector<uint8_t> 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<void(uint8_t*, uint16_t)> 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<uint8_t> /* codec_conf */,
base::OnceCallback<void(uint8_t*, uint16_t)> 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<uint8_t> 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<void(uint8_t*, uint16_t)> cb) {
std::vector<uint8_t> 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<void(uint8_t*, uint16_t)> iso_cb;
ON_CALL(hcic_interface_, RemoveIsoDataPath)
.WillByDefault(
[&iso_cb](uint16_t iso_handle, uint8_t data_path_dir,
base::OnceCallback<void(uint8_t*, uint16_t)> 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<uint8_t> 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<uint8_t> 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<uint8_t> data_vec(data_len, 0);
IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(),
data_vec.size());
}
}
}
TEST_F(IsoManagerTest, SendIsoDataBigValid) {
IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id,
kDefaultBigParams);
for (auto& handle : volatile_test_big_params_evt_.conn_handles) {
IsoManager::GetInstance()->SetupIsoDataPath(handle,
kDefaultIsoDataPathParams);
for (uint8_t num_pkts = 2; num_pkts != 0; num_pkts--) {
constexpr uint8_t data_len = 108;
EXPECT_CALL(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<uint8_t> data_vec(data_len, 0);
IsoManager::GetInstance()->SendIsoData(handle, data_vec.data(),
data_vec.size());
}
}
}
TEST_F(IsoManagerTest, SendIsoDataNoCredits) {
uint8_t num_buffers = controller_interface_.GetIsoBufferCount();
std::vector<uint8_t> 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
uint8_t mock_rsp[5];
uint8_t* p = mock_rsp;
UINT8_TO_STREAM(p, 1);
UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]);
UINT16_TO_STREAM(p, num_buffers);
IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp));
// Check on BIG
IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id,
kDefaultBigParams);
IsoManager::GetInstance()->SetupIsoDataPath(
volatile_test_big_params_evt_.conn_handles[0], kDefaultIsoDataPathParams);
/* Try sending twice as much data as we can ignoring the credit limits and
* expect the redundant packets to be ignored and not propagated down to the
* HCI.
*/
EXPECT_CALL(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_interface_.GetIsoBufferCount();
std::vector<uint8_t> 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
uint8_t mock_rsp[5];
uint8_t* p = mock_rsp;
UINT8_TO_STREAM(p, 1);
UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]);
UINT16_TO_STREAM(p, num_buffers);
IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp));
// Expect some more events go down the HCI
EXPECT_CALL(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
p = mock_rsp;
UINT8_TO_STREAM(p, 1);
UINT16_TO_STREAM(p, volatile_test_cig_create_cmpl_evt_.conn_handles[0]);
UINT16_TO_STREAM(p, num_buffers);
IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp));
// Check on BIG
IsoManager::GetInstance()->CreateBig(volatile_test_big_params_evt_.big_id,
kDefaultBigParams);
IsoManager::GetInstance()->SetupIsoDataPath(
volatile_test_big_params_evt_.conn_handles[0], kDefaultIsoDataPathParams);
/* Try sending twice as much data as we can, ignoring the credits limit and
* expect the redundant packets to be ignored and not propagated down to the
* HCI.
*/
EXPECT_CALL(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
p = mock_rsp;
UINT8_TO_STREAM(p, 1);
UINT16_TO_STREAM(p, volatile_test_big_params_evt_.conn_handles[0]);
UINT16_TO_STREAM(p, num_buffers);
IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp));
// Expect some more events go down the HCI
EXPECT_CALL(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_interface_.GetIsoBufferCount();
std::vector<uint8_t> 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<uint8_t> 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<uint8_t> 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<bluetooth::hci::iso_manager::cis_disconnected_evt*>(
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<uint8_t> 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<uint8_t> dummy_msg(18);
uint8_t* p = dummy_msg.data();
UINT16_TO_STREAM(p, BT_EVT_TO_BTU_HCI_ISO);
UINT16_TO_STREAM(p, 10); // .len
UINT16_TO_STREAM(p, 0); // .offset
UINT16_TO_STREAM(p, 0); // .layer_specific
UINT16_TO_STREAM(p, handle);
IsoManager::GetInstance()->HandleIsoData(dummy_msg.data());
}
/* This test case simulates HCI thread scheduling events on the main thread,
* without knowing the we are already shutting down the stack and Iso Manager
* is already stopped.
*/
TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleDisconnect) {
IsoManager::GetInstance()->CreateCig(
volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
IsoManager::GetInstance()->EstablishCis({{{handle, 1}}});
// Stop iso manager before trying to call the HCI callbacks
IsoManager::GetInstance()->Stop();
// Expect no event when callback is being called on a stopped iso manager
EXPECT_CALL(*cig_callbacks_, OnCisEvent).Times(0);
// Expect no assert on this call - should be gracefully ignored
IsoManager::GetInstance()->HandleDisconnect(handle, 16);
}
/* This test case simulates HCI thread scheduling events on the main thread,
* without knowing the we are already shutting down the stack and Iso Manager
* is already stopped.
*/
TEST_F(IsoManagerDeathTestNoCleanup,
HandleLateArivingEventHandleNumComplDataPkts) {
uint8_t num_buffers = controller_interface_.GetIsoBufferCount();
IsoManager::GetInstance()->CreateCig(
volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
IsoManager::GetInstance()->EstablishCis({{{handle, 1}}});
// Stop iso manager before trying to call the HCI callbacks
IsoManager::GetInstance()->Stop();
// Expect no assert on this call - should be gracefully ignored
uint8_t mock_rsp[5];
uint8_t* p = mock_rsp;
UINT8_TO_STREAM(p, 1);
UINT16_TO_STREAM(p, handle);
UINT16_TO_STREAM(p, num_buffers);
IsoManager::GetInstance()->HandleNumComplDataPkts(mock_rsp, sizeof(mock_rsp));
}
/* This test case simulates HCI thread scheduling events on the main thread,
* without knowing the we are already shutting down the stack and Iso Manager
* is already stopped.
*/
TEST_F(IsoManagerDeathTestNoCleanup, HandleLateArivingEventHandleHciEvent) {
const uint8_t big_id = 0x22;
IsoManager::GetInstance()->CreateBig(big_id, kDefaultBigParams);
// Stop iso manager before trying to call the HCI callbacks
IsoManager::GetInstance()->Stop();
EXPECT_CALL(
*big_callbacks_,
OnBigEvent(bluetooth::hci::iso_manager::kIsoEventBigOnTerminateCmpl, _))
.Times(0);
// Expect no assert on this call - should be gracefully ignored
std::vector<uint8_t> buf(2);
uint8_t* p = buf.data();
UINT8_TO_STREAM(p, big_id);
UINT8_TO_STREAM(p, 16); // Terminated by local host
IsoManager::GetInstance()->HandleHciEvent(HCI_BLE_TERM_BIG_CPL_EVT,
buf.data(), buf.size());
}
TEST_F(IsoManagerTest, HandleIsoDataSameSeqNb) {
IsoManager::GetInstance()->CreateCig(
volatile_test_cig_create_cmpl_evt_.cig_id, kDefaultCigParams);
auto handle = volatile_test_cig_create_cmpl_evt_.conn_handles[0];
IsoManager::GetInstance()->EstablishCis({{{handle, 1}}});
EXPECT_CALL(
*cig_callbacks_,
OnCisEvent(bluetooth::hci::iso_manager::kIsoEventCisDataAvailable, _))
.Times(2);
std::vector<uint8_t> 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<bluetooth::hci::iso_manager::cis_establish_cmpl_evt*>(
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<void(uint8_t*, uint16_t)> iso_cb;
ON_CALL(hcic_interface_, ReadIsoLinkQuality)
.WillByDefault(
[&iso_cb](uint16_t iso_handle,
base::OnceCallback<void(uint8_t*, uint16_t)> 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<uint8_t> 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());
}