| // 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 <gtest/gtest.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/supplement_data.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/common/uint128.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/hci/low_energy_scanner.h" |
| #include "src/connectivity/bluetooth/core/bt-host/public/pw_bluetooth_sapphire/internal/host/testing/test_helpers.h" |
| |
| namespace bt::gap { |
| namespace { |
| |
| constexpr uint16_t kUuid0 = 0x180d; |
| |
| TEST(DiscoveryFilterTest, Flags) { |
| const StaticByteBuffer kNoFlagsData(0x02, 0x09, 'a'); |
| const StaticByteBuffer kValidFlagsData(0x02, 0x01, 0b101); |
| |
| auto no_flags_data = AdvertisingData::FromBytes(kNoFlagsData).value(); |
| auto valid_flags_data = AdvertisingData::FromBytes(kValidFlagsData).value(); |
| |
| DiscoveryFilter filter; |
| |
| // Empty filter should match everything. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b100); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b001); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::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(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b111); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b011); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b010); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| // The following filters requre that *all* bits be present in the advertising |
| // data. |
| filter.set_flags(0b101, /*require_all=*/true); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b111, /*require_all=*/true); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b011, /*require_all=*/true); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_flags(0b010, /*require_all=*/true); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| no_flags_data, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| valid_flags_data, false, hci_spec::kRSSIInvalid)); |
| } |
| |
| TEST(DiscoveryFilterTest, Connectable) { |
| DiscoveryFilter filter; |
| |
| // Empty filter should match both. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, true, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter connectable. |
| filter.set_connectable(true); |
| EXPECT_TRUE(filter.connectable()); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, true, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter not connectable. |
| filter.set_connectable(false); |
| EXPECT_TRUE(filter.connectable()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, true, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_FALSE(filter.connectable()); |
| } |
| |
| TEST(DiscoveryFilterTest, 16BitServiceUuids) { |
| constexpr uint16_t kUuid1 = 0x1800; |
| |
| // Below, "Incomplete" refers to the "Incomplete Service UUIDs" field while |
| // "Complete" refers to "Complete Service UUIDs". |
| |
| const auto kIncompleteEmpty( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x01, DataType::kIncomplete16BitServiceUuids)) |
| .value()); |
| const auto kIncompleteNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, |
| DataType::kIncomplete16BitServiceUuids, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04)) |
| .value()); |
| const auto kIncompleteMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, |
| DataType::kIncomplete16BitServiceUuids, |
| 0x01, |
| 0x02, |
| LowerBits(kUuid0), |
| UpperBits(kUuid0))) |
| .value()); |
| const auto kIncompleteMatch1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, |
| DataType::kIncomplete16BitServiceUuids, |
| LowerBits(kUuid1), |
| UpperBits(kUuid1), |
| 0x03, |
| 0x04)) |
| .value()); |
| const auto kCompleteEmpty( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x01, DataType::kComplete16BitServiceUuids)) |
| .value()); |
| const auto kCompleteNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, |
| DataType::kComplete16BitServiceUuids, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04)) |
| .value()); |
| const auto kCompleteMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, |
| DataType::kComplete16BitServiceUuids, |
| 0x01, |
| 0x02, |
| LowerBits(kUuid0), |
| UpperBits(kUuid0))) |
| .value()); |
| const auto kCompleteMatch1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, |
| DataType::kComplete16BitServiceUuids, |
| LowerBits(kUuid1), |
| UpperBits(kUuid1), |
| 0x03, |
| 0x04)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter for kUuid0 and kUuid1. |
| filter.set_service_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)}); |
| EXPECT_FALSE(filter.service_uuids().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kIncompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kIncompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.service_uuids().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, 32BitServiceUuids) { |
| constexpr uint32_t kUuid1 = 0xabcd1800; |
| |
| // Below, "Incomplete" refers to the "Incomplete Service UUIDs" field while |
| // "Complete" refers to "Complete Service UUIDs". |
| |
| const auto kIncompleteEmpty( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x01, 0x04)).value()); |
| const auto kIncompleteNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x09, |
| DataType::kIncomplete32BitServiceUuids, |
| // First UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| // Second UUID |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08)) |
| .value()); |
| const auto kIncompleteMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x09, |
| DataType::kIncomplete32BitServiceUuids, |
| // First UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| // kUuid0 |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x00, |
| 0x00)) |
| .value()); |
| const auto kIncompleteMatch1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x09, |
| DataType::kIncomplete32BitServiceUuids, |
| // kUuid1 |
| 0x00, |
| 0x18, |
| 0xcd, |
| 0xab, |
| // Second UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04)) |
| .value()); |
| const auto kCompleteEmpty( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x01, DataType::kComplete32BitServiceUuids)) |
| .value()); |
| const auto kCompleteNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x09, |
| DataType::kComplete32BitServiceUuids, |
| // First UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| // Second UUID |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08)) |
| .value()); |
| const auto kCompleteMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x09, |
| DataType::kComplete32BitServiceUuids, |
| // First UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| // kUuid0 |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x00, |
| 0x00)) |
| .value()); |
| const auto kCompleteMatch1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x09, |
| DataType::kComplete32BitServiceUuids, |
| // kUuid1 |
| 0x00, |
| 0x18, |
| 0xcd, |
| 0xab, |
| // Second UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter for kUuid0 and kUuid1. |
| filter.set_service_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)}); |
| EXPECT_FALSE(filter.service_uuids().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kIncompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kIncompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.service_uuids().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, 128BitServiceUuids) { |
| constexpr UInt128 kUuid1 = {0xDE, |
| 0xAD, |
| 0xBE, |
| 0xEF, |
| 0xDE, |
| 0xAD, |
| 0xBE, |
| 0xEF, |
| 0xDE, |
| 0xAD, |
| 0xBE, |
| 0xEF, |
| 0x00, |
| 0x18, |
| 0xcd, |
| 0xab}; |
| |
| // Below, "Incomplete" refers to the "Incomplete Service UUIDs" field while |
| // "Complete" refers to "Complete Service UUIDs". |
| |
| const auto kIncompleteEmpty( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x01, DataType::kIncomplete128BitServiceUuids)) |
| .value()); |
| const auto kIncompleteNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x11, |
| DataType::kIncomplete128BitServiceUuids, |
| |
| // UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| const auto kIncompleteMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x21, |
| DataType::kIncomplete128BitServiceUuids, |
| // First UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F, |
| |
| // kUuid0 - padded with the BT SIG Base UUID. |
| // See Core Spec v5.0, Vol 3, Part B, Section 2.5.1. |
| 0xFB, |
| 0x34, |
| 0x9B, |
| 0x5F, |
| 0x80, |
| 0x00, |
| 0x00, |
| 0x80, |
| 0x00, |
| 0x10, |
| 0x00, |
| 0x00, |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x00, |
| 0x00)) |
| .value()); |
| const auto kIncompleteMatch1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x21, |
| DataType::kIncomplete128BitServiceUuids, |
| |
| // kUuid1 |
| kUuid1[0], |
| kUuid1[1], |
| kUuid1[2], |
| kUuid1[3], |
| kUuid1[4], |
| kUuid1[5], |
| kUuid1[6], |
| kUuid1[7], |
| kUuid1[8], |
| kUuid1[9], |
| kUuid1[10], |
| kUuid1[11], |
| kUuid1[12], |
| kUuid1[13], |
| kUuid1[14], |
| kUuid1[15], |
| |
| // Second UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| const auto kCompleteEmpty( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x01, DataType::kComplete128BitServiceUuids)) |
| .value()); |
| const auto kCompleteNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x11, |
| DataType::kComplete128BitServiceUuids, |
| |
| // UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| const auto kCompleteMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x21, |
| DataType::kComplete128BitServiceUuids, |
| |
| // First UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F, |
| |
| // kUuid0 - padded with the BT SIG Base UUID. |
| // See Core Spec v5.0, Vol 3, Part B, Section 2.5.1. |
| 0xFB, |
| 0x34, |
| 0x9B, |
| 0x5F, |
| 0x80, |
| 0x00, |
| 0x00, |
| 0x80, |
| 0x00, |
| 0x10, |
| 0x00, |
| 0x00, |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x00, |
| 0x00)) |
| .value()); |
| const auto kCompleteMatch1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x21, |
| DataType::kComplete128BitServiceUuids, |
| |
| // kUuid1 |
| kUuid1[0], |
| kUuid1[1], |
| kUuid1[2], |
| kUuid1[3], |
| kUuid1[4], |
| kUuid1[5], |
| kUuid1[6], |
| kUuid1[7], |
| kUuid1[8], |
| kUuid1[9], |
| kUuid1[10], |
| kUuid1[11], |
| kUuid1[12], |
| kUuid1[13], |
| kUuid1[14], |
| kUuid1[15], |
| |
| // Second UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter for kUuid0 and kUuid1. |
| filter.set_service_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)}); |
| EXPECT_FALSE(filter.service_uuids().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kIncompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kIncompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kIncompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteEmpty, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.service_uuids().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, 16BitServiceDataUuids) { |
| constexpr uint16_t kUuid1 = 0x1800; |
| |
| const auto kNoMatch( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer( |
| 0x05, DataType::kServiceData16Bit, 0x01, 0x02, 0x03, 0x04)) |
| .value()); |
| const auto kMatch0( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x05, |
| DataType::kServiceData16Bit, |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x01, |
| 0x02)) |
| .value()); |
| const auto kMatch1( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x05, |
| DataType::kServiceData16Bit, |
| LowerBits(kUuid1), |
| UpperBits(kUuid1), |
| 0x03, |
| 0x04)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter for kUuid0 and kUuid1. |
| filter.set_service_data_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)}); |
| EXPECT_FALSE(filter.service_data_uuids().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(kNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.service_data_uuids().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, 32BitServiceDataUuids) { |
| constexpr uint32_t kUuid1 = 0xabcd1800; |
| |
| const auto kNoMatch( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x09, |
| DataType::kServiceData32Bit, |
| // Random UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| // Random UUID |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08)) |
| .value()); |
| const auto kMatch0( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x09, |
| DataType::kServiceData32Bit, |
| // kUuid0 |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x00, |
| 0x00, |
| // Data |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04)) |
| .value()); |
| const auto kMatch1( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x09, |
| DataType::kServiceData32Bit, |
| // kUuid1 |
| 0x00, |
| 0x18, |
| 0xcd, |
| 0xab, |
| // Random UUID |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter for kUuid0 and kUuid1. |
| filter.set_service_data_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)}); |
| EXPECT_FALSE(filter.service_data_uuids().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(kNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.service_data_uuids().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, 128BitServiceDataUuids) { |
| constexpr UInt128 kUuid1 = {0xDE, |
| 0xAD, |
| 0xBE, |
| 0xEF, |
| 0xDE, |
| 0xAD, |
| 0xBE, |
| 0xEF, |
| 0xDE, |
| 0xAD, |
| 0xBE, |
| 0xEF, |
| 0x00, |
| 0x18, |
| 0xcd, |
| 0xab}; |
| |
| const auto kNoMatch( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x11, |
| DataType::kServiceData128Bit, |
| |
| // Random UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| const auto kMatch0( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x21, |
| DataType::kServiceData128Bit, |
| // kUuid0 - padded with the BT SIG Base UUID. See |
| // Core Spec v5.0, Vol 3, Part B, Section 2.5.1. |
| 0xFB, |
| 0x34, |
| 0x9B, |
| 0x5F, |
| 0x80, |
| 0x00, |
| 0x00, |
| 0x80, |
| 0x00, |
| 0x10, |
| 0x00, |
| 0x00, |
| LowerBits(kUuid0), |
| UpperBits(kUuid0), |
| 0x00, |
| 0x00, |
| // Random Data |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| const auto kMatch1( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x21, |
| DataType::kServiceData128Bit, |
| |
| // kUuid1 |
| kUuid1[0], |
| kUuid1[1], |
| kUuid1[2], |
| kUuid1[3], |
| kUuid1[4], |
| kUuid1[5], |
| kUuid1[6], |
| kUuid1[7], |
| kUuid1[8], |
| kUuid1[9], |
| kUuid1[10], |
| kUuid1[11], |
| kUuid1[12], |
| kUuid1[13], |
| kUuid1[14], |
| kUuid1[15], |
| |
| // Random UUID |
| 0x00, |
| 0x01, |
| 0x02, |
| 0x03, |
| 0x04, |
| 0x05, |
| 0x06, |
| 0x07, |
| 0x08, |
| 0x09, |
| 0x0A, |
| 0x0B, |
| 0x0C, |
| 0x0D, |
| 0x0E, |
| 0x0F)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| // Filter for kUuid0 and kUuid1. |
| filter.set_service_data_uuids(std::vector<UUID>{UUID(kUuid0), UUID(kUuid1)}); |
| EXPECT_FALSE(filter.service_data_uuids().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(kNoMatch, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kMatch1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.service_data_uuids().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, NameSubstring) { |
| const auto kShortenedName( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x05, 0x08, 'T', 'e', 's', 't')) |
| .value()); |
| const auto kCompleteName(AdvertisingData::FromBytes(StaticByteBuffer(0x0E, |
| 0x09, |
| 'T', |
| 'e', |
| 's', |
| 't', |
| ' ', |
| 'C', |
| 'o', |
| 'm', |
| 'p', |
| 'l', |
| 'e', |
| 't', |
| 'e')) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // An empty filter should match all payloads. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kShortenedName, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteName, false, hci_spec::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(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kShortenedName, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteName, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_name_substring("foo"); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kShortenedName, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kCompleteName, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_name_substring("est"); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kShortenedName, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteName, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_name_substring("Compl"); |
| EXPECT_FALSE(filter.name_substring().empty()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kShortenedName, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kCompleteName, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_TRUE(filter.name_substring().empty()); |
| } |
| |
| TEST(DiscoveryFilterTest, RSSI) { |
| constexpr int8_t kRSSIThreshold = 60; |
| DiscoveryFilter filter; |
| filter.set_rssi(hci_spec::kRSSIInvalid); |
| |
| // |result| reports an invalid RSSI. This should fail to match even though the |
| // value numerically satisfies the filter. |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, true, hci_spec::kRSSIInvalid)); |
| |
| filter.set_rssi(kRSSIThreshold); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, true, hci_spec::kRSSIInvalid)); |
| |
| EXPECT_TRUE(filter.MatchLowEnergyResult(std::nullopt, true, kRSSIThreshold)); |
| |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, 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(std::nullopt, true, kRSSIThreshold + 1)); |
| |
| // Finally, an empty filter should always succeed. |
| filter.Reset(); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, true, kRSSIThreshold + 1)); |
| } |
| |
| TEST(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 auto kDataWithTxPower( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x02, 0x0A, kTxPower)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| filter.set_pathloss(kPathlossThreshold); |
| |
| // No Tx Power and no RSSI. Filter should not match. |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, true, hci_spec::kRSSIInvalid)); |
| |
| // Tx Power is reported but RSSI is unknown. Filter should not match. |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kDataWithTxPower, true, hci_spec::kRSSIInvalid)); |
| |
| // RSSI is known but Tx Power is not reported. |
| EXPECT_FALSE(filter.MatchLowEnergyResult(std::nullopt, 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_TRUE(filter.pathloss()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(kDataWithTxPower, true, kNotMatchingRSSI)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, true, kNotMatchingRSSI)); |
| |
| // Finally, an empty filter should always succeed. |
| filter.Reset(); |
| EXPECT_FALSE(filter.pathloss()); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kDataWithTxPower, true, kNotMatchingRSSI)); |
| } |
| |
| TEST(DiscoveryFilterTest, ManufacturerCode) { |
| const auto kValidData0( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x03, 0xFF, 0xE0, 0x00)) |
| .value()); |
| const auto kValidData1( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer(0x06, 0xFF, 0xE0, 0x00, 0x01, 0x02, 0x03)) |
| .value()); |
| const auto kInvalidData1( |
| AdvertisingData::FromBytes(StaticByteBuffer(0x03, 0xFF, 0x4C, 0x00)) |
| .value()); |
| |
| DiscoveryFilter filter; |
| |
| // Empty filter should match everything. |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kValidData0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kValidData1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE(filter.MatchLowEnergyResult( |
| kInvalidData1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.set_manufacturer_code(0x00E0); |
| EXPECT_TRUE(filter.manufacturer_code()); |
| EXPECT_FALSE( |
| filter.MatchLowEnergyResult(std::nullopt, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kValidData0, false, hci_spec::kRSSIInvalid)); |
| EXPECT_TRUE( |
| filter.MatchLowEnergyResult(kValidData1, false, hci_spec::kRSSIInvalid)); |
| EXPECT_FALSE(filter.MatchLowEnergyResult( |
| kInvalidData1, false, hci_spec::kRSSIInvalid)); |
| |
| filter.Reset(); |
| EXPECT_FALSE(filter.manufacturer_code()); |
| } |
| |
| TEST(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 uint16_t kMatchingServiceDataUuid = 0x1234; |
| constexpr uint16_t kNotMatchingServiceDataUuid = 0x5678; |
| |
| constexpr char kMatchingName[] = "test"; |
| constexpr char kNotMatchingName[] = "foo"; |
| |
| const auto kAdvertisingData( |
| AdvertisingData::FromBytes(StaticByteBuffer( |
| // Flags |
| 0x02, |
| 0x01, |
| 0x01, |
| |
| // 16 Bit Service UUIDs |
| 0x03, |
| 0x02, |
| 0x0d, |
| 0x18, |
| |
| // 16 Bit Service Data UUIDs |
| 0x03, |
| DataType::kServiceData16Bit, |
| 0x34, |
| 0x12, |
| |
| // Complete name |
| 0x05, |
| 0x09, |
| 't', |
| 'e', |
| 's', |
| 't', |
| |
| // Tx Power Level |
| 0x02, |
| 0x0A, |
| kTxPower, |
| |
| // Manufacturer specific data |
| 0x05, |
| 0xFF, |
| 0xE0, |
| 0x00, |
| 0x01, |
| 0x02)) |
| .value()); |
| |
| 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_service_data_uuids( |
| std::vector<UUID>{UUID(kMatchingServiceDataUuid)}); |
| 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, /*require_all=*/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_service_data_uuids( |
| std::vector<UUID>{UUID(kNotMatchingServiceDataUuid)}); |
| EXPECT_FALSE(filter.MatchLowEnergyResult(kAdvertisingData, true, kRSSI)); |
| filter.set_service_data_uuids( |
| std::vector<UUID>{UUID(kMatchingServiceDataUuid)}); |
| |
| 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(DiscoveryFilterTest, GeneralDiscoveryFlags) { |
| const auto kLimitedDiscoverableData( |
| AdvertisingData::FromBytes(StaticByteBuffer( |
| // Flags |
| 0x02, |
| 0x01, |
| 0x01)) |
| .value()); |
| const auto kGeneralDiscoverableData( |
| AdvertisingData::FromBytes(StaticByteBuffer( |
| // Flags |
| 0x02, |
| 0x01, |
| 0x02)) |
| .value()); |
| const auto kNonDiscoverableData( |
| AdvertisingData::FromBytes( |
| StaticByteBuffer( |
| // Flags (all flags are set except for discoverability). |
| 0x02, |
| 0x01, |
| 0xFC)) |
| .value()); |
| |
| 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 bt::gap |