From 331c80117f1ab8c3d3c464ecc7532e97ee69b834 Mon Sep 17 00:00:00 2001 From: Jakub Pawlowski Date: Thu, 16 Mar 2017 22:10:32 -0700 Subject: [PATCH] Divide advertise data when needed Bug: 30622771 Test: BleAdvertisingManagerTest.test_data_sender Change-Id: I447fed753b08cef766d99ea8dfa47b1212a9ce03 --- stack/btm/btm_ble_multi_adv.cc | 66 ++++++++++++++--- stack/test/ble_advertiser_test.cc | 146 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 204 insertions(+), 8 deletions(-) diff --git a/stack/btm/btm_ble_multi_adv.cc b/stack/btm/btm_ble_multi_adv.cc index 07c3717ed..a733861ae 100644 --- a/stack/btm/btm_ble_multi_adv.cc +++ b/stack/btm/btm_ble_multi_adv.cc @@ -38,6 +38,8 @@ extern void btm_gen_resolvable_private_addr( base::Callback 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; @@ -586,14 +588,60 @@ class BleAdvertisingManagerImpl } 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 DivideAndSendData(int inst_id, std::vector 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 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, @@ -612,8 +660,10 @@ class BleAdvertisingManagerImpl 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, diff --git a/stack/test/ble_advertiser_test.cc b/stack/test/ble_advertiser_test.cc index 03296bdf0..497770451 100644 --- a/stack/test/ble_advertiser_test.cc +++ b/stack/test/ble_advertiser_test.cc @@ -65,6 +65,12 @@ fixed_queue_t* btu_general_alarm_queue = nullptr; 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; @@ -467,3 +473,143 @@ TEST_F(BleAdvertisingManagerTest, test_start_advertising_set_params_failed) { // 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 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); +} -- 2.11.0