blob: 75cabcd950a1df0fc90848af7d4d1a0eb820af45 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/gap/discovery_filter.h"
#include <endian.h>
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/advertising_data.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/assert.h"
#include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/log.h"
namespace bt::gap {
void DiscoveryFilter::SetGeneralDiscoveryFlags() {
set_flags(static_cast<uint8_t>(AdvFlag::kLEGeneralDiscoverableMode) |
static_cast<uint8_t>(AdvFlag::kLELimitedDiscoverableMode));
}
bool DiscoveryFilter::MatchLowEnergyResult(
const std::optional<std::reference_wrapper<const AdvertisingData>>
advertising_data,
bool connectable,
int8_t rssi) const {
// No need to check |advertising_data| for the |connectable_| filter.
if (connectable_ && *connectable_ != connectable) {
return false;
}
// If a pathloss filter is not set then apply the RSSI filter before
// checking |advertising_data|. (An RSSI value of hci_spec::kRSSIInvalid means
// that RSSI is not available, which we check for here).
bool rssi_ok = !rssi_ || (rssi != hci_spec::kRSSIInvalid && rssi >= *rssi_);
if (!pathloss_ && !rssi_ok) {
return false;
}
// Any of these filters being set requires us to have a valid
// |advertising_data| to pass.
bool needs_ad_check = flags_ || !service_uuids_.empty() ||
!service_data_uuids_.empty() ||
!name_substring_.empty() || manufacturer_code_;
if (!advertising_data.has_value() && needs_ad_check) {
return false;
}
// Pathloss is complicated because we can pass if it's set and we have no
// |advertising_data| by passing RSSI instead.
if (pathloss_) {
if (!advertising_data.has_value() ||
!advertising_data->get().tx_power().has_value()) {
// If no RSSI filter was set OR if one was set but it didn't match the
// scan result, we fail.
if (!rssi_ || !rssi_ok) {
return false;
}
// Otherwise we fall back to RSSI passing if tx_power was not set.
} else {
int8_t tx_power_lvl = *advertising_data->get().tx_power();
if (tx_power_lvl < rssi) {
bt_log(WARN,
"gap",
"reported tx-power level is less than RSSI, failed pathloss");
return false;
}
int8_t pathloss = tx_power_lvl - rssi;
if (pathloss > *pathloss_) {
return false;
}
// mark the rssi_ok since we pass based on pathloss.
rssi_ok = true;
}
}
// If we made it here without advetising_data, and there's no need to check,
// we pass if rssi passed (which also passes if RSSI filtering was not set)
if (!advertising_data.has_value() && !needs_ad_check) {
return rssi_ok;
}
BT_DEBUG_ASSERT(advertising_data.has_value());
const AdvertisingData& ad = advertising_data->get();
if (flags_) {
if (all_flags_required_ && ad.flags() != flags_) {
return false;
}
if (!ad.flags().has_value()) {
return false;
}
uint8_t matched_flags = ad.flags().value() & *flags_;
if (matched_flags == 0) {
return false;
}
}
if (!name_substring_.empty()) {
if (!ad.local_name()) {
return false;
}
// TODO(jamuraa): If this is an incomplete name should we match the first
// part?
if (ad.local_name()->name.find(name_substring_) == std::string_view::npos) {
return false;
}
}
if (manufacturer_code_) {
if (ad.manufacturer_data_ids().find(*manufacturer_code_) ==
ad.manufacturer_data_ids().end()) {
return false;
}
}
if (!service_uuids_.empty()) {
bool service_found = false;
const auto& ad_service_uuids = ad.service_uuids();
for (auto uuid : service_uuids_) {
if (ad_service_uuids.count(uuid) != 0) {
service_found = true;
break;
}
}
if (!service_found) {
return false;
}
}
if (!service_data_uuids_.empty()) {
bool service_data_found = false;
const auto& ad_data_uuids = ad.service_data_uuids();
for (auto uuid : service_data_uuids_) {
if (ad_data_uuids.count(uuid) != 0) {
service_data_found = true;
break;
}
}
if (!service_data_found) {
return false;
}
}
// We haven't filtered it out, so it matches.
return true;
}
void DiscoveryFilter::Reset() {
service_uuids_.clear();
service_data_uuids_.clear();
name_substring_.clear();
connectable_.reset();
manufacturer_code_.reset();
pathloss_.reset();
rssi_.reset();
}
} // namespace bt::gap