// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "connections/implementation/mediums/ble/discovered_peripheral_tracker.h" #include #include #include #include #include #include #include #include "gmock/gmock.h" #include "protobuf-matchers/protocol-buffer-matchers.h" #include "gtest/gtest.h" #include "absl/base/thread_annotations.h" #include "absl/memory/memory.h" #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "connections/implementation/mediums/advertisements/dct_advertisement.h" #include "connections/implementation/mediums/ble/advertisement_read_result.h" #include "connections/implementation/mediums/ble/ble_advertisement.h" #include "connections/implementation/mediums/ble/ble_advertisement_header.h" #include "connections/implementation/mediums/ble/ble_utils.h" #include "connections/implementation/mediums/ble/bloom_filter.h" #include "connections/implementation/mediums/ble/discovered_peripheral_callback.h" #include "connections/implementation/mediums/ble/instant_on_lost_advertisement.h" #include "connections/implementation/mediums/utils.h" #include "connections/implementation/pcp.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/ble.h" #include "internal/platform/bluetooth_adapter.h" #include "internal/platform/byte_array.h" #include "internal/platform/count_down_latch.h" #include "internal/platform/feature_flags.h" #include "internal/platform/implementation/ble.h" #include "internal/platform/mac_address.h" #include "internal/platform/medium_environment.h" #include "internal/platform/mutex.h" #include "internal/platform/mutex_lock.h" #include "internal/platform/uuid.h" namespace nearby { namespace connections { namespace mediums { namespace { using ::testing::UnorderedElementsAre; constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); constexpr absl::string_view kFastAdvertisementServiceUuid = "FE2C"; constexpr absl::string_view kServiceIdA = "A"; constexpr absl::string_view kServiceIdB = "B"; constexpr absl::string_view kData = "\x04\x02\x00"; constexpr absl::string_view kData2 = "\x07\x00\x07"; constexpr absl::string_view kDeviceToken = "\x04\x20"; constexpr absl::string_view kDeviceName = "device"; constexpr absl::Duration kDefaultGattFetchDelay = absl::Milliseconds(200); // Delay to allow background threads to complete during teardown. // This helps prevent use-after-free errors on resources like FakeClock // used by background tasks (e.g., in PendingJobRegistry). constexpr absl::Duration kTeardownDelay = absl::Milliseconds(400); ByteArray CreateFastBleAdvertisement(const ByteArray& data, const ByteArray& device_token) { return ByteArray(BleAdvertisement( BleAdvertisement::Version::kV2, BleAdvertisement::SocketVersion::kV2, /*service_id_hash=*/ByteArray{}, data, device_token, BleAdvertisementHeader::kDefaultPsmValue)); } ByteArray CreateBleAdvertisement(const std::string& service_id, const ByteArray& data, const ByteArray& device_token) { return ByteArray(BleAdvertisement( BleAdvertisement::Version::kV2, BleAdvertisement::SocketVersion::kV2, bleutils::GenerateServiceIdHash(service_id), data, device_token, BleAdvertisementHeader::kDefaultPsmValue)); } // Legacy advertisement is not supported any more but we fake the legacy one // by setting version and socket version as kV1 and using legacy hash function. ByteArray CreateLegacyBleAdvertisement(const std::string& service_id, const ByteArray& data, const ByteArray& device_token) { return ByteArray(BleAdvertisement( BleAdvertisement::Version::kV1, BleAdvertisement::SocketVersion::kV1, bleutils::GenerateServiceIdHash(service_id, BleAdvertisement::Version::kV1), data, device_token, BleAdvertisementHeader::kDefaultPsmValue)); } ByteArray CreateDctAdvertisement(const std::string& service_id, const std::string& device_name) { auto dct_advertisement = advertisements::ble::DctAdvertisement::Create( service_id, device_name, 0xf100, 0x01); return ByteArray(dct_advertisement->ToData()); } BleAdvertisementHeader CreateFastBleAdvertisementHeader( const ByteArray& advertisement_bytes) { BloomFilter bloom_filter( std::make_unique>()); return BleAdvertisementHeader( BleAdvertisementHeader::Version::kV2, /*extended_advertisement=*/false, /*num_slots=*/1, ByteArray(bloom_filter), bleutils::GenerateAdvertisementHash(advertisement_bytes), /*psm=*/BleAdvertisementHeader::kDefaultPsmValue); } ByteArray CreateBleAdvertisementHeader(const ByteArray& advertisement_hash, int psm, std::vector& service_ids) { BloomFilter service_id_bloom_filter( std::make_unique>()); for (const std::string& service_id : service_ids) { service_id_bloom_filter.Add(service_id); } return ByteArray(BleAdvertisementHeader(BleAdvertisementHeader::Version::kV2, /*extended_advertisement=*/false, /*num_slots=*/service_ids.size(), ByteArray(service_id_bloom_filter), advertisement_hash, psm)); } ByteArray CreateBleAdvertisementHeader(const ByteArray& advertisement_hash, std::vector& service_ids) { return CreateBleAdvertisementHeader(advertisement_hash, BleAdvertisementHeader::kDefaultPsmValue, service_ids); } ByteArray CreateExtendedBleAdvertisementHeader( const ByteArray& advertisement_hash, std::vector& service_ids) { BloomFilter service_id_bloom_filter( std::make_unique>()); for (const std::string& service_id : service_ids) { service_id_bloom_filter.Add(service_id); } return ByteArray(BleAdvertisementHeader( BleAdvertisementHeader::Version::kV2, /*extended_advertisement=*/true, /*num_slots=*/service_ids.size(), ByteArray(service_id_bloom_filter), advertisement_hash, BleAdvertisementHeader::kDefaultPsmValue)); } ByteArray GenerateRandomAdvertisementHash() { ByteArray random_advertisement_hash = Utils::GenerateRandomBytes( BleAdvertisementHeader::kAdvertisementHashByteLength); return random_advertisement_hash; } class MockDiscoveredPeripheralCallback : public DiscoveredPeripheralCallback { public: MOCK_METHOD(void, OnPeripheralDiscovered, (BlePeripheral, const std::string&, const ByteArray&, bool), ()); MOCK_METHOD(void, OnPeripheralLost, (BlePeripheral, const std::string&, const ByteArray&, bool), ()); MOCK_METHOD(void, OnInstantLost, (BlePeripheral, const std::string&, const ByteArray&, bool), ()); MOCK_METHOD(void, OnLegacyDeviceDiscovered, (), ()); }; } // namespace class DiscoveredPeripheralTrackerTest : public testing::TestWithParam< std::tuple> { public: void SetUp() override { bool is_extended_advertisement_available = std::get<0>(GetParam()); EnvironmentConfig config{ .webrtc_enabled = false, .use_simulated_clock = true, }; MediumEnvironment::Instance().Start(config); discovered_peripheral_tracker_ = absl::WrapUnique( new DiscoveredPeripheralTracker(is_extended_advertisement_available, /*start_fetch_executor=*/false)); // Do not start the fetch executor since we will manually run the GATT // fetch loop in the test. adapter_peripheral_ = std::make_unique(); adapter_central_ = std::make_unique(); ble_peripheral_ = std::make_unique(*adapter_peripheral_); ble_central_ = std::make_unique(*adapter_central_); } void TearDown() override { discovered_peripheral_tracker_.reset(); // Add a small delay to allow background threads to complete. absl::SleepFor(kTeardownDelay); MediumEnvironment::Instance().Stop(); NearbyFlags::GetInstance().ResetOverridedValues(); } BlePeripheral CreateBlePeripheral() { return BlePeripheral(*ble_central_, adapter_peripheral_->GetAddress().address()); } void SetupMultipleAdvertisementsState( const BleAdvertisementHeader& header, const BleAdvertisement& advertisement_1, const BleAdvertisement& advertisement_2) { MutexLock lock(&discovered_peripheral_tracker_->mutex_); ByteArray advertisement_bytes_1 = advertisement_1.ByteArrayWithExtraField(); ByteArray advertisement_bytes_2 = advertisement_2.ByteArrayWithExtraField(); std::vector gatt_advertisement_bytes_list = { &advertisement_bytes_1, &advertisement_bytes_2}; discovered_peripheral_tracker_->HandleRawGattAdvertisements( CreateBlePeripheral(), header, gatt_advertisement_bytes_list, /*service_uuid=*/{}); } void RegisterServiceIdCallback(const std::string& service_id, CountDownLatch& lost_latch) { discovered_peripheral_tracker_->StartTracking( service_id, /*include_dct_advertisement=*/false, Pcp::kP2pPointToPoint, { .instant_lost_cb = [&lost_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch.CountDown(); }, }, /*fast_advertisement_service_uuid=*/{}); } bool CallHandleOnLostAdvertisementLocked( const api::ble::BleAdvertisementData& advertisement_data) { MutexLock lock(&discovered_peripheral_tracker_->mutex_); return discovered_peripheral_tracker_->HandleOnLostAdvertisementLocked( advertisement_data); } // Simulates to see a fast advertisement. void FindFastAdvertisement( const api::ble::BleAdvertisementData& advertisement_data, const std::vector& advertisement_bytes_list, CountDownLatch& fetch_latch) { BlePeripheral peripheral = CreateBlePeripheral(); discovered_peripheral_tracker_->ProcessFoundBleAdvertisement( peripheral, advertisement_data, GetAdvertisementFetcher(fetch_latch, advertisement_bytes_list)); } // Simulates to see an extended advertisement, which don't need to fetch // advertisement from GATT. void FindExtendedAdvertisement( const api::ble::BleAdvertisementData& advertisement_data, CountDownLatch& fetch_latch) { BlePeripheral peripheral = CreateBlePeripheral(); DiscoveredPeripheralTracker::AdvertisementFetcher placeholder_fetcher = [](BlePeripheral, int, int, const std::vector&, mediums::AdvertisementReadResult&) {}; discovered_peripheral_tracker_->ProcessFoundBleAdvertisement( peripheral, advertisement_data, std::move(placeholder_fetcher)); fetch_latch.CountDown(); } // Simulates to see a regular advertisement. void FindAdvertisement( const api::ble::BleAdvertisementData& advertisement_data, const std::vector& advertisement_bytes_list, CountDownLatch& fetch_latch) { BlePeripheral peripheral = CreateBlePeripheral(); discovered_peripheral_tracker_->ProcessFoundBleAdvertisement( peripheral, advertisement_data, GetAdvertisementFetcher(fetch_latch, advertisement_bytes_list)); } void FindAdvertisementWithDelay( const api::ble::BleAdvertisementData& advertisement_data, const std::vector& advertisement_bytes_list, CountDownLatch& fetch_latch, absl::Duration delay) { BlePeripheral peripheral = CreateBlePeripheral(); discovered_peripheral_tracker_->ProcessFoundBleAdvertisement( peripheral, advertisement_data, GetAdvertisementFetcherWithDelay(fetch_latch, advertisement_bytes_list, delay)); } int GetFetchAdvertisementCallbackCount() const { MutexLock lock(&mutex_); return fetch_count_; } protected: // A stub Advertisement fetcher. DiscoveredPeripheralTracker::AdvertisementFetcher GetAdvertisementFetcher( CountDownLatch& fetch_latch, const std::vector& advertisement_bytes_list) { return [this, &fetch_latch, advertisement_bytes_list]( BlePeripheral peripheral, int num_slots, int psm, const std::vector& interesting_service_ids, mediums::AdvertisementReadResult& advertisement_read_result) { MutexLock lock(&mutex_); fetch_count_++; int slot = 0; for (const auto& advertisement_bytes : advertisement_bytes_list) { advertisement_read_result.AddAdvertisement(slot++, advertisement_bytes); } advertisement_read_result.RecordLastReadStatus( /*is_success=*/true); fetch_latch.CountDown(); }; } DiscoveredPeripheralTracker::AdvertisementFetcher GetAdvertisementFetcherWithDelay( CountDownLatch& fetch_latch, const std::vector& advertisement_bytes_list, absl::Duration delay) { return [this, &fetch_latch, advertisement_bytes_list, delay]( BlePeripheral peripheral, int num_slots, int psm, const std::vector& interesting_service_ids, mediums::AdvertisementReadResult& advertisement_read_result) { MutexLock lock(&mutex_); fetch_count_++; int slot = 0; // In real environment, the GATT fetch may run about 3-5 seconds. absl::SleepFor(delay); for (const auto& advertisement_bytes : advertisement_bytes_list) { advertisement_read_result.AddAdvertisement(slot++, advertisement_bytes); } advertisement_read_result.RecordLastReadStatus( /*is_success=*/true); fetch_latch.CountDown(); }; } std::unique_ptr adapter_peripheral_; std::unique_ptr adapter_central_; std::unique_ptr ble_peripheral_; std::unique_ptr ble_central_; mutable Mutex mutex_; int fetch_count_ ABSL_GUARDED_BY(mutex_) = 0; std::unique_ptr discovered_peripheral_tracker_; }; TEST_P(DiscoveredPeripheralTrackerTest, FoundFastAdvertisementPeripheralDiscovered) { ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {}, fetch_latch); // We should receive a client callback of a peripheral discovery without a // GATT read. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, DctAdvertisementPeripheralDiscovered) { ByteArray dct_advertisement_bytes = CreateDctAdvertisement( std::string(kServiceIdA), std::string(kDeviceName)); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), true, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; advertisement_data.service_data.insert( {bleutils::kDctServiceUuid, dct_advertisement_bytes}); FindAdvertisement(advertisement_data, {}, fetch_latch); // We should receive a client callback of a peripheral discovery without a // GATT read. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, ReportFoundLegacyDeviceWhenFoundBleAdvertisementPeripheralDiscovered) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch legacy_found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, {.peripheral_discovered_cb = [&found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, .legacy_device_discovered_cb = [&legacy_found_latch]() { legacy_found_latch.CountDown(); }}, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(legacy_found_latch.Await(kWaitDuration).result()); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, CanStartMultipleTrackingWithSameServiceId) { ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(3); CountDownLatch fetch_latch(3); std::atomic callback_times = 0; // 1st tracking. discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&callback_times, &found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { callback_times++; EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {}, fetch_latch); // 2nd tracking. discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&callback_times, &found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { callback_times++; EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); FindFastAdvertisement(advertisement_data, {}, fetch_latch); // 3rd tracking. discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&callback_times, &found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { callback_times++; EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); FindFastAdvertisement(advertisement_data, {}, fetch_latch); } TEST_P(DiscoveredPeripheralTrackerTest, FoundFastAdvertisementDuplicateAdvertisements) { ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(3); CountDownLatch fetch_latch(3); std::atomic callback_times = 0; discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&callback_times, &found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { callback_times++; EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {}, fetch_latch); FindFastAdvertisement(advertisement_data, {}, fetch_latch); FindFastAdvertisement(advertisement_data, {}, fetch_latch); // We should only receive ONE client callback of a peripheral discovery with // ZERO GATT reads. fetch_latch.Await(kWaitDuration * 3); EXPECT_EQ(callback_times, 1); // The count down won't be finished since the callback only been called once. // Let it time out. EXPECT_FALSE(found_latch.Await(3 * kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, FoundFastAdvertisementUntrackedFastAdvertisementServiceUuid) { ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); // Start tracking a service ID and then process a discovery containing a valid // fast advertisement, but under a different service UUID. discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, }, Uuid("FE3C")); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {}, fetch_latch); // We should get no new discoveries. fetch_latch.Await(kWaitDuration); EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, FoundFastAdvertisementAndGattAdvertisementSimultaneously) { std::vector service_ids = {std::string(kServiceIdB)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdB), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch_a(1); CountDownLatch found_latch_b(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch_a](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch_a.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdB), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch_b](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch_b.CountDown(); }, }, {}); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive two separate peripheral discoveries. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch_a.Await(kWaitDuration).result()); EXPECT_TRUE(found_latch_b.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, FoundBleAdvertisementPeripheralDiscovered) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, FoundBleAdvertisementLegacyPeripheralDiscovered) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray legacy_advertisement_bytes = CreateLegacyBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {legacy_advertisement_bytes}, fetch_latch); // We should not receive a client callback with the Version/SocketVersion kV1. fetch_latch.Await(kWaitDuration); EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, FoundBleAdvertisementFavorLatestPeripheral) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData2)), ByteArray(std::string(kDeviceToken))); ByteArray legacy_advertisement_bytes = CreateLegacyBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); std::atomic callback_times = 0; discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&callback_times, &found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { callback_times++; EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData2))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes, legacy_advertisement_bytes}, fetch_latch); // We should only receive one callback with data from the V2 GATT // advertisement. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(callback_times, 1); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, FoundBleAdvertisementDuplicateAdvertisements) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(3); CountDownLatch fetch_latch(3); std::atomic callback_times = 0; discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&callback_times, &found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { callback_times++; EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should only receive ONE client callback of a peripheral discovery. And // we should also do only ONE GATT read. fetch_latch.Await(kWaitDuration); EXPECT_EQ(callback_times, 1); // The count down won't be finished since the callback only been called once. // Let it time out. EXPECT_FALSE(found_latch.Await(3 * kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, FoundBleAdvertisementUntrackedServiceId) { std::vector service_ids = {std::string(kServiceIdA), std::string(kServiceIdB)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdB), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should get no new discoveries. fetch_latch.Await(kWaitDuration); EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, LostPeripheralForFastAdvertisementLost) { std::vector service_ids = {}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch lost_latch(2); CountDownLatch fetch_latch(1); std::atomic lost_callback_times = 0; discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, .peripheral_lost_cb = [&lost_latch, &lost_callback_times]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_callback_times++; lost_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {fast_advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); // Then, go through two cycles of onLost. The first cycle should include the // recently discovered peripheral in its 'found' pool. The second one should // trigger the onLost callback. discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); // We should receive a client callback of a lost peripheral. EXPECT_FALSE(lost_latch.Await(kWaitDuration).result()); EXPECT_EQ(lost_callback_times, 1); } TEST_P(DiscoveredPeripheralTrackerTest, FoundFastAdvertisementAlmostLostPeripheral) { std::vector service_ids = {}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, .peripheral_lost_cb = [&lost_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } // Start tracking a service ID and then process a loop of discoveries and // onLost alarms. for (int i = 0; i < 20; i++) { FindFastAdvertisement(advertisement_data, {fast_advertisement_bytes}, fetch_latch); discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); } // We should only receive ONE client callback of a peripheral discovery, ZERO // GATT reads, and no onLost calls. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); EXPECT_FALSE(lost_latch.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, LostPeripheralForAdvertisementLost) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, .peripheral_lost_cb = [&lost_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); // Then, go through two cycles of onLost. The first cycle should include the // recently discovered peripheral in its 'found' pool. The second one should // trigger the onLost callback. discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); // We should receive a client callback of a lost peripheral EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, LostPeripheralForFastAndGattAdvertisementLost) { std::vector service_ids = {std::string(kServiceIdB)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdB), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch_a(1); CountDownLatch found_latch_b(1); CountDownLatch lost_latch_a(1); CountDownLatch lost_latch_b(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch_a](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch_a.CountDown(); }, .peripheral_lost_cb = [&lost_latch_a]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch_a.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdB), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch_b](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch_b.CountDown(); }, .peripheral_lost_cb = [&lost_latch_b]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch_b.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive two separate peripheral discoveries. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch_a.Await(kWaitDuration).result()); EXPECT_TRUE(found_latch_b.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); // Then, go through two cycles of onLost. The first cycle should include the // recently discovered peripheral in its 'found' pool. The second one should // trigger the onLost callback. discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); // We should receive two client callbacks of a lost peripheral from each // service ID. EXPECT_TRUE(lost_latch_a.Await(kWaitDuration).result()); EXPECT_TRUE(lost_latch_b.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, LostPeripheralNotCallbackForUntrackedServiceId) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, .peripheral_lost_cb = [&lost_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); // Then, stop tracking the service ID and go through two cycles of onLost. discovered_peripheral_tracker_->StopTracking(std::string(kServiceIdA)); discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); // We should NOT receive a client callback of a lost peripheral EXPECT_FALSE(lost_latch.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, InstantLostPeripheralForInstantOnLost) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader(advertisement_hash, service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, .instant_lost_cb = [&lost_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); ASSERT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); auto advertisement = InstantOnLostAdvertisement::CreateFromHashes( std::list({std::string( bleutils::GenerateAdvertisementHash(advertisement_bytes))})); ASSERT_OK(advertisement); api::ble::BleAdvertisementData loss_advertisement_data{}; loss_advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, ByteArray(advertisement->ToBytes())}); FindAdvertisement(loss_advertisement_data, {ByteArray(advertisement->ToBytes())}, fetch_latch); // Then, go through a cycle of onLost. Since we triggered a forced loss via // the instant on los advertisement, the lost call should trigger the onLost // client callback. discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); // We should receive a client callback of a lost peripheral EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, InstantLostPeripheralForInstantOnLost_MultipleAdvertisements) { ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); BleAdvertisementHeader header(BleAdvertisementHeader::Version::kV2, /*extended_advertisement=*/false, /*num_slots=*/1, ByteArray{}, // bloom filter advertisement_hash, BleAdvertisementHeader::kDefaultPsmValue); ByteArray advertisement_bytes_1 = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); ByteArray advertisement_bytes_2 = CreateBleAdvertisement( std::string(kServiceIdB), ByteArray(std::string(kData2)), ByteArray(std::string(kDeviceToken))); auto adv_status_or_1 = BleAdvertisement::CreateBleAdvertisement(advertisement_bytes_1); ASSERT_OK(adv_status_or_1); BleAdvertisement advertisement_1 = adv_status_or_1.value(); auto adv_status_or_2 = BleAdvertisement::CreateBleAdvertisement(advertisement_bytes_2); ASSERT_OK(adv_status_or_2); BleAdvertisement advertisement_2 = adv_status_or_2.value(); // Register callbacks for both Service A and Service B. CountDownLatch lost_latch(2); RegisterServiceIdCallback(std::string(kServiceIdA), lost_latch); RegisterServiceIdCallback(std::string(kServiceIdB), lost_latch); // Use helper method to set up state manually. SetupMultipleAdvertisementsState(header, advertisement_1, advertisement_2); // Create OnLost advertisement for advertisement_1's hash. auto advertisement = InstantOnLostAdvertisement::CreateFromHashes( std::list({std::string(bleutils::GenerateAdvertisementHash( advertisement_1.ByteArrayWithExtraField()))})); ASSERT_OK(advertisement); api::ble::BleAdvertisementData loss_advertisement_data{}; loss_advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, ByteArray(advertisement->ToBytes())}); // Call HandleOnLostAdvertisementLocked using helper. bool result = CallHandleOnLostAdvertisementLocked(loss_advertisement_data); EXPECT_TRUE(result); // Verify that both are lost (callback triggered twice). EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, IgnoreFoundAdvertisementForInstantOnLost) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader(advertisement_hash, service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch fetch_latch(1); MockDiscoveredPeripheralCallback mock_callback; CountDownLatch discovered_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&mock_callback, &discovered_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { mock_callback.OnPeripheralDiscovered(peripheral, service_id, advertisement_bytes, fast_advertisement); discovered_latch.CountDown(); }, .instant_lost_cb = [&mock_callback](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { mock_callback.OnInstantLost(peripheral, service_id, advertisement_bytes, fast_advertisement); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } EXPECT_CALL(mock_callback, OnPeripheralDiscovered).Times(1); FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); fetch_latch.Await(kWaitDuration); EXPECT_TRUE(discovered_latch.Await(kWaitDuration).result()); // We should receive a client callback of a peripheral discovery. EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); CountDownLatch fetch_latch2(1); auto advertisement = InstantOnLostAdvertisement::CreateFromHashes( std::list({std::string( bleutils::GenerateAdvertisementHash(advertisement_bytes))})); ASSERT_OK(advertisement); api::ble::BleAdvertisementData loss_advertisement_data{}; loss_advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, ByteArray(advertisement->ToBytes())}); EXPECT_CALL(mock_callback, OnInstantLost).Times(1); FindAdvertisement(loss_advertisement_data, {ByteArray(advertisement->ToBytes())}, fetch_latch2); fetch_latch2.Await(kWaitDuration); // Then, go through a cycle of onLost. Since we triggered a forced loss via // the instant on los advertisement, the lost call should trigger the onLost // client callback. discovered_peripheral_tracker_->ProcessLostGattAdvertisements(); // Lost advertisement should not be reported. CountDownLatch fetch_latch3(1); EXPECT_CALL(mock_callback, OnPeripheralDiscovered).Times(0); FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch3); fetch_latch3.Await(kWaitDuration); } TEST_P(DiscoveredPeripheralTrackerTest, LostPeripheralWithFastAdvertisementForInstantOnLost) { ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement( ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); // Used to get advertisement hash from fast advertisement bytes. BleAdvertisementHeader ble_advertisement_header = CreateFastBleAdvertisementHeader(fast_advertisement_bytes); ByteArray advertisement_hash = ble_advertisement_header.GetAdvertisementHash(); CountDownLatch found_latch(1); CountDownLatch lost_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_TRUE(fast_advertisement); found_latch.CountDown(); }, .instant_lost_cb = [&lost_latch]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { lost_latch.CountDown(); }, }, Uuid(kFastAdvertisementServiceUuid)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!fast_advertisement_bytes.Empty()) { advertisement_data.service_data.insert( {Uuid(kFastAdvertisementServiceUuid), fast_advertisement_bytes}); } FindFastAdvertisement(advertisement_data, {}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); ASSERT_TRUE(found_latch.Await(kWaitDuration).result()); auto advertisement = InstantOnLostAdvertisement::CreateFromHashes( std::list({std::string(advertisement_hash)})); ASSERT_OK(advertisement); api::ble::BleAdvertisementData loss_advertisement_data{}; loss_advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, ByteArray(advertisement->ToBytes())}); FindAdvertisement(loss_advertisement_data, {ByteArray(advertisement->ToBytes())}, fetch_latch); // We should receive a client callback of a lost peripheral EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); } TEST_P(DiscoveredPeripheralTrackerTest, HandleDummyAdvertisement) { auto flag = nearby::FeatureFlags::Flags{ .enable_invoking_legacy_device_discovered_cb = true, }; MediumEnvironment::Instance().SetFeatureFlags(flag); api::ble::BleAdvertisementData advertising_data{}; advertising_data.is_extended_advertisement = false; ByteArray encoded_bytes{ DiscoveredPeripheralTracker::kDummyAdvertisementValue}; advertising_data.service_data.insert( {mediums::bleutils::kCopresenceServiceUuid, encoded_bytes}); CountDownLatch fetch_latch(1); CountDownLatch legacy_device_found_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { FAIL() << "Should NOT report found for dummy advertisement"; }, .legacy_device_discovered_cb = [&legacy_device_found_latch]() { legacy_device_found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); FindAdvertisement(advertising_data, {}, fetch_latch); EXPECT_TRUE(legacy_device_found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, SkipDummyAdvertisement) { auto flag = nearby::FeatureFlags::Flags{ .enable_invoking_legacy_device_discovered_cb = false, }; MediumEnvironment::Instance().SetFeatureFlags(flag); api::ble::BleAdvertisementData advertising_data{}; advertising_data.is_extended_advertisement = false; ByteArray encoded_bytes{ DiscoveredPeripheralTracker::kDummyAdvertisementValue}; advertising_data.service_data.insert( {mediums::bleutils::kCopresenceServiceUuid, encoded_bytes}); CountDownLatch fetch_latch(1); CountDownLatch legacy_device_found_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { FAIL() << "Should NOT report found for dummy advertisement"; }, .legacy_device_discovered_cb = []() { FAIL() << "Should NOT report found for dummy advertisement"; }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); FindAdvertisement(advertising_data, {}, fetch_latch); EXPECT_FALSE(legacy_device_found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, FetchGattAdvertisementInThread) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisementWithDelay(advertisement_data, {advertisement_bytes}, fetch_latch, kDefaultGattFetchDelay); // We should receive a client callback of a peripheral discovery. EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok()); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, IgnoreGattAdvertisementResultWhentrackingStoppedInThread) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(1); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisementWithDelay(advertisement_data, {advertisement_bytes}, fetch_latch, kDefaultGattFetchDelay); // We should receive a client callback of a peripheral discovery. absl::SleepFor(absl::Milliseconds(20)); discovered_peripheral_tracker_->StopTracking(std::string(kServiceIdA)); EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok()); EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, FetchMultipleGattAdvertisementResultsInThread) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_header_bytes = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_header_bytes_2 = CreateBleAdvertisementHeader( GenerateRandomAdvertisementHash(), service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); ByteArray advertisement_bytes_2 = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData2)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(2); CountDownLatch fetch_latch(2); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, {}); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisementWithDelay(advertisement_data, {advertisement_bytes}, fetch_latch, kDefaultGattFetchDelay); api::ble::BleAdvertisementData advertisement_data_2{}; if (!advertisement_header_bytes_2.Empty()) { advertisement_data_2.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes_2}); } FindAdvertisementWithDelay(advertisement_data_2, {advertisement_bytes_2}, fetch_latch, kDefaultGattFetchDelay); // We should receive a client callback of a peripheral discovery. EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok()); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 2); } TEST_P(DiscoveredPeripheralTrackerTest, OnlyGattAdvertisementReceivedOnDeviceWithExtended) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = CreateExtendedBleAdvertisementHeader(advertisement_hash, service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(2); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, bleutils::kCopresenceServiceUuid); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // 1. First receive a GATT advertisement data, it should be skipped. api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // 2. Receive GATT advertisement data again after 4 seconds, it should access // GATT server. MediumEnvironment::Instance().FastForward(absl::Seconds(4)); FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); // We should receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); } TEST_P(DiscoveredPeripheralTrackerTest, SkipExpiredGattAdvertisement) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = CreateExtendedBleAdvertisementHeader(advertisement_hash, service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(2); discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch](BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { EXPECT_EQ(advertisement_bytes, ByteArray(std::string(kData))); EXPECT_FALSE(fast_advertisement); found_latch.CountDown(); }, }, bleutils::kCopresenceServiceUuid); // 1. First receive a GATT advertisement data, it will expire after 15s. api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisementWithDelay(advertisement_data, {advertisement_bytes}, fetch_latch, kDefaultGattFetchDelay); // 2. The GATT advertisement is already queued and will be skipped. FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); MediumEnvironment::Instance().FastForward(absl::Seconds(20)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // We should not receive a client callback of a peripheral discovery. fetch_latch.Await(kWaitDuration); EXPECT_FALSE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0); } TEST_P(DiscoveredPeripheralTrackerTest, DiscoveredOnceWhenGattAndExtendedAdvertisementReceived) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_bytes = CreateExtendedBleAdvertisementHeader(advertisement_hash, service_ids); ByteArray advertisement_bytes = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(1); CountDownLatch fetch_latch(2); std::atomic callback_count = 0; discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch, &callback_count]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { ++callback_count; found_latch.CountDown(); }, }, bleutils::kCopresenceServiceUuid); MediumEnvironment::Instance().FastForward(absl::Seconds(4)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // 1. Received extended advertisement. api::ble::BleAdvertisementData extended_advertisement_data{}; extended_advertisement_data.is_extended_advertisement = true; extended_advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_bytes}); FindExtendedAdvertisement(extended_advertisement_data, fetch_latch); // 2. Received GATT advertisement. api::ble::BleAdvertisementData advertisement_data{}; if (!advertisement_header_bytes.Empty()) { advertisement_data.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_bytes}); } FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch); EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok()); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1); EXPECT_EQ(callback_count, 1); } TEST_P(DiscoveredPeripheralTrackerTest, FindGattAdvertisementInHigherPriorityThanExtendedGattAdvertisement) { std::vector service_ids = {std::string(kServiceIdA)}; ByteArray advertisement_hash_a = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_a = CreateBleAdvertisementHeader(advertisement_hash_a, service_ids); ByteArray advertisement_a = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string("advertisement_a")), ByteArray(std::string(kDeviceToken))); ByteArray advertisement_hash_b = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_b = CreateExtendedBleAdvertisementHeader(advertisement_hash_b, service_ids); ByteArray advertisement_b = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string("advertisement_b")), ByteArray(std::string(kDeviceToken))); ByteArray advertisement_hash_c = GenerateRandomAdvertisementHash(); ByteArray advertisement_header_c = CreateBleAdvertisementHeader(advertisement_hash_c, service_ids); ByteArray advertisement_c = CreateBleAdvertisement( std::string(kServiceIdA), ByteArray(std::string("advertisement_c")), ByteArray(std::string(kDeviceToken))); CountDownLatch found_latch(3); CountDownLatch fetch_latch(3); std::vector discovered_peripheral; discovered_peripheral_tracker_->StartTracking( std::string(kServiceIdA), false, Pcp::kP2pPointToPoint, { .peripheral_discovered_cb = [&found_latch, &discovered_peripheral]( BlePeripheral peripheral, const std::string& service_id, const ByteArray& advertisement_bytes, bool fast_advertisement) { discovered_peripheral.push_back( std::string(advertisement_bytes)); found_latch.CountDown(); }, }, bleutils::kCopresenceServiceUuid); MediumEnvironment::Instance().FastForward(absl::Seconds(4)); discovered_peripheral_tracker_->StartFetchExecutorForTesting(); // 1. Find peripheral A with GATT advertisement. api::ble::BleAdvertisementData advertisement_data_a{}; advertisement_data_a.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_a}); FindAdvertisementWithDelay(advertisement_data_a, {advertisement_a}, fetch_latch, kDefaultGattFetchDelay); // 2. Find peripheral B with extended advertisement. api::ble::BleAdvertisementData advertisement_data_b{}; advertisement_data_b.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_b}); FindAdvertisement(advertisement_data_b, {advertisement_b}, fetch_latch); // 3. Find peripheral C with GATT advertisement. api::ble::BleAdvertisementData advertisement_data_c{}; advertisement_data_c.service_data.insert( {bleutils::kCopresenceServiceUuid, advertisement_header_c}); FindAdvertisement(advertisement_data_c, {advertisement_c}, fetch_latch); EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok()); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); ASSERT_EQ(discovered_peripheral.size(), 3); EXPECT_THAT(discovered_peripheral, UnorderedElementsAre("advertisement_a", "advertisement_c", "advertisement_b")); } INSTANTIATE_TEST_SUITE_P( DiscoveredPeripheralTrackerFlagsTest, DiscoveredPeripheralTrackerTest, ::testing::Combine( /*is_extended_advertisement_available=*/testing::Bool())); } // namespace mediums } // namespace connections } // namespace nearby