[NC]Read GATT data on device with BLE extended support

PiperOrigin-RevId: 797846587
This commit is contained in:
Guogang Li
2025-08-21 11:32:49 -07:00
committed by Copybara-Service
parent 6113d225e9
commit adf211a891
4 changed files with 498 additions and 72 deletions
@@ -141,6 +141,7 @@ cc_test(
"//internal/platform:uuid",
"//internal/platform/implementation:comm",
"//internal/platform/implementation/g3", # buildcleaner: keep
"//internal/test",
"//proto/mediums:ble_frames_cc_proto",
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/base:core_headers",
@@ -59,6 +59,8 @@ namespace mediums {
namespace {
constexpr int kGattThreadCount = 1;
constexpr absl::Duration kInstantLostAdvertisementTimeout = absl::Seconds(60);
constexpr absl::Duration kExtendedAdvertisementHeaderDelay = absl::Seconds(3);
constexpr absl::Duration kAdvertisementHeaderExpiry = absl::Seconds(15);
} // namespace
DiscoveredPeripheralTracker::DiscoveredPeripheralTracker(
@@ -127,6 +129,11 @@ void DiscoveredPeripheralTracker::StartTracking(
.include_dct_advertisement = include_dct_advertisement,
.pcp = pcp};
if (service_id_infos_.empty()) {
medium_start_scanning_time_ = SystemClock::ElapsedRealtime();
VLOG(1) << "Medium start scanning time: " << medium_start_scanning_time_;
}
// Replace if key exists.
service_id_infos_.insert_or_assign(service_id, std::move(service_id_info));
@@ -177,8 +184,7 @@ void DiscoveredPeripheralTracker::ProcessFoundBleAdvertisement(
}
if (IsSkippableGattAdvertisement(advertisement_data)) {
LOG(INFO)
<< "Ignore GATT advertisement and wait for extended advertisement.";
VLOG(1) << "Ignore GATT advertisement and wait for extended advertisement.";
return;
}
@@ -330,8 +336,21 @@ bool DiscoveredPeripheralTracker::IsSkippableGattAdvertisement(
BleAdvertisementHeader advertisement_header(
ExtractAdvertisementHeaderBytes(advertisement_data));
return advertisement_header.IsValid() &&
advertisement_header.IsSupportExtendedAdvertisement();
if (is_read_gatt_for_extended_advertisement_enabled_) {
if (!advertisement_header.IsValid()) {
return false;
}
if (advertisement_header.IsSupportExtendedAdvertisement() &&
(SystemClock::ElapsedRealtime() - medium_start_scanning_time_) <
kExtendedAdvertisementHeaderDelay) {
return true;
}
return false;
} else {
return advertisement_header.IsValid() &&
advertisement_header.IsSupportExtendedAdvertisement();
}
}
void DiscoveredPeripheralTracker::ClearGattAdvertisement(
@@ -749,46 +768,67 @@ void DiscoveredPeripheralTracker::HandleAdvertisementHeader(
}
// Determine whether or not we need to read a fresh GATT advertisement.
if (ShouldReadRawAdvertisementFromServer(advertisement_header)) {
// Determine whether or not we need to read a fresh GATT advertisement.
if (is_fetching_in_thread_) {
VLOG(1) << ": Handle GATT advertisement header with hash "
<< absl::BytesToHexString(
advertisement_header.GetAdvertisementHash().AsStringView())
<< " in thread";
if (is_fetching_in_thread_) {
VLOG(1) << ": Handle GATT advertisement header with hash "
<< absl::BytesToHexString(
advertisement_header.GetAdvertisementHash().AsStringView())
<< " in thread";
if (!fetching_advertisements_.insert(advertisement_header).second) {
VLOG(1) << ": Ignore the advertisement header due to it "
"is already in fetching.";
return;
}
if (executor_ == nullptr) {
// The situation happens when flag value changed
executor_ = std::make_unique<MultiThreadExecutor>(kGattThreadCount);
}
if (executor_ == nullptr) {
// The situation happens when flag value changed
executor_ = std::make_unique<MultiThreadExecutor>(kGattThreadCount);
}
if (!ShouldReadRawAdvertisementFromServer(advertisement_header)) {
UpdateCommonStateForFoundBleAdvertisement(advertisement_header);
return;
}
if (is_read_gatt_for_extended_advertisement_enabled_) {
{
MutexLock lock(&task_mutex_);
gatt_fetch_tasks_.push_back({
.peripheral = peripheral,
.advertisement_header = advertisement_header,
.advertisement_fetcher = std::move(advertisement_fetcher),
});
cond_.Notify();
if (is_read_gatt_for_extended_advertisement_enabled_) {
MutexLock lock(&task_mutex_);
if (advertisement_header.IsSupportExtendedAdvertisement() &&
is_extended_advertisement_available_) {
for (auto& item : gatt_extended_fetch_tasks_) {
if (item.advertisement_header == advertisement_header) {
item.scheduled_time = SystemClock::ElapsedRealtime();
UpdateCommonStateForFoundBleAdvertisement(advertisement_header);
return;
}
}
gatt_extended_fetch_tasks_.push_back({
.peripheral = peripheral,
.advertisement_header = advertisement_header,
.advertisement_fetcher = std::move(advertisement_fetcher),
.scheduled_time = SystemClock::ElapsedRealtime(),
});
} else {
executor_->Execute([this, peripheral, advertisement_header,
advertisement_fetcher =
std::move(advertisement_fetcher)]() mutable {
FetchRawAdvertisementsInThread(peripheral, advertisement_header,
std::move(advertisement_fetcher));
for (auto& item : gatt_fetch_tasks_) {
if (item.advertisement_header == advertisement_header) {
item.scheduled_time = SystemClock::ElapsedRealtime();
UpdateCommonStateForFoundBleAdvertisement(advertisement_header);
return;
}
}
gatt_fetch_tasks_.push_back({
.peripheral = peripheral,
.advertisement_header = advertisement_header,
.advertisement_fetcher = std::move(advertisement_fetcher),
.scheduled_time = SystemClock::ElapsedRealtime(),
});
}
return;
cond_.Notify();
} else {
executor_->Execute(
[this, peripheral, advertisement_header,
advertisement_fetcher = std::move(advertisement_fetcher)]() mutable {
FetchRawAdvertisementsInThread(peripheral, advertisement_header,
std::move(advertisement_fetcher));
});
}
} else {
if (ShouldReadRawAdvertisementFromServer(advertisement_header)) {
std::vector<const ByteArray*> gatt_advertisement_bytes_list =
FetchRawAdvertisements(peripheral, advertisement_header,
std::move(advertisement_fetcher));
@@ -869,7 +909,7 @@ bool DiscoveredPeripheralTracker::ShouldReadRawAdvertisementFromServer(
<< ", but we have already read its GATT advertisement.";
return false;
case AdvertisementReadResult::RetryStatus::kTooSoon:
LOG(INFO)
VLOG(1)
<< "Received advertisement header with hash "
<< absl::BytesToHexString(
advertisement_header.GetAdvertisementHash().AsStringView())
@@ -922,7 +962,10 @@ void DiscoveredPeripheralTracker::FetchRawAdvertisementsInThread(
if (!IsInterestingAdvertisementHeader(advertisement_header)) {
LOG(INFO) << ": Ignore to read raw advertisement from server due to it "
"is not interesting header now.";
fetching_advertisements_.erase(advertisement_header);
return;
}
if (!ShouldReadRawAdvertisementFromServer(advertisement_header)) {
return;
}
@@ -943,7 +986,6 @@ void DiscoveredPeripheralTracker::FetchRawAdvertisementsInThread(
LOG(WARNING)
<< ": Ignore the fetched GATT advertisement from server due to it "
"is not interesting header now.";
fetching_advertisements_.erase(advertisement_header);
return;
}
@@ -952,9 +994,9 @@ void DiscoveredPeripheralTracker::FetchRawAdvertisementsInThread(
std::vector<const ByteArray*> gatt_advertisement_bytes_list =
it.first->second->GetAdvertisements();
if (gatt_advertisement_bytes_list.empty() ||
!IsInterestingAdvertisementHeader(advertisement_header)) {
fetching_advertisements_.erase(advertisement_header);
if (gatt_advertisement_bytes_list.empty()) {
VLOG(1) << ": Ignore the fetched GATT advertisement from server due to "
"it is empty.";
return;
}
@@ -962,7 +1004,6 @@ void DiscoveredPeripheralTracker::FetchRawAdvertisementsInThread(
gatt_advertisement_bytes_list,
/*service_uuid=*/{});
UpdateCommonStateForFoundBleAdvertisement(advertisement_header);
fetching_advertisements_.erase(advertisement_header);
VLOG(1) << ": Completed to handle GATT advertisement header with hash "
<< absl::BytesToHexString(
advertisement_header.GetAdvertisementHash().AsStringView())
@@ -979,17 +1020,37 @@ void DiscoveredPeripheralTracker::GattFetchingLoop() {
gatt_fetch_tasks_.clear();
return;
}
while (gatt_fetch_tasks_.empty() && !shutting_down_) {
while (gatt_fetch_tasks_.empty() && gatt_extended_fetch_tasks_.empty() &&
!shutting_down_) {
cond_.Wait();
}
if (shutting_down_) {
gatt_fetch_tasks_.clear();
gatt_extended_fetch_tasks_.clear();
return;
}
task = std::move(gatt_fetch_tasks_.front());
gatt_fetch_tasks_.pop_front();
// Non-extended advertisements are prioritized over extended
// advertisements.
if (!gatt_fetch_tasks_.empty()) {
task = std::move(gatt_fetch_tasks_.front());
gatt_fetch_tasks_.pop_front();
} else if (!gatt_extended_fetch_tasks_.empty()) {
task = std::move(gatt_extended_fetch_tasks_.front());
gatt_extended_fetch_tasks_.pop_front();
}
}
// Check if the task is expired.
if (SystemClock::ElapsedRealtime() - task.scheduled_time >
kAdvertisementHeaderExpiry) {
VLOG(1) << "GATT advertisement with hash: "
<< absl::BytesToHexString(
task.advertisement_header.GetAdvertisementHash()
.AsStringView())
<< " is expired, skip to fetch raw advertisement.";
continue;
}
FetchRawAdvertisementsInThread(task.peripheral, task.advertisement_header,
@@ -1001,7 +1062,7 @@ void DiscoveredPeripheralTracker::UpdateCommonStateForFoundBleAdvertisement(
const BleAdvertisementHeader& advertisement_header) {
const auto ga_it = gatt_advertisements_.find(advertisement_header);
if (ga_it == gatt_advertisements_.end()) {
LOG(INFO)
VLOG(1)
<< "No GATT advertisements found for advertisement header with hash "
<< absl::BytesToHexString(
advertisement_header.GetAdvertisementHash().AsStringView());
@@ -158,6 +158,7 @@ class DiscoveredPeripheralTracker {
BleV2Peripheral peripheral;
BleAdvertisementHeader advertisement_header;
AdvertisementFetcher advertisement_fetcher;
absl::Time scheduled_time;
};
// Clears stale data from any previous sessions.
@@ -309,6 +310,9 @@ class DiscoveredPeripheralTracker {
bool is_read_gatt_for_extended_advertisement_enabled_ = true;
bool is_extended_advertisement_available_;
absl::Time medium_start_scanning_time_ ABSL_GUARDED_BY(mutex_) =
absl::InfiniteFuture();
// ------------ SERVICE ID MAPS ------------
// Entries in these maps all follow the same lifecycle. Entries are added in
// StartTracking, and removed in StopTracking.
@@ -351,10 +355,6 @@ class DiscoveredPeripheralTracker {
absl::flat_hash_map<BleAdvertisement, GattAdvertisementInfo>
gatt_advertisement_infos_ ABSL_GUARDED_BY(mutex_);
// Tracks the advertisements in GATT fetching.
absl::flat_hash_set<BleAdvertisementHeader> fetching_advertisements_
ABSL_GUARDED_BY(mutex_);
std::unique_ptr<MultiThreadExecutor> executor_ ABSL_GUARDED_BY(mutex_) =
nullptr;
@@ -362,6 +362,8 @@ class DiscoveredPeripheralTracker {
AtomicBoolean shutting_down_{false};
ConditionVariable cond_{&task_mutex_};
std::deque<GattFetchTask> gatt_fetch_tasks_ ABSL_GUARDED_BY(task_mutex_);
std::deque<GattFetchTask> gatt_extended_fetch_tasks_
ABSL_GUARDED_BY(task_mutex_);
// Maps an advertisement header's hash with the time it's reported lost.
// Ignores subsequent discovery events for the same advertisement header.
@@ -17,8 +17,10 @@
#include <atomic>
#include <list>
#include <memory>
#include <optional>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "gmock/gmock.h"
@@ -51,6 +53,7 @@
#include "internal/platform/mutex.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/uuid.h"
#include "internal/test/fake_clock.h"
namespace nearby {
namespace connections {
@@ -66,6 +69,7 @@ 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);
ByteArray CreateFastBleAdvertisement(const ByteArray& data,
const ByteArray& device_token) {
@@ -141,6 +145,24 @@ ByteArray CreateBleAdvertisementHeader(const ByteArray& advertisement_hash,
service_ids);
}
ByteArray CreateExtendedBleAdvertisementHeader(
const ByteArray& advertisement_hash,
std::vector<std::string>& service_ids) {
BloomFilter service_id_bloom_filter(
std::make_unique<BitSetImpl<
BleAdvertisementHeader::kServiceIdBloomFilterByteLength>>());
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);
@@ -179,13 +201,18 @@ class DiscoveredPeripheralTrackerTest
config_package_nearby::nearby_connections_feature::
kEnableGattQueryInThread,
std::get<0>(GetParam()));
bool enable_read_gatt_for_extended_advertisement = std::get<1>(GetParam());
NearbyFlags::GetInstance().OverrideBoolFlagValue(
config_package_nearby::nearby_connections_feature::
kEnableReadGattForExtendedAdvertisement,
std::get<1>(GetParam()));
MediumEnvironment::Instance().Start();
enable_read_gatt_for_extended_advertisement);
EnvironmentConfig config{
.webrtc_enabled = false,
.use_simulated_clock = enable_read_gatt_for_extended_advertisement};
MediumEnvironment::Instance().Start(config);
discovered_peripheral_tracker_ =
std::make_unique<DiscoveredPeripheralTracker>();
std::make_unique<DiscoveredPeripheralTracker>(
enable_read_gatt_for_extended_advertisement);
adapter_peripheral_ = std::make_unique<BluetoothAdapter>();
adapter_central_ = std::make_unique<BluetoothAdapter>();
ble_peripheral_ = std::make_unique<BleV2Medium>(*adapter_peripheral_);
@@ -216,6 +243,20 @@ class DiscoveredPeripheralTrackerTest
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_v2::BleAdvertisementData& advertisement_data,
CountDownLatch& fetch_latch) {
BleV2Peripheral peripheral = CreateBlePeripheral();
DiscoveredPeripheralTracker::AdvertisementFetcher placeholder_fetcher =
[](BleV2Peripheral, int, int, const std::vector<std::string>&,
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_v2::BleAdvertisementData& advertisement_data,
@@ -228,15 +269,16 @@ class DiscoveredPeripheralTrackerTest
GetAdvertisementFetcher(fetch_latch, advertisement_bytes_list));
}
void FindAdvertisementWithSlowFetcher(
void FindAdvertisementWithDelay(
const api::ble_v2::BleAdvertisementData& advertisement_data,
const std::vector<ByteArray>& advertisement_bytes_list,
CountDownLatch& fetch_latch) {
CountDownLatch& fetch_latch, absl::Duration delay) {
BleV2Peripheral peripheral = CreateBlePeripheral();
discovered_peripheral_tracker_->ProcessFoundBleAdvertisement(
peripheral, advertisement_data,
GetSlowAdvertisementFetcher(fetch_latch, advertisement_bytes_list));
GetAdvertisementFetcherWithDelay(fetch_latch, advertisement_bytes_list,
delay));
}
int GetFetchAdvertisementCallbackCount() const {
@@ -278,10 +320,12 @@ class DiscoveredPeripheralTrackerTest
};
}
DiscoveredPeripheralTracker::AdvertisementFetcher GetSlowAdvertisementFetcher(
DiscoveredPeripheralTracker::AdvertisementFetcher
GetAdvertisementFetcherWithDelay(
CountDownLatch& fetch_latch,
const std::vector<ByteArray>& advertisement_bytes_list) {
return [this, &fetch_latch, advertisement_bytes_list](
const std::vector<ByteArray>& advertisement_bytes_list,
absl::Duration delay) {
return [this, &fetch_latch, advertisement_bytes_list, delay](
BleV2Peripheral peripheral, int num_slots, int psm,
const std::vector<std::string>& interesting_service_ids,
mediums::AdvertisementReadResult& advertisement_read_result) {
@@ -289,7 +333,7 @@ class DiscoveredPeripheralTrackerTest
fetch_count_++;
int slot = 0;
// In real environment, the GATT fetch may run about 3-5 seconds.
absl::SleepFor(absl::Milliseconds(200));
absl::SleepFor(delay);
for (const auto& advertisement_bytes : advertisement_bytes_list) {
advertisement_read_result.AddAdvertisement(slot++, advertisement_bytes);
}
@@ -1539,11 +1583,11 @@ TEST_P(DiscoveredPeripheralTrackerTest, FetchGattAdvertisementInThread) {
{bleutils::kCopresenceServiceUuid, advertisement_header_bytes});
}
FindAdvertisementWithSlowFetcher(advertisement_data, {advertisement_bytes},
fetch_latch);
FindAdvertisementWithDelay(advertisement_data, {advertisement_bytes},
fetch_latch, kDefaultGattFetchDelay);
// We should receive a client callback of a peripheral discovery.
fetch_latch.Await(kWaitDuration);
EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok());
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1);
}
@@ -1586,13 +1630,13 @@ TEST_P(DiscoveredPeripheralTrackerTest,
{bleutils::kCopresenceServiceUuid, advertisement_header_bytes});
}
FindAdvertisementWithSlowFetcher(advertisement_data, {advertisement_bytes},
fetch_latch);
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));
fetch_latch.Await(kWaitDuration);
EXPECT_TRUE(fetch_latch.Await(kWaitDuration).ok());
EXPECT_FALSE(found_latch.Await(kWaitDuration).result());
EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1);
}
@@ -1638,8 +1682,8 @@ TEST_P(DiscoveredPeripheralTrackerTest,
{bleutils::kCopresenceServiceUuid, advertisement_header_bytes});
}
FindAdvertisementWithSlowFetcher(advertisement_data, {advertisement_bytes},
fetch_latch);
FindAdvertisementWithDelay(advertisement_data, {advertisement_bytes},
fetch_latch, kDefaultGattFetchDelay);
api::ble_v2::BleAdvertisementData advertisement_data_2{};
if (!advertisement_header_bytes_2.Empty()) {
@@ -1647,13 +1691,331 @@ TEST_P(DiscoveredPeripheralTrackerTest,
{bleutils::kCopresenceServiceUuid, advertisement_header_bytes_2});
}
FindAdvertisementWithSlowFetcher(advertisement_data_2,
{advertisement_bytes_2}, fetch_latch);
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,
GattAdvertisementGotEarlierThanExtendedAdvertisement) {
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableGattQueryInThread) ||
!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableReadGattForExtendedAdvertisement)) {
return;
}
std::vector<std::string> 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](BleV2Peripheral 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 should be skipped.
api::ble_v2::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 extended advertisement data first.
api::ble_v2::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);
// We should receive a client callback of a peripheral discovery.
fetch_latch.Await(kWaitDuration);
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 2);
EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 0);
}
TEST_P(DiscoveredPeripheralTrackerTest,
OnlyGattAdvertisementReceivedOnDeviceWithExtended) {
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableGattQueryInThread) ||
!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableReadGattForExtendedAdvertisement)) {
return;
}
std::optional<FakeClock*> fake_clock =
MediumEnvironment::Instance().GetSimulatedClock();
std::vector<std::string> 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](BleV2Peripheral 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 should be skipped.
api::ble_v2::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.
(*fake_clock)->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) {
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableGattQueryInThread) ||
!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableReadGattForExtendedAdvertisement)) {
return;
}
std::optional<FakeClock*> fake_clock =
MediumEnvironment::Instance().GetSimulatedClock();
std::vector<std::string> 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](BleV2Peripheral 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 should be skipped.
api::ble_v2::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 will be expired after 20 seconds.
FindAdvertisement(advertisement_data, {advertisement_bytes}, fetch_latch);
(*fake_clock)->FastForward(absl::Seconds(20));
// We should 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) {
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableGattQueryInThread) ||
!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableReadGattForExtendedAdvertisement)) {
return;
}
std::optional<FakeClock*> fake_clock =
MediumEnvironment::Instance().GetSimulatedClock();
std::vector<std::string> 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<int> callback_count = 0;
discovered_peripheral_tracker_->StartTracking(
std::string(kServiceIdA), false, Pcp::kP2pPointToPoint,
{
.peripheral_discovered_cb =
[&found_latch, &callback_count](
BleV2Peripheral peripheral, const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
++callback_count;
found_latch.CountDown();
},
},
bleutils::kCopresenceServiceUuid);
(*fake_clock)->FastForward(absl::Seconds(4));
// 1. Received extended advertisement.
api::ble_v2::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_v2::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) {
if (!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableGattQueryInThread) ||
!NearbyFlags::GetInstance().GetBoolFlag(
config_package_nearby::nearby_connections_feature::
kEnableReadGattForExtendedAdvertisement)) {
return;
}
std::optional<FakeClock*> fake_clock =
MediumEnvironment::Instance().GetSimulatedClock();
std::vector<std::string> 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<std::string> discovered_peripheral_order;
discovered_peripheral_tracker_->StartTracking(
std::string(kServiceIdA), false, Pcp::kP2pPointToPoint,
{
.peripheral_discovered_cb =
[&found_latch, &discovered_peripheral_order](
BleV2Peripheral peripheral, const std::string& service_id,
const ByteArray& advertisement_bytes,
bool fast_advertisement) {
discovered_peripheral_order.push_back(
std::string(advertisement_bytes));
found_latch.CountDown();
},
},
bleutils::kCopresenceServiceUuid);
(*fake_clock)->FastForward(absl::Seconds(4));
// 1. Find peripheral A with GATT advertisement.
api::ble_v2::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_v2::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_v2::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_order.size(), 3);
EXPECT_EQ(discovered_peripheral_order[0], "advertisement_a");
EXPECT_EQ(discovered_peripheral_order[1], "advertisement_c");
EXPECT_EQ(discovered_peripheral_order[2], "advertisement_b");
}
INSTANTIATE_TEST_SUITE_P(