base::Callback<void(uint8_t[8])> cb);
extern fixed_queue_t* btu_general_alarm_queue;
+constexpr int ADV_DATA_LEN_MAX = 251;
+
struct AdvertisingInstance {
uint8_t inst_id;
bool in_use;
}
VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
+ DivideAndSendData(
+ inst_id, data, cb,
+ base::Bind(&BleAdvertisingManagerImpl::SetDataAdvDataSender,
+ base::Unretained(this), is_scan_rsp));
+ }
- if (is_scan_rsp) {
- GetHciInterface()->SetScanResponseData(inst_id, 0x03, 0x01, data.size(),
- data.data(), cb);
- } else {
- GetHciInterface()->SetAdvertisingData(inst_id, 0x03, 0x01, data.size(),
- data.data(), cb);
+ void SetDataAdvDataSender(uint8_t is_scan_rsp, uint8_t inst_id,
+ uint8_t operation, uint8_t length, uint8_t* data,
+ MultiAdvCb cb) {
+ if (is_scan_rsp)
+ GetHciInterface()->SetScanResponseData(inst_id, operation, 0x01, length,
+ data, cb);
+ else
+ GetHciInterface()->SetAdvertisingData(inst_id, operation, 0x01, length,
+ data, cb);
+ }
+
+ using DataSender = base::Callback<void(
+ uint8_t /*inst_id*/, uint8_t /* operation */, uint8_t /* length */,
+ uint8_t* /* data */, MultiAdvCb /* done */)>;
+
+ void DivideAndSendData(int inst_id, std::vector<uint8_t> data,
+ MultiAdvCb done_cb, DataSender sender) {
+ DivideAndSendDataRecursively(true, inst_id, std::move(data), 0,
+ std::move(done_cb), std::move(sender), 0);
+ }
+
+ static void DivideAndSendDataRecursively(bool isFirst, int inst_id,
+ std::vector<uint8_t> data,
+ int offset, MultiAdvCb done_cb,
+ DataSender sender, uint8_t status) {
+ constexpr uint8_t INTERMEDIATE =
+ 0x00; // Intermediate fragment of fragmented data
+ constexpr uint8_t FIRST = 0x01; // First fragment of fragmented data
+ constexpr uint8_t LAST = 0x02; // Last fragment of fragmented data
+ constexpr uint8_t COMPLETE = 0x03; // Complete extended advertising data
+
+ int dataSize = (int)data.size();
+ if (status != 0 || (!isFirst && offset == dataSize)) {
+ /* if we got error writing data, or reached the end of data */
+ done_cb.Run(status);
+ return;
}
+
+ bool moreThanOnePacket = dataSize - offset > ADV_DATA_LEN_MAX;
+ uint8_t operation = isFirst ? moreThanOnePacket ? FIRST : COMPLETE
+ : moreThanOnePacket ? INTERMEDIATE : LAST;
+ int length = moreThanOnePacket ? ADV_DATA_LEN_MAX : dataSize - offset;
+ int newOffset = offset + length;
+
+ sender.Run(
+ inst_id, operation, length, data.data() + offset,
+ Bind(&BleAdvertisingManagerImpl::DivideAndSendDataRecursively, false,
+ inst_id, std::move(data), newOffset, std::move(done_cb), sender));
}
void SetPeriodicAdvertisingParameters(uint8_t inst_id,
VLOG(1) << "data is: " << base::HexEncode(data.data(), data.size());
- GetHciInterface()->SetPeriodicAdvertisingData(inst_id, 0x03, data.size(),
- data.data(), cb);
+ DivideAndSendData(
+ inst_id, data, cb,
+ base::Bind(&BleAdvertiserHciInterface::SetPeriodicAdvertisingData,
+ base::Unretained(GetHciInterface())));
}
void SetPeriodicAdvertisingEnable(uint8_t inst_id, uint8_t enable,
namespace {
+constexpr uint8_t INTERMEDIATE =
+ 0x00; // Intermediate fragment of fragmented data
+constexpr uint8_t FIRST = 0x01; // First fragment of fragmented data
+constexpr uint8_t LAST = 0x02; // Last fragment of fragmented data
+constexpr uint8_t COMPLETE = 0x03; // Complete extended advertising data
+
class AdvertiserHciMock : public BleAdvertiserHciInterface {
public:
AdvertiserHciMock() = default;
// Expect the whole flow to fail right away
EXPECT_EQ(BTM_BLE_MULTI_ADV_FAILURE, start_advertising_status);
}
+
+TEST_F(BleAdvertisingManagerTest, test_data_sender) {
+ // prepare test input vector
+ const int max_data_size = 1650;
+ std::vector<uint8_t> data(max_data_size);
+ for (int i = 0; i < max_data_size; i++) data[i] = i;
+
+ BleAdvertisingManager::Get()->RegisterAdvertiser(base::Bind(
+ &BleAdvertisingManagerTest::RegistrationCb, base::Unretained(this)));
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, reg_status);
+ int advertiser_id = reg_inst_id;
+
+ status_cb set_data_cb;
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, FIRST, _, 251, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ EXPECT_CALL(*hci_mock,
+ SetAdvertisingData(advertiser_id, INTERMEDIATE, _, 251, _, _))
+ .Times(5)
+ .WillRepeatedly(SaveArg<5>(&set_data_cb));
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, LAST, _, 144, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ for (int i = 0; i < 7; i++) {
+ set_data_cb.Run(0x00);
+ }
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+
+ // ***************** Try again with different data size *********************
+ data.resize(503);
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, FIRST, _, 251, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ EXPECT_CALL(*hci_mock,
+ SetAdvertisingData(advertiser_id, INTERMEDIATE, _, 251, _, _))
+ .Times(1)
+ .WillRepeatedly(SaveArg<5>(&set_data_cb));
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, LAST, _, 1, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ for (int i = 0; i < 3; i++) {
+ set_data_cb.Run(0x00);
+ }
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+
+ // ***************** Try again with different data size *********************
+ data.resize(502);
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, FIRST, _, 251, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, LAST, _, 251, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ for (int i = 0; i < 2; i++) {
+ set_data_cb.Run(0x00);
+ }
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+
+ // ***************** Try again with different data size *********************
+ data.resize(501);
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, FIRST, _, 251, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ EXPECT_CALL(*hci_mock, SetAdvertisingData(advertiser_id, LAST, _, 250, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ for (int i = 0; i < 2; i++) {
+ set_data_cb.Run(0x00);
+ }
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+
+ // ***************** Try again with different data size *********************
+ data.resize(251);
+ EXPECT_CALL(*hci_mock,
+ SetAdvertisingData(advertiser_id, COMPLETE, _, 251, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ set_data_cb.Run(0x00);
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+
+ // ***************** Try again with different data size *********************
+ data.resize(120);
+ EXPECT_CALL(*hci_mock,
+ SetAdvertisingData(advertiser_id, COMPLETE, _, 120, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ set_data_cb.Run(0x00);
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+
+ // ***************** Try again with different data size *********************
+ data.resize(0);
+ EXPECT_CALL(*hci_mock,
+ SetAdvertisingData(advertiser_id, COMPLETE, _, 0, _, _))
+ .Times(1)
+ .WillOnce(SaveArg<5>(&set_data_cb));
+ BleAdvertisingManager::Get()->SetData(
+ advertiser_id, false, data,
+ base::Bind(&BleAdvertisingManagerTest::SetDataCb,
+ base::Unretained(this)));
+ set_data_cb.Run(0x00);
+ ::testing::Mock::VerifyAndClearExpectations(hci_mock.get());
+ // Expect the whole flow to succeed
+ EXPECT_EQ(BTM_BLE_MULTI_ADV_SUCCESS, set_data_status);
+}