constexpr uint16_t kDefaultLeScanWindow = 4800;
constexpr uint16_t kDefaultLeScanInterval = 4800;
+constexpr uint8_t kScannableBit = 1;
+constexpr uint8_t kDirectedBit = 2;
+constexpr uint8_t kScanResponseBit = 3;
+constexpr uint8_t kLegacyBit = 4;
+constexpr uint8_t kDataStatusBits = 5;
+
const ModuleFactory LeScanningManager::Factory = ModuleFactory([]() { return new LeScanningManager(); });
enum class ScanApiType {
bool in_use;
};
+class AdvertisingCache {
+ public:
+ const std::vector<GapData>& Set(const AddressWithType& address_with_type, std::vector<GapData> data) {
+ auto it = Find(address_with_type);
+ if (it != items.end()) {
+ it->data = std::move(data);
+ }
+
+ if (items.size() > cache_max) {
+ items.pop_back();
+ }
+
+ items.emplace_front(address_with_type, std::move(data));
+ return items.front().data;
+ }
+
+ bool Exist(const AddressWithType& address_with_type) {
+ auto it = Find(address_with_type);
+ if (it == items.end()) {
+ return false;
+ }
+ return true;
+ }
+
+ const std::vector<GapData>& Append(const AddressWithType& address_with_type, std::vector<GapData> data) {
+ auto it = Find(address_with_type);
+ if (it != items.end()) {
+ it->data.insert(it->data.end(), data.begin(), data.end());
+ return it->data;
+ }
+
+ if (items.size() > cache_max) {
+ items.pop_back();
+ }
+
+ items.emplace_front(address_with_type, std::move(data));
+ return items.front().data;
+ }
+
+ /* Clear data for device |addr_type, addr| */
+ void Clear(AddressWithType address_with_type) {
+ auto it = Find(address_with_type);
+ if (it != items.end()) {
+ items.erase(it);
+ }
+ }
+
+ void ClearAll() {
+ items.clear();
+ }
+
+ struct Item {
+ AddressWithType address_with_type;
+ std::vector<GapData> data;
+
+ Item(const AddressWithType& address_with_type, std::vector<GapData> data)
+ : address_with_type(address_with_type), data(data) {}
+ };
+
+ std::list<Item>::iterator Find(const AddressWithType& address_with_type) {
+ for (auto it = items.begin(); it != items.end(); it++) {
+ if (it->address_with_type == address_with_type) {
+ return it;
+ }
+ }
+ return items.end();
+ }
+
+ /* we keep maximum 7 devices in the cache */
+ const size_t cache_max = 1000;
+ std::list<Item> items;
+};
+
struct LeScanningManager::impl : public bluetooth::hci::LeAddressManagerCallback {
impl(Module* module) : module_(module), le_scanning_interface_(nullptr) {}
return;
}
- // TODO: handle AdvertisingCache for scan response
for (LeAdvertisingReport report : reports) {
uint16_t extended_event_type = 0;
switch (report.event_type_) {
return;
}
- scanning_callbacks_->OnScanResult(
- (uint16_t)report.event_type_,
+ process_advertising_package_content(
+ extended_event_type,
(uint8_t)report.address_type_,
report.address_,
(uint8_t)PrimaryPhyType::LE_1M,
return;
}
- // TODO: handle AdvertisingCache for scan response
for (LeExtendedAdvertisingReport report : reports) {
- uint16_t event_type = report.connectable_ | (report.scannable_ << 1) | (report.directed_ << 2) |
- (report.scan_response_ << 3) | (report.legacy_ << 4) | ((uint16_t)report.data_status_ << 5);
- scanning_callbacks_->OnScanResult(
+ uint16_t event_type = report.connectable_ | (report.scannable_ << kScannableBit) |
+ (report.directed_ << kDirectedBit) | (report.scan_response_ << kScanResponseBit) |
+ (report.legacy_ << kLegacyBit) | ((uint16_t)report.data_status_ << kDataStatusBits);
+ process_advertising_package_content(
event_type,
(uint8_t)report.address_type_,
report.address_,
}
}
+ void process_advertising_package_content(
+ uint16_t event_type,
+ uint8_t address_type,
+ Address address,
+ uint8_t primary_phy,
+ uint8_t secondary_phy,
+ uint8_t advertising_sid,
+ int8_t tx_power,
+ int8_t rssi,
+ uint16_t periodic_advertising_interval,
+ std::vector<GapData> advertising_data) {
+ bool is_scannable = event_type & (1 << kScannableBit);
+ bool is_scan_response = event_type & (1 << kScanResponseBit);
+ bool is_legacy = event_type & (1 << kLegacyBit);
+ // TODO handle anonymous advertisement (0xFF)
+ AddressWithType address_with_type(address, (AddressType)address_type);
+
+ if (is_legacy && is_scan_response && !advertising_cache_.Exist(address_with_type)) {
+ return;
+ }
+
+ bool is_start = is_legacy && is_scannable && !is_scan_response;
+
+ std::vector<GapData> const& adv_data = is_start ? advertising_cache_.Set(address_with_type, advertising_data)
+ : advertising_cache_.Append(address_with_type, advertising_data);
+
+ // TODO: handle data status
+
+ if (is_scannable && !is_scan_response) {
+ // Waiting for scan response
+ return;
+ }
+
+ scanning_callbacks_->OnScanResult(
+ event_type,
+ address_type,
+ address,
+ primary_phy,
+ secondary_phy,
+ advertising_sid,
+ tx_power,
+ rssi,
+ periodic_advertising_interval,
+ adv_data);
+
+ advertising_cache_.Clear(address_with_type);
+ }
+
void configure_scan() {
std::vector<PhyScanParameters> parameter_vector;
PhyScanParameters phy_scan_parameters;
bool is_scanning_ = false;
bool scan_on_resume_ = false;
bool paused_ = false;
+ AdvertisingCache advertising_cache_;
uint32_t interval_ms_{1000};
uint16_t window_ms_{1000};
test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
LeAdvertisingReport report{};
- report.event_type_ = AdvertisingEventType::ADV_IND;
+ report.event_type_ = AdvertisingEventType::ADV_DIRECT_IND;
report.address_type_ = AddressType::PUBLIC_DEVICE_ADDRESS;
Address::FromString("12:34:56:78:9a:bc", report.address_);
std::vector<GapData> gap_data{};
test_hci_layer_->IncomingEvent(LeSetScanEnableCompleteBuilder::Create(uint8_t{1}, ErrorCode::SUCCESS));
LeAdvertisingReport report{};
- report.event_type_ = AdvertisingEventType::ADV_IND;
+ report.event_type_ = AdvertisingEventType::ADV_DIRECT_IND;
report.address_type_ = AddressType::PUBLIC_DEVICE_ADDRESS;
Address::FromString("12:34:56:78:9a:bc", report.address_);
std::vector<GapData> gap_data{};
LeExtendedAdvertisingReport report{};
report.connectable_ = 1;
- report.scannable_ = 1;
+ report.scannable_ = 0;
report.address_type_ = DirectAdvertisingAddressType::PUBLIC_DEVICE_ADDRESS;
Address::FromString("12:34:56:78:9a:bc", report.address_);
std::vector<GapData> gap_data{};