blob: 3f57930ba573018e932f4808779d8f19f6788333 [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/gap/discovery_filter.h"
#include "gtest/gtest.h"
#include "src/connectivity/bluetooth/core/bt-host/hci/low_energy_scanner.h"
namespace bt {
namespace gap {
namespace {
TEST(GAP_DiscoveryFilterTest, Flags) {
const BufferView kEmptyData;
const auto kInvalidFlagsData = CreateStaticByteBuffer(0x01, 0x01);
const auto kValidFlagsData = CreateStaticByteBuffer(0x02, 0x01, 0b101);
DiscoveryFilter filter;
// Empty filter should match everything.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b100);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b001);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
// The following filters set multiple bits. As long as one of them is set, the
// filter should match.
filter.set_flags(0b101);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b111);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b011);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b010);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
// The following filters requre that *all* bits be present in the advertising
// data.
filter.set_flags(0b101, true);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b111, true);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b011, true);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
filter.set_flags(0b010, true);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidFlagsData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kValidFlagsData, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, Connectable) {
BufferView empty;
DiscoveryFilter filter;
// Empty filter should match both.
EXPECT_TRUE(filter.MatchLowEnergyResult(empty, true, hci::kRSSIInvalid));
EXPECT_TRUE(filter.MatchLowEnergyResult(empty, false, hci::kRSSIInvalid));
// Filter connectable.
filter.set_connectable(true);
EXPECT_TRUE(filter.MatchLowEnergyResult(empty, true, hci::kRSSIInvalid));
EXPECT_FALSE(filter.MatchLowEnergyResult(empty, false, hci::kRSSIInvalid));
// Filter not connectable.
filter.set_connectable(false);
EXPECT_FALSE(filter.MatchLowEnergyResult(empty, true, hci::kRSSIInvalid));
EXPECT_TRUE(filter.MatchLowEnergyResult(empty, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, 16BitServiceUuids) {
constexpr uint16_t kUuid0 = 0x180d;
constexpr uint16_t kUuid1 = 0x1800;
const BufferView kEmptyData;
// Below, "Incomplete" refers to the "Incomplete Service UUIDs" field while
// "Complete" refers to "Complete Service UUIDs".
const auto kIncompleteEmpty = CreateStaticByteBuffer(0x01, 0x02);
const auto kIncompleteNoMatch =
CreateStaticByteBuffer(0x05, 0x02, 0x01, 0x02, 0x03, 0x04);
const auto kIncompleteMatch1 =
CreateStaticByteBuffer(0x05, 0x02, 0x01, 0x02, 0x0d, 0x18);
const auto kIncompleteMatch2 =
CreateStaticByteBuffer(0x05, 0x02, 0x00, 0x18, 0x03, 0x04);
const auto kCompleteEmpty = CreateStaticByteBuffer(0x01, 0x03);
const auto kCompleteNoMatch =
CreateStaticByteBuffer(0x05, 0x03, 0x01, 0x02, 0x03, 0x04);
const auto kCompleteMatch1 =
CreateStaticByteBuffer(0x05, 0x03, 0x01, 0x02, 0x0d, 0x18);
const auto kCompleteMatch2 =
CreateStaticByteBuffer(0x05, 0x03, 0x00, 0x18, 0x03, 0x04);
DiscoveryFilter filter;
// An empty filter should match all payloads.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_TRUE(filter.MatchLowEnergyResult(kIncompleteNoMatch, false,
hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch2, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteNoMatch, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch2, false, hci::kRSSIInvalid));
// Filter for kUuid0 and kUuid1.
filter.set_service_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)});
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kIncompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_FALSE(filter.MatchLowEnergyResult(kIncompleteNoMatch, false,
hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch2, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteNoMatch, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch2, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, 32BitServiceUuids) {
constexpr uint16_t kUuid0 = 0x180d;
constexpr uint16_t kUuid1 = 0x1800;
const BufferView kEmptyData;
// Below, "Incomplete" refers to the "Incomplete Service UUIDs" field while
// "Complete" refers to "Complete Service UUIDs".
const auto kIncompleteEmpty = CreateStaticByteBuffer(0x01, 0x04);
const auto kIncompleteNoMatch = CreateStaticByteBuffer(
0x09, 0x04, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
const auto kIncompleteMatch1 = CreateStaticByteBuffer(
0x09, 0x04, 0x01, 0x02, 0x03, 0x04, 0x0d, 0x18, 0x00, 0x00);
const auto kIncompleteMatch2 = CreateStaticByteBuffer(
0x09, 0x04, 0x00, 0x18, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04);
const auto kCompleteEmpty = CreateStaticByteBuffer(0x01, 0x05);
const auto kCompleteNoMatch = CreateStaticByteBuffer(
0x09, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08);
const auto kCompleteMatch1 = CreateStaticByteBuffer(
0x09, 0x05, 0x01, 0x02, 0x03, 0x04, 0x0d, 0x18, 0x00, 0x00);
const auto kCompleteMatch2 = CreateStaticByteBuffer(
0x09, 0x05, 0x00, 0x18, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04);
DiscoveryFilter filter;
// An empty filter should match all payloads.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_TRUE(filter.MatchLowEnergyResult(kIncompleteNoMatch, false,
hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch2, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteNoMatch, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch2, false, hci::kRSSIInvalid));
// Filter for kUuid0 and kUuid1.
filter.set_service_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)});
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kIncompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_FALSE(filter.MatchLowEnergyResult(kIncompleteNoMatch, false,
hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch2, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteNoMatch, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch2, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, 128BitServiceUuids) {
constexpr uint16_t kUuid0 = 0x180d;
constexpr uint16_t kUuid1 = 0x1800;
const BufferView kEmptyData;
// Below, "Incomplete" refers to the "Incomplete Service UUIDs" field while
// "Complete" refers to "Complete Service UUIDs".
const auto kIncompleteEmpty = CreateStaticByteBuffer(0x01, 0x06);
const auto kIncompleteNoMatch =
CreateStaticByteBuffer(0x11, 0x06,
// UUID
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F);
const auto kIncompleteMatch1 =
CreateStaticByteBuffer(0x21, 0x06,
// UUID 1
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
// UUID 2
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x0d, 0x18, 0x00, 0x00);
const auto kIncompleteMatch2 =
CreateStaticByteBuffer(0x21, 0x06,
// UUID 1
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
// UUID 2
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F);
const auto kCompleteEmpty = CreateStaticByteBuffer(0x01, 0x07);
const auto kCompleteNoMatch =
CreateStaticByteBuffer(0x11, 0x07,
// UUID
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F);
const auto kCompleteMatch1 =
CreateStaticByteBuffer(0x21, 0x07,
// UUID 1
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
// UUID 2
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x0d, 0x18, 0x00, 0x00);
const auto kCompleteMatch2 =
CreateStaticByteBuffer(0x21, 0x07,
// UUID 1
0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80,
0x00, 0x10, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00,
// UUID 2
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F);
DiscoveryFilter filter;
// An empty filter should match all payloads.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_TRUE(filter.MatchLowEnergyResult(kIncompleteNoMatch, false,
hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch2, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteNoMatch, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch2, false, hci::kRSSIInvalid));
// Filter for kUuid0 and kUuid1.
filter.set_service_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)});
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kIncompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_FALSE(filter.MatchLowEnergyResult(kIncompleteNoMatch, false,
hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kIncompleteMatch2, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteEmpty, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteNoMatch, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteMatch2, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, NameSubstring) {
const BufferView kEmptyData;
const auto kShortenedName =
CreateStaticByteBuffer(0x05, 0x08, 'T', 'e', 's', 't');
const auto kCompleteName =
CreateStaticByteBuffer(0x0E, 0x09, 'T', 'e', 's', 't', ' ', 'C', 'o', 'm',
'p', 'l', 'e', 't', 'e');
DiscoveryFilter filter;
// An empty filter should match all payloads.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kShortenedName, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteName, false, hci::kRSSIInvalid));
// Assigning an empty string for the name filter should have the same effect
// as an empty filter.
filter.set_name_substring("");
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kShortenedName, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteName, false, hci::kRSSIInvalid));
filter.set_name_substring("foo");
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kShortenedName, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kCompleteName, false, hci::kRSSIInvalid));
filter.set_name_substring("est");
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kShortenedName, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteName, false, hci::kRSSIInvalid));
filter.set_name_substring("Compl");
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kShortenedName, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kCompleteName, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, RSSI) {
constexpr int8_t kRSSIThreshold = 60;
const BufferView kEmptyData;
DiscoveryFilter filter;
filter.set_rssi(hci::kRSSIInvalid);
// |result| reports an invalid RSSI. This should fail to match even though the
// value numerically satisfies the filter.
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, true, hci::kRSSIInvalid));
filter.set_rssi(kRSSIThreshold);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, true, hci::kRSSIInvalid));
EXPECT_TRUE(filter.MatchLowEnergyResult(kEmptyData, true, kRSSIThreshold));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, true, kRSSIThreshold + 1));
// When a pathloss filter value is set and the scan result does not satisfy it
// because it didn't include the transmission power level, the filter should
// match since an RSSI value has been set which was used as a fallback.
filter.set_pathloss(5);
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, true, kRSSIThreshold + 1));
// Finally, an empty filter should always succeed.
filter.Reset();
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, true, kRSSIThreshold + 1));
}
TEST(GAP_DiscoveryFilterTest, Pathloss) {
constexpr int8_t kPathlossThreshold = 70;
constexpr int8_t kTxPower = 5;
constexpr int8_t kMatchingRSSI = -65;
constexpr int8_t kNotMatchingRSSI = -66;
constexpr int8_t kTooLargeRSSI = 71;
const BufferView kEmptyData;
const auto kDataWithTxPower = CreateStaticByteBuffer(0x02, 0x0A, kTxPower);
DiscoveryFilter filter;
filter.set_pathloss(kPathlossThreshold);
// No Tx Power and no RSSI. Filter should not match.
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, true, hci::kRSSIInvalid));
// Tx Power is reported but RSSI is unknown. Filter should not match.
EXPECT_FALSE(
filter.MatchLowEnergyResult(kDataWithTxPower, true, hci::kRSSIInvalid));
// RSSI is known but Tx Power is not reported.
EXPECT_FALSE(filter.MatchLowEnergyResult(kEmptyData, true, kMatchingRSSI));
// RSSI and Tx Power are present and pathloss is within threshold.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kDataWithTxPower, true, kMatchingRSSI));
// RSSI and Tx Power are present but RSSI is larger than Tx Power.
EXPECT_FALSE(
filter.MatchLowEnergyResult(kDataWithTxPower, true, kTooLargeRSSI));
// RSSI and Tx Power are present but pathloss is above threshold.
EXPECT_FALSE(
filter.MatchLowEnergyResult(kDataWithTxPower, true, kNotMatchingRSSI));
// Assign a RSSI filter. Even though this field alone WOULD satisfy the
// filter, the match function should not fall back to it when Tx Power is
// present and the pathloss filter is unsatisfied.
filter.set_rssi(kNotMatchingRSSI);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kDataWithTxPower, true, kNotMatchingRSSI));
EXPECT_TRUE(filter.MatchLowEnergyResult(kEmptyData, true, kNotMatchingRSSI));
// Finally, an empty filter should always succeed.
filter.Reset();
EXPECT_TRUE(
filter.MatchLowEnergyResult(kDataWithTxPower, true, kNotMatchingRSSI));
}
TEST(GAP_DiscoveryFilterTest, ManufacturerCode) {
const BufferView kEmptyData;
const auto kValidData0 = CreateStaticByteBuffer(0x03, 0xFF, 0xE0, 0x00);
const auto kValidData1 =
CreateStaticByteBuffer(0x06, 0xFF, 0xE0, 0x00, 0x01, 0x02, 0x03);
const auto kInvalidData0 = CreateStaticByteBuffer(0x02, 0xFF, 0xE0);
const auto kInvalidData1 = CreateStaticByteBuffer(0x03, 0xFF, 0x4C, 0x00);
DiscoveryFilter filter;
// Empty filter should match everything.
EXPECT_TRUE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidData0, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidData1, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kInvalidData0, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kInvalidData1, false, hci::kRSSIInvalid));
filter.set_manufacturer_code(0x00E0);
EXPECT_FALSE(
filter.MatchLowEnergyResult(kEmptyData, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidData0, false, hci::kRSSIInvalid));
EXPECT_TRUE(
filter.MatchLowEnergyResult(kValidData1, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidData0, false, hci::kRSSIInvalid));
EXPECT_FALSE(
filter.MatchLowEnergyResult(kInvalidData1, false, hci::kRSSIInvalid));
}
TEST(GAP_DiscoveryFilterTest, Combined) {
constexpr int8_t kMatchingPathlossThreshold = 70;
constexpr int8_t kNotMatchingPathlossThreshold = 69;
constexpr int8_t kTxPower = 5;
constexpr int8_t kRSSI = -65;
constexpr uint16_t kMatchingUuid = 0x180d;
constexpr uint16_t kNotMatchingUuid = 0x1800;
constexpr char kMatchingName[] = "test";
constexpr char kNotMatchingName[] = "foo";
const auto kAdvertisingData = CreateStaticByteBuffer(
// Flags
0x02, 0x01, 0x01,
// 16 Bit UUIDs
0x03, 0x02, 0x0d, 0x18,
// Complete name
0x05, 0x09, 't', 'e', 's', 't',
// Tx Power Level
0x02, 0x0A, kTxPower,
// Manufacturer specific data
0x05, 0xFF, 0xE0, 0x00, 0x01, 0x02);
DiscoveryFilter filter;
// Empty filter should match.
EXPECT_TRUE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
// Assign all fields and make them match.
filter.set_flags(0x01);
filter.set_connectable(true);
filter.set_service_uuids(std::vector<UUID>{UUID(kMatchingUuid)});
filter.set_name_substring(kMatchingName);
filter.set_pathloss(kMatchingPathlossThreshold);
filter.set_manufacturer_code(0x00E0);
EXPECT_TRUE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
// Toggle each field one by one to test that a single mismatch causes the
// filter to fail.
filter.set_flags(0x03, true);
EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
filter.set_flags(0x01);
filter.set_connectable(false);
EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
filter.set_connectable(true);
filter.set_service_uuids(std::vector<UUID>{UUID(kNotMatchingUuid)});
EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
filter.set_service_uuids(std::vector<UUID>{UUID(kMatchingUuid)});
filter.set_name_substring(kNotMatchingName);
EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
filter.set_name_substring(kMatchingName);
filter.set_pathloss(kNotMatchingPathlossThreshold);
EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
filter.set_pathloss(kMatchingPathlossThreshold);
filter.set_manufacturer_code(0x004C);
EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
filter.set_manufacturer_code(0x00E0);
EXPECT_TRUE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI));
}
TEST(GAP_DiscoveryFilterTest, GeneralDiscoveryFlags) {
const auto kLimitedDiscoverableData = CreateStaticByteBuffer(
// Flags
0x02, 0x01, 0x01);
const auto kGeneralDiscoverableData = CreateStaticByteBuffer(
// Flags
0x02, 0x01, 0x02);
const auto kNonDiscoverableData = CreateStaticByteBuffer(
// Flags (all flags are set except for discoverability).
0x02, 0x01, 0xFC);
DiscoveryFilter filter;
filter.SetGeneralDiscoveryFlags();
EXPECT_TRUE(filter.MatchLowEnergyResult(kLimitedDiscoverableData, true, 0));
EXPECT_TRUE(filter.MatchLowEnergyResult(kGeneralDiscoverableData, true, 0));
EXPECT_FALSE(filter.MatchLowEnergyResult(kNonDiscoverableData, true, 0));
}
} // namespace
} // namespace gap
} // namespace bt