OSDN Git Service

Divide advertise data when needed
authorJakub Pawlowski <jpawlowski@google.com>
Fri, 17 Mar 2017 05:10:32 +0000 (22:10 -0700)
committerJakub Pawlowski <jpawlowski@google.com>
Fri, 17 Mar 2017 06:21:09 +0000 (23:21 -0700)
Bug: 30622771
Test: BleAdvertisingManagerTest.test_data_sender
Change-Id: I447fed753b08cef766d99ea8dfa47b1212a9ce03

stack/btm/btm_ble_multi_adv.cc
stack/test/ble_advertiser_test.cc

index 07c3717..a733861 100644 (file)
@@ -38,6 +38,8 @@ extern void btm_gen_resolvable_private_addr(
     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;
@@ -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(
+      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,
@@ -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,
index 03296bd..4977704 100644 (file)
@@ -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<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);
+}