Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit abbbb133 authored by Henri Chataing's avatar Henri Chataing Committed by Gerrit Code Review
Browse files

Merge "le_scanning: Extended le_scanning_reassembler to support periodic...

Merge "le_scanning: Extended le_scanning_reassembler to support periodic advertising events" into main
parents 0161d636 3d0d1dad
Loading
Loading
Loading
Loading
+15 −1
Original line number Diff line number Diff line
@@ -15,6 +15,8 @@
 */
#pragma once

#include <android_bluetooth_flags.h>

#include <chrono>
#include <memory>
#include <utility>
@@ -26,6 +28,7 @@
#include "hci/hci_packets.h"
#include "hci/le_scanning_callback.h"
#include "hci/le_scanning_interface.h"
#include "hci/le_scanning_reassembler.h"
#include "hci/uuid.h"
#include "module.h"
#include "os/alarm.h"
@@ -313,13 +316,23 @@ class PeriodicSyncManager {
      LOG_ERROR("[PSync]: index not found for handle %u", sync_handle);
      return;
    }

    auto complete_advertising_data =
        IS_FLAG_ENABLED(le_periodic_scanning_reassembler)
            ? scanning_reassembler_.ProcessPeriodicAdvertisingReport(
                  sync_handle, DataStatus(event_view.GetDataStatus()), event_view.GetData())
            : event_view.GetData();
    if (!complete_advertising_data.has_value()) {
      return;
    }

    LOG_DEBUG("%s", "[PSync]: invoking callback");
    callbacks_->OnPeriodicSyncReport(
        sync_handle,
        event_view.GetTxPower(),
        event_view.GetRssi(),
        (uint16_t)event_view.GetDataStatus(),
        event_view.GetData());
        complete_advertising_data.value());
  }

  void HandleLePeriodicAdvertisingSyncLost(LePeriodicAdvertisingSyncLostView event_view) {
@@ -532,6 +545,7 @@ class PeriodicSyncManager {
  std::list<PendingPeriodicSyncRequest> pending_sync_requests_;
  std::list<PeriodicSyncStates> periodic_syncs_;
  std::list<PeriodicSyncTransferStates> periodic_sync_transfers_;
  LeScanningReassembler scanning_reassembler_;
  bool sync_received_callback_registered_ = false;
  int sync_received_callback_id{};
};
+49 −0
Original line number Diff line number Diff line
@@ -97,6 +97,25 @@ LeScanningReassembler::ProcessAdvertisingReport(
  return result;
}

std::optional<std::vector<uint8_t>> LeScanningReassembler::ProcessPeriodicAdvertisingReport(
    uint16_t sync_handle, DataStatus data_status, const std::vector<uint8_t>& advertising_data) {
  // Concatenate the data with existing fragments.
  std::list<PeriodicAdvertisingFragment>::iterator advertising_fragment =
      AppendPeriodicFragment(sync_handle, advertising_data);

  // Return and wait for additional fragments if the data is marked as
  // incomplete.
  if (data_status == DataStatus::CONTINUING) {
    return {};
  }

  // The complete payload has been received; trim the advertising data,
  // remove the cache entry and return the complete advertising data.
  std::vector<uint8_t> result = TrimAdvertisingData(advertising_fragment->data);
  periodic_cache_.erase(advertising_fragment);
  return result;
}

/// Trim the advertising data by removing empty or overflowing
/// GAP Data entries.
std::vector<uint8_t> LeScanningReassembler::TrimAdvertisingData(
@@ -189,4 +208,34 @@ std::list<LeScanningReassembler::AdvertisingFragment>::iterator LeScanningReasse
  return cache_.end();
}

/// Append to the current advertising data of the selected periodic advertiser.
/// If the advertiser is unknown a new entry is added, optionally by
/// dropping the oldest advertiser.
std::list<LeScanningReassembler::PeriodicAdvertisingFragment>::iterator
LeScanningReassembler::AppendPeriodicFragment(
    uint16_t sync_handle, const std::vector<uint8_t>& data) {
  auto it = FindPeriodicFragment(sync_handle);
  if (it != periodic_cache_.end()) {
    it->data.insert(it->data.end(), data.cbegin(), data.cend());
    return it;
  }

  if (periodic_cache_.size() > kMaximumPeriodicCacheSize) {
    periodic_cache_.pop_back();
  }

  periodic_cache_.emplace_front(sync_handle, data);
  return periodic_cache_.begin();
}

std::list<LeScanningReassembler::PeriodicAdvertisingFragment>::iterator
LeScanningReassembler::FindPeriodicFragment(uint16_t sync_handle) {
  for (auto it = periodic_cache_.begin(); it != periodic_cache_.end(); it++) {
    if (it->sync_handle == sync_handle) {
      return it;
    }
  }
  return periodic_cache_.end();
}

}  // namespace bluetooth::hci
+25 −0
Original line number Diff line number Diff line
@@ -58,6 +58,13 @@ class LeScanningReassembler {
      uint8_t advertising_sid,
      const std::vector<uint8_t>& advertising_data);

  /// Process an incoming periodic advertising report, extracted from the
  /// HCI LE Periodic Advertising Report events.
  /// Returns the completed advertising data if the event was complete,
  /// or the completion of a fragmented advertising event.
  std::optional<std::vector<uint8_t>> ProcessPeriodicAdvertisingReport(
      uint16_t sync_handle, DataStatus status, const std::vector<uint8_t>& advertising_data);

  /// Configure the scan response filter.
  /// If true all scan responses are ignored.
  void SetIgnoreScanResponses(bool ignore_scan_responses) {
@@ -105,6 +112,15 @@ class LeScanningReassembler {
        : key(key), extended_event_type(extended_event_type), data(data.begin(), data.end()) {}
  };

  /// Packs incomplete periodic advertising data.
  struct PeriodicAdvertisingFragment {
    std::optional<uint16_t> sync_handle;
    std::vector<uint8_t> data;

    PeriodicAdvertisingFragment(uint16_t sync_handle, const std::vector<uint8_t>& data)
        : sync_handle(sync_handle), data(data.begin(), data.end()) {}
  };

  /// Advertising cache for de-fragmenting extended advertising reports,
  /// and joining advertising reports with the matching scan response when
  /// applicable.
@@ -123,6 +139,15 @@ class LeScanningReassembler {

  std::list<AdvertisingFragment>::iterator FindFragment(const AdvertisingKey& key);

  /// Advertising cache for de-fragmenting periodic advertising reports.
  static constexpr size_t kMaximumPeriodicCacheSize = 16;
  std::list<PeriodicAdvertisingFragment> periodic_cache_;

  std::list<PeriodicAdvertisingFragment>::iterator AppendPeriodicFragment(
      uint16_t sync_handle, const std::vector<uint8_t>& data);

  std::list<PeriodicAdvertisingFragment>::iterator FindPeriodicFragment(uint16_t sync_handle);

  /// Trim the advertising data by removing empty or overflowing
  /// GAP Data entries.
  static std::vector<uint8_t> TrimAdvertisingData(const std::vector<uint8_t>& advertising_data);
+48 −0
Original line number Diff line number Diff line
@@ -42,6 +42,10 @@ static constexpr uint8_t kSidNotPresent = 0xff;
// Test addresses.
static const Address kTestAddress = Address({0, 1, 2, 3, 4, 5});

// Test sync handles.
static const uint16_t kTestSyncHandle1 = 0x4242;
static const uint16_t kTestSyncHandle2 = 0x4243;

class LeScanningReassemblerTest : public ::testing::Test {
 public:
  LeScanningReassembler reassembler_;
@@ -383,4 +387,48 @@ TEST_F(LeScanningReassemblerTest, interleaved_advertising) {
      std::vector<uint8_t>({0x2, 0x3, 0x3}));
}

TEST_F(LeScanningReassemblerTest, periodic_advertising) {
  // Test periodic advertising.
  ASSERT_FALSE(
      reassembler_
          .ProcessPeriodicAdvertisingReport(kTestSyncHandle1, DataStatus::CONTINUING, {0x1, 0x2})
          .has_value());

  auto processed_report = reassembler_.ProcessPeriodicAdvertisingReport(
      kTestSyncHandle1, DataStatus::COMPLETE, {0x3, 0x4, 0x5, 0x6});
  ASSERT_TRUE(processed_report.has_value());
  ASSERT_EQ(processed_report.value(), std::vector<uint8_t>({0x1, 0x2, 0x3, 0x4, 0x5, 0x6}));

  // Test periodic advertising with the same handle
  // to validate that the context was cleared.
  processed_report = reassembler_.ProcessPeriodicAdvertisingReport(
      kTestSyncHandle1, DataStatus::COMPLETE, {0x4, 0xa0, 0xb0, 0xc0, 0xd0});
  ASSERT_TRUE(processed_report.has_value());
  ASSERT_EQ(processed_report.value(), std::vector<uint8_t>({0x4, 0xa0, 0xb0, 0xc0, 0xd0}));
}

TEST_F(LeScanningReassemblerTest, interleaved_periodic_advertising) {
  // The reassembler must disambiguate advertising events by address,
  // address type, and SID.
  ASSERT_FALSE(
      reassembler_
          .ProcessPeriodicAdvertisingReport(kTestSyncHandle1, DataStatus::CONTINUING, {0x2, 0x0})
          .has_value());

  ASSERT_FALSE(
      reassembler_
          .ProcessPeriodicAdvertisingReport(kTestSyncHandle2, DataStatus::CONTINUING, {0x2, 0x1})
          .has_value());

  ASSERT_EQ(
      reassembler_.ProcessPeriodicAdvertisingReport(kTestSyncHandle1, DataStatus::COMPLETE, {0x0})
          .value(),
      std::vector<uint8_t>({0x2, 0x0, 0x0}));

  ASSERT_EQ(
      reassembler_.ProcessPeriodicAdvertisingReport(kTestSyncHandle2, DataStatus::COMPLETE, {0x1})
          .value(),
      std::vector<uint8_t>({0x2, 0x1, 0x1}));
}

}  // namespace bluetooth::hci