mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
[BLE Refactor] Implements DiscoveredPeripheralTracker.
PiperOrigin-RevId: 441524475
This commit is contained in:
committed by
Copybara-Service
parent
3aa6b2f175
commit
3050f9432c
@@ -22,6 +22,7 @@ cc_library(
|
||||
"ble_packet.cc",
|
||||
"ble_utils.cc",
|
||||
"bloom_filter.cc",
|
||||
"discovered_peripheral_tracker.cc",
|
||||
],
|
||||
hdrs = [
|
||||
"advertisement_read_result.h",
|
||||
@@ -32,6 +33,7 @@ cc_library(
|
||||
"ble_utils.h",
|
||||
"bloom_filter.h",
|
||||
"discovered_peripheral_callback.h",
|
||||
"discovered_peripheral_tracker.h",
|
||||
],
|
||||
copts = ["-DCORE_ADAPTER_DLL"],
|
||||
visibility = [
|
||||
@@ -41,6 +43,7 @@ cc_library(
|
||||
"//connections:core_types",
|
||||
"//connections/implementation/mediums:utils",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:comm",
|
||||
"//internal/platform:logging",
|
||||
"//internal/platform:types",
|
||||
"//internal/platform:util",
|
||||
@@ -49,6 +52,7 @@ cc_library(
|
||||
"@com_google_absl//absl/container:flat_hash_set",
|
||||
"@com_google_absl//absl/numeric:int128",
|
||||
"@com_google_absl//absl/strings",
|
||||
"@com_google_absl//absl/strings:str_format",
|
||||
"@com_google_absl//absl/time",
|
||||
],
|
||||
)
|
||||
@@ -63,11 +67,13 @@ cc_test(
|
||||
"ble_peripheral_test.cc",
|
||||
"ble_utils_test.cc",
|
||||
"bloom_filter_test.cc",
|
||||
"discovered_peripheral_tracker_test.cc",
|
||||
],
|
||||
deps = [
|
||||
":ble_v2",
|
||||
"//internal/platform:base",
|
||||
"//internal/platform:comm",
|
||||
"//internal/platform:types",
|
||||
"//internal/platform/implementation/g3", # buildcleaner: keep
|
||||
"@com_github_protobuf_matchers//protobuf-matchers",
|
||||
"@com_google_absl//absl/hash:hash_testing",
|
||||
|
||||
@@ -34,6 +34,20 @@ namespace {
|
||||
constexpr std::int64_t kAdvertisementUuidMsb = 0x0000000000003000;
|
||||
constexpr std::int64_t kAdvertisementUuidLsb = 0x8000000000000000;
|
||||
|
||||
// Creates a string as a space separated listing of hex bytes with [] at the
|
||||
// beginning and the end.
|
||||
//
|
||||
// This is the legacy input for hash in version (kV1). It is for testing only.
|
||||
std::string StringToPrintableHexString(const std::string& source) {
|
||||
// Print out the byte array as a space separated listing of hex bytes.
|
||||
std::string out = "[ ";
|
||||
for (const char& c : source) {
|
||||
absl::StrAppend(&out, absl::StrFormat("%#04x ", c));
|
||||
}
|
||||
absl::StrAppend(&out, "]");
|
||||
return out;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const absl::string_view kCopresenceServiceUuid =
|
||||
@@ -43,8 +57,21 @@ ByteArray GenerateHash(const std::string& source, size_t size) {
|
||||
return Utils::Sha256Hash(source, size);
|
||||
}
|
||||
|
||||
ByteArray GenerateServiceIdHash(const std::string& service_id) {
|
||||
return Utils::Sha256Hash(service_id, BlePacket::kServiceIdHashLength);
|
||||
ByteArray GenerateServiceIdHash(const std::string& service_id,
|
||||
BleAdvertisement::Version version) {
|
||||
switch (version) {
|
||||
// legacy hash for testing only.
|
||||
case BleAdvertisement::Version::kV1:
|
||||
return Utils::Sha256Hash(StringToPrintableHexString(service_id),
|
||||
BlePacket::kServiceIdHashLength);
|
||||
case BleAdvertisement::Version::kV2:
|
||||
[[fallthrough]];
|
||||
case BleAdvertisement::Version::kUndefined:
|
||||
[[fallthrough]];
|
||||
default:
|
||||
// Use the latest known hashing scheme.
|
||||
return Utils::Sha256Hash(service_id, BlePacket::kServiceIdHashLength);
|
||||
}
|
||||
}
|
||||
|
||||
ByteArray GenerateDeviceToken() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "connections/implementation/mediums/ble_v2//ble_advertisement.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_packet.h"
|
||||
@@ -32,10 +33,34 @@ namespace bleutils {
|
||||
|
||||
ABSL_CONST_INIT extern const absl::string_view kCopresenceServiceUuid;
|
||||
|
||||
// Return SHA256 hash.
|
||||
//
|
||||
// source - the string to be hashed.
|
||||
// size - size of returned byte array.
|
||||
ByteArray GenerateHash(const std::string& source, size_t size);
|
||||
ByteArray GenerateServiceIdHash(const std::string& service_id);
|
||||
|
||||
// Return SHA256 hash of service ID.
|
||||
//
|
||||
// source - service id.
|
||||
// version - BleAdvertisement::Version. kV1 has been deprecated and just used
|
||||
// for testing.
|
||||
ByteArray GenerateServiceIdHash(
|
||||
const std::string& service_id,
|
||||
BleAdvertisement::Version version = BleAdvertisement::Version::kV2);
|
||||
|
||||
// Returns device token generated by SHA256 hash in random uint32 and
|
||||
// size of mediums::BleAdvertisement::kDeviceTokenLength
|
||||
ByteArray GenerateDeviceToken();
|
||||
|
||||
// Return SHA256 hash of advertisement byte array.
|
||||
//
|
||||
// advertisement_bytes - advertisement byte array.
|
||||
// The generated hash size is determined by byte array size of input.
|
||||
ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes);
|
||||
|
||||
// Generates a BLE characteristic UUID for an advertisement at the given slot.
|
||||
//
|
||||
// slot - the advertisement slot to generate a UUID for.
|
||||
std::string GenerateAdvertisementUuid(int slot);
|
||||
|
||||
} // namespace bleutils
|
||||
|
||||
@@ -0,0 +1,638 @@
|
||||
// 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_v2/discovered_peripheral_tracker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/escaping.h"
|
||||
#include "connections/implementation/mediums/ble_v2/advertisement_read_result.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_advertisement.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_utils.h"
|
||||
#include "connections/implementation/mediums/ble_v2/bloom_filter.h"
|
||||
#include "internal/platform/mutex_lock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
void DiscoveredPeripheralTracker::StartTracking(
|
||||
const std::string& service_id,
|
||||
const DiscoveredPeripheralCallback& discovered_peripheral_callback,
|
||||
const std::string& fast_advertisement_service_uuid) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
ServiceIdInfo service_id_info = {
|
||||
.discovered_peripheral_callback =
|
||||
std::move(discovered_peripheral_callback),
|
||||
.lost_entity_tracker =
|
||||
absl::make_unique<LostEntityTracker<BleAdvertisement>>(),
|
||||
.fast_advertisement_service_uuid = fast_advertisement_service_uuid};
|
||||
|
||||
// Replace if key exists.
|
||||
service_id_infos_.insert_or_assign(service_id, std::move(service_id_info));
|
||||
|
||||
// Clear all of the GATT read results. With this cleared, we will now attempt
|
||||
// to reconnect to every peripheral we see, giving us a chance to search for
|
||||
// the new service we're now tracking.
|
||||
// See the documentation of advertisementReadResult for more information.
|
||||
advertisement_read_results_.clear();
|
||||
|
||||
// Remove stale data from any previous sessions.
|
||||
ClearDataForServiceId(service_id);
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::StopTracking(const std::string& service_id) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
service_id_infos_.erase(service_id);
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::ProcessFoundBleAdvertisement(
|
||||
BleV2Peripheral peripheral,
|
||||
const ::location::nearby::api::ble_v2::BleAdvertisementData&
|
||||
advertisement_data,
|
||||
AdvertisementFetcher advertisement_fetcher) {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
if (service_id_infos_.empty()) {
|
||||
NEARBY_LOGS(INFO) << "Ignoring BLE advertisement header because we are not "
|
||||
"tracking any service IDs.";
|
||||
return;
|
||||
}
|
||||
|
||||
if (!peripheral.IsValid() || advertisement_data.service_data.empty()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Ignoring BLE advertisement header because the peripheral is "
|
||||
"invalid or the given service data is empty.";
|
||||
return;
|
||||
}
|
||||
|
||||
HandleAdvertisement(peripheral, advertisement_data);
|
||||
HandleAdvertisementHeader(advertisement_data, /*mutated=*/peripheral,
|
||||
std::move(advertisement_fetcher));
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::ProcessLostGattAdvertisements() {
|
||||
MutexLock lock(&mutex_);
|
||||
|
||||
for (const auto& it : service_id_infos_) {
|
||||
const std::string& service_id = it.first;
|
||||
const ServiceIdInfo& service_id_info = it.second;
|
||||
DiscoveredPeripheralCallback discovered_peripheral_callback =
|
||||
service_id_info.discovered_peripheral_callback;
|
||||
|
||||
BleAdvertisementSet lost_gatt_advertisements =
|
||||
service_id_info.lost_entity_tracker->ComputeLostEntities();
|
||||
// Clear the map state for each lost GATT advertisement and report it to the
|
||||
// client.
|
||||
for (const auto& gatt_advertisement : lost_gatt_advertisements) {
|
||||
ClearGattAdvertisement(gatt_advertisement);
|
||||
|
||||
BlePeripheral peripheral = GenerateBlePeripheral(gatt_advertisement);
|
||||
if (peripheral.IsValid()) {
|
||||
discovered_peripheral_callback.peripheral_lost_cb(peripheral,
|
||||
service_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::ClearDataForServiceId(
|
||||
const std::string& service_id) {
|
||||
for (const auto& it : gatt_advertisement_infos_) {
|
||||
if (it.second.service_id != service_id) {
|
||||
continue;
|
||||
}
|
||||
ClearGattAdvertisement(it.first);
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::ClearGattAdvertisement(
|
||||
const BleAdvertisement& gatt_advertisement) {
|
||||
const auto gai_it = gatt_advertisement_infos_.find(gatt_advertisement);
|
||||
if (gai_it == gatt_advertisement_infos_.end()) {
|
||||
return;
|
||||
}
|
||||
auto item = gatt_advertisement_infos_.extract(gai_it);
|
||||
GattAdvertisementInfo& gatt_advertisement_info = item.mapped();
|
||||
|
||||
const auto ga_it =
|
||||
gatt_advertisements_.find(gatt_advertisement_info.advertisement_header);
|
||||
if (ga_it != gatt_advertisements_.end()) {
|
||||
// Remove the GATT advertisement from the advertisement header it's
|
||||
// associated with.
|
||||
BleAdvertisementSet& gatt_advertisement_set = ga_it->second;
|
||||
gatt_advertisement_set.erase(gatt_advertisement);
|
||||
|
||||
// Unconditionally remove the header from advertisement_read_results_ so we
|
||||
// can attempt to reread the GATT advertisement if they return.
|
||||
advertisement_read_results_.erase(
|
||||
gatt_advertisement_info.advertisement_header);
|
||||
|
||||
// If there are no more tracked GATT advertisements under this header, go
|
||||
// ahead and remove it from gatt_advertisements_.
|
||||
if (gatt_advertisement_set.empty()) {
|
||||
gatt_advertisements_.erase(ga_it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::HandleAdvertisement(
|
||||
const BleV2Peripheral& peripheral,
|
||||
const location::nearby::api::ble_v2::BleAdvertisementData&
|
||||
advertisement_data) {
|
||||
ByteArray advertisement_bytes =
|
||||
ExtractInterestingAdvertisementBytes(advertisement_data);
|
||||
if (advertisement_bytes.Empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The UUID that the Fast/Regular Advertisement was found on. For regular
|
||||
// advertisement, it's always kCopresenceServiceUuid. For fast
|
||||
// advertisement, we may have below 2 UUIDs: 1. kCopresenceServiceUuid 2.
|
||||
// Caller UUID.
|
||||
// First filter out kCopresenceServiceUuid and see if any Caller UUID
|
||||
// existed; if not then just take the kCopresenceServiceUuid as
|
||||
// |service_uuid|.
|
||||
std::vector<std::string> extracted_uuids;
|
||||
// Filter out kCoprsence service uuid.
|
||||
std::remove_copy_if(advertisement_data.service_uuids.begin(),
|
||||
advertisement_data.service_uuids.end(),
|
||||
std::back_inserter(extracted_uuids),
|
||||
[](const std::string& advertisement_data_service_uuid) {
|
||||
return advertisement_data_service_uuid ==
|
||||
bleutils::kCopresenceServiceUuid;
|
||||
});
|
||||
std::string service_uuid;
|
||||
if (!extracted_uuids.empty()) {
|
||||
service_uuid = extracted_uuids.front();
|
||||
} else {
|
||||
service_uuid = std::string(bleutils::kCopresenceServiceUuid);
|
||||
}
|
||||
|
||||
// Create a header tied to this fast advertisement. This helps us track the
|
||||
// advertisement when reporting it as lost or connecting.
|
||||
BleAdvertisementHeader advertisement_header =
|
||||
CreateAdvertisementHeader(advertisement_bytes);
|
||||
|
||||
// Process the fast advertisement like we would a GATT advertisement and
|
||||
// insert a placeholder AdvertisementReadResult.
|
||||
advertisement_read_results_.insert(
|
||||
{advertisement_header, absl::make_unique<AdvertisementReadResult>()});
|
||||
|
||||
BleAdvertisementHeader new_advertisement_header = HandleRawGattAdvertisements(
|
||||
advertisement_header, {&advertisement_bytes}, service_uuid);
|
||||
UpdateCommonStateForFoundBleAdvertisement(new_advertisement_header,
|
||||
/*mac_address=*/peripheral.GetId());
|
||||
}
|
||||
|
||||
ByteArray DiscoveredPeripheralTracker::ExtractInterestingAdvertisementBytes(
|
||||
const location::nearby::api::ble_v2::BleAdvertisementData&
|
||||
advertisement_data) {
|
||||
// Iterate through all tracked service IDs to see if any of their fast
|
||||
// advertisements are contained within this BLE advertisement.
|
||||
for (const auto& item : service_id_infos_) {
|
||||
const ServiceIdInfo& service_id_info = item.second;
|
||||
// Check if there's service data for this fast advertisement
|
||||
// service UUID. If so, we can short-circuit since all BLE
|
||||
// advertisements can contain at most ONE fast advertisement.
|
||||
const auto sd_it = advertisement_data.service_data.find(
|
||||
service_id_info.fast_advertisement_service_uuid);
|
||||
if (sd_it != advertisement_data.service_data.end()) {
|
||||
return sd_it->second;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
BleAdvertisementHeader DiscoveredPeripheralTracker::CreateAdvertisementHeader(
|
||||
const ByteArray& advertisement_bytes) {
|
||||
// Our end goal is to have a fully zeroed-out byte array of the correct
|
||||
// length representing an empty bloom filter.
|
||||
BloomFilter bloom_filter(
|
||||
std::make_unique<BitSetImpl<
|
||||
BleAdvertisementHeader::kServiceIdBloomFilterByteLength>>());
|
||||
|
||||
return BleAdvertisementHeader(
|
||||
BleAdvertisementHeader::Version::kV2, /*extended_advertisement=*/false,
|
||||
/*num_slots=*/1, ByteArray(bloom_filter),
|
||||
bleutils::GenerateAdvertisementHash(advertisement_bytes),
|
||||
/*psm=*/BleAdvertisementHeader::kDefaultPsmValue);
|
||||
}
|
||||
|
||||
BleAdvertisementHeader DiscoveredPeripheralTracker::HandleRawGattAdvertisements(
|
||||
const BleAdvertisementHeader& advertisement_header,
|
||||
const std::vector<const ByteArray*>& gatt_advertisement_bytes_list,
|
||||
const std::string& service_uuid) {
|
||||
absl::flat_hash_map<std::string, BleAdvertisement>
|
||||
parsed_gatt_advertisements = ParseRawGattAdvertisements(
|
||||
gatt_advertisement_bytes_list, service_uuid);
|
||||
|
||||
// Update state for each GATT advertisement.
|
||||
BleAdvertisementSet ble_advertisement_set;
|
||||
BleAdvertisementHeader new_advertisement_header = advertisement_header;
|
||||
for (const auto& item : parsed_gatt_advertisements) {
|
||||
const std::string& service_id = item.first;
|
||||
const BleAdvertisement& gatt_advertisement = item.second;
|
||||
BleAdvertisementHeader old_advertisement_header;
|
||||
|
||||
const auto gai_it = gatt_advertisement_infos_.find(gatt_advertisement);
|
||||
if (gai_it != gatt_advertisement_infos_.end()) {
|
||||
old_advertisement_header = gai_it->second.advertisement_header;
|
||||
}
|
||||
|
||||
ble_advertisement_set.insert(gatt_advertisement);
|
||||
|
||||
int new_psm = new_advertisement_header.GetPsm();
|
||||
if (gatt_advertisement.GetPsm() !=
|
||||
BleAdvertisementHeader::kDefaultPsmValue &&
|
||||
gatt_advertisement.GetPsm() != new_psm) {
|
||||
new_psm = gatt_advertisement.GetPsm();
|
||||
// Also set header with new psm value to compare next time.
|
||||
new_advertisement_header.SetPsm(new_psm);
|
||||
}
|
||||
|
||||
// If the device received first fast advertisement is legacy one after then
|
||||
// received extended one, should replace legacy with extended one which has
|
||||
// psm value.
|
||||
if (!old_advertisement_header.IsValid() ||
|
||||
ShouldNotifyForNewPsm(old_advertisement_header.GetPsm(), new_psm)) {
|
||||
// The GATT advertisement has never been seen before. Report it up to the
|
||||
// client.
|
||||
const auto sii_it = service_id_infos_.find(service_id);
|
||||
if (sii_it == service_id_infos_.end()) {
|
||||
NEARBY_LOGS(WARNING) << "HandleRawGattAdvertisements, failed to find "
|
||||
"callback for service_id="
|
||||
<< service_id;
|
||||
continue;
|
||||
}
|
||||
BlePeripheral discovered_ble_peripheral =
|
||||
GenerateBlePeripheral(gatt_advertisement, new_psm);
|
||||
if (discovered_ble_peripheral.IsValid()) {
|
||||
sii_it->second.discovered_peripheral_callback.peripheral_discovered_cb(
|
||||
discovered_ble_peripheral, service_id, gatt_advertisement.GetData(),
|
||||
gatt_advertisement.IsFastAdvertisement());
|
||||
}
|
||||
} else if (old_advertisement_header.GetPsm() !=
|
||||
BleAdvertisementHeader::kDefaultPsmValue &&
|
||||
new_advertisement_header.GetPsm() ==
|
||||
BleAdvertisementHeader::kDefaultPsmValue) {
|
||||
// Don't replace it in advertisementHeaders if this one without PSM but
|
||||
// older has, it's the case that we received extended fast advertisement
|
||||
// after then received legacy one.
|
||||
continue;
|
||||
} else if (ShouldRemoveHeader(old_advertisement_header,
|
||||
new_advertisement_header)) {
|
||||
// The GATT advertisement has been seen on a different advertisement
|
||||
// header. Remove info about the old advertisement header since it's stale
|
||||
// now.
|
||||
advertisement_read_results_.erase(old_advertisement_header);
|
||||
gatt_advertisements_.erase(old_advertisement_header);
|
||||
}
|
||||
|
||||
GattAdvertisementInfo gatt_advertisement_info = {
|
||||
.service_id = service_id,
|
||||
.advertisement_header = new_advertisement_header,
|
||||
.mac_address = {}};
|
||||
gatt_advertisement_infos_.insert_or_assign(
|
||||
gatt_advertisement, std::move(gatt_advertisement_info));
|
||||
}
|
||||
// Insert the list of read GATT advertisements for this advertisement
|
||||
// header.
|
||||
gatt_advertisements_.insert(
|
||||
{new_advertisement_header, std::move(ble_advertisement_set)});
|
||||
return new_advertisement_header;
|
||||
}
|
||||
|
||||
absl::flat_hash_map<std::string, BleAdvertisement>
|
||||
DiscoveredPeripheralTracker::ParseRawGattAdvertisements(
|
||||
const std::vector<const ByteArray*>& gatt_advertisement_bytes_list,
|
||||
const std::string& service_uuid) {
|
||||
absl::flat_hash_map<std::string, BleAdvertisement>
|
||||
parsed_gatt_advertisements = {};
|
||||
|
||||
// TODO(edwinwu): Refactor this big loop as subroutines.
|
||||
for (const auto gatt_advertisement_bytes : gatt_advertisement_bytes_list) {
|
||||
// First, parse the raw bytes into a BleAdvertisement.
|
||||
BleAdvertisement gatt_advertisement(*gatt_advertisement_bytes);
|
||||
if (!gatt_advertisement.IsValid()) {
|
||||
NEARBY_LOGS(INFO) << "Unable to parse raw GATT advertisement:"
|
||||
<< absl::BytesToHexString(
|
||||
gatt_advertisement_bytes->data());
|
||||
continue;
|
||||
}
|
||||
|
||||
// Make sure the advertisement belongs to a service ID we're tracking.
|
||||
for (const auto& item : service_id_infos_) {
|
||||
const std::string& service_id = item.first;
|
||||
// If we already found a higher version advertisement for this service ID,
|
||||
// there's no point in comparing this advertisement against it.
|
||||
const auto pga_it = parsed_gatt_advertisements.find(service_id);
|
||||
if (pga_it != parsed_gatt_advertisements.end()) {
|
||||
if (pga_it->second.GetVersion() > gatt_advertisement.GetVersion()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// service_id_hash is null here (mediums advertisement) because we already
|
||||
// have a UUID in the fast advertisement.
|
||||
if (gatt_advertisement.IsFastAdvertisement() && !service_uuid.empty()) {
|
||||
const auto sii_it = service_id_infos_.find(service_id);
|
||||
if (sii_it != service_id_infos_.end()) {
|
||||
if (sii_it->second.fast_advertisement_service_uuid == service_uuid) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "This GATT advertisement:"
|
||||
<< absl::BytesToHexString(gatt_advertisement_bytes->data())
|
||||
<< " is a fast advertisement and matched UUID=" << service_uuid
|
||||
<< " in a map with service_id=" << service_id;
|
||||
parsed_gatt_advertisements.insert({service_id, gatt_advertisement});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Map the service ID to the advertisement if the service_id_hash match.
|
||||
if (bleutils::GenerateServiceIdHash(service_id) ==
|
||||
gatt_advertisement.GetServiceIdHash()) {
|
||||
NEARBY_LOGS(INFO) << "Matched service_id=" << service_id
|
||||
<< " to GATT advertisement="
|
||||
<< absl::BytesToHexString(
|
||||
gatt_advertisement_bytes->data());
|
||||
parsed_gatt_advertisements.insert({service_id, gatt_advertisement});
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return parsed_gatt_advertisements;
|
||||
}
|
||||
|
||||
bool DiscoveredPeripheralTracker::ShouldNotifyForNewPsm(int old_psm,
|
||||
int new_psm) const {
|
||||
return new_psm != BleAdvertisementHeader::kDefaultPsmValue &&
|
||||
old_psm != new_psm;
|
||||
}
|
||||
|
||||
bool DiscoveredPeripheralTracker::ShouldRemoveHeader(
|
||||
const BleAdvertisementHeader& old_advertisement_header,
|
||||
const BleAdvertisementHeader& new_advertisement_header) {
|
||||
if (old_advertisement_header == new_advertisement_header) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// We received the physical from legacy advertisement and create a mock one
|
||||
// when receive a regular advertisement from extended advertisements. Avoid to
|
||||
// remove the physical header for the new incoming regular extended
|
||||
// advertisement. Otherwise, it make the device to fetch advertisement when
|
||||
// received a physical header again.
|
||||
// TODO(b/213835576) : Implement API to fetch the support for extended
|
||||
// advertisement from platform impl.
|
||||
bool is_extended_advertisement_available = false;
|
||||
if (is_extended_advertisement_available) {
|
||||
if (!IsDummyAdvertisementHeader(old_advertisement_header) &&
|
||||
IsDummyAdvertisementHeader(new_advertisement_header)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiscoveredPeripheralTracker::IsDummyAdvertisementHeader(
|
||||
const BleAdvertisementHeader& advertisement_header) {
|
||||
// Do not count advertisementHash and psm value here, for L2CAP feature, the
|
||||
// regular advertisement has different value, it will include PSM value if
|
||||
// received it from extended advertisement protocol and it will not has PSM
|
||||
// value if it fetcted from GATT connection.
|
||||
BloomFilter bloom_filter(
|
||||
std::make_unique<BitSetImpl<
|
||||
BleAdvertisementHeader::kServiceIdBloomFilterByteLength>>());
|
||||
return advertisement_header.GetVersion() ==
|
||||
BleAdvertisementHeader::Version::kV2 &&
|
||||
advertisement_header.GetNumSlots() == 1 &&
|
||||
advertisement_header.GetServiceIdBloomFilter() ==
|
||||
ByteArray(bloom_filter);
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::HandleAdvertisementHeader(
|
||||
const location::nearby::api::ble_v2::BleAdvertisementData&
|
||||
advertisement_data,
|
||||
BleV2Peripheral& peripheral, AdvertisementFetcher advertisement_fetcher) {
|
||||
// Attempt to parse the advertisement header.
|
||||
BleAdvertisementHeader advertisement_header(
|
||||
ExtractAdvertisementHeaderBytes(advertisement_data));
|
||||
if (!advertisement_header.IsValid()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Failed to deserialize BLE advertisement header. Ignoring.";
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if the advertisement header contains a service ID we're tracking.
|
||||
if (!IsInterestingAdvertisementHeader(advertisement_header)) {
|
||||
NEARBY_LOGS(VERBOSE) << "Ignoring BLE advertisement header="
|
||||
<< absl::BytesToHexString(
|
||||
ByteArray(advertisement_header).data())
|
||||
<< " because it does not contain any service IDs "
|
||||
"we're interested in.";
|
||||
return;
|
||||
}
|
||||
|
||||
// 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.
|
||||
std::vector<const ByteArray*> gatt_advertisement_bytes_list =
|
||||
FetchRawAdvertisements(advertisement_header,
|
||||
/*mutated=*/peripheral,
|
||||
std::move(advertisement_fetcher));
|
||||
if (!gatt_advertisement_bytes_list.empty()) {
|
||||
HandleRawGattAdvertisements(advertisement_header,
|
||||
gatt_advertisement_bytes_list, "");
|
||||
}
|
||||
}
|
||||
|
||||
// Regardless of whether or not we read a new GATT advertisement, the maps
|
||||
// should now be up-to-date. With this information, do some general
|
||||
// housekeeping.
|
||||
UpdateCommonStateForFoundBleAdvertisement(advertisement_header,
|
||||
/*mac_address=*/peripheral.GetId());
|
||||
}
|
||||
|
||||
ByteArray DiscoveredPeripheralTracker::ExtractAdvertisementHeaderBytes(
|
||||
const location::nearby::api::ble_v2::BleAdvertisementData&
|
||||
advertisement_data) {
|
||||
const auto it =
|
||||
advertisement_data.service_data.find(bleutils::kCopresenceServiceUuid);
|
||||
if (it != advertisement_data.service_data.end()) {
|
||||
const ByteArray& advertisement_header_bytes = it->second;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
return advertisement_header_bytes;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool DiscoveredPeripheralTracker::IsInterestingAdvertisementHeader(
|
||||
const BleAdvertisementHeader& advertisement_header) {
|
||||
BloomFilter bloom_filter(
|
||||
std::make_unique<BitSetImpl<
|
||||
BleAdvertisementHeader::kServiceIdBloomFilterByteLength>>(),
|
||||
advertisement_header.GetServiceIdBloomFilter());
|
||||
|
||||
for (const auto& item : service_id_infos_) {
|
||||
const std::string& service_id = item.first;
|
||||
if (bloom_filter.PossiblyContains(service_id)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiscoveredPeripheralTracker::ShouldReadRawAdvertisementFromServer(
|
||||
const BleAdvertisementHeader& advertisement_header) {
|
||||
// Check if we have never seen this header. New headers should always be
|
||||
// read.
|
||||
ByteArray advertisement_header_bytes(advertisement_header);
|
||||
const auto it = advertisement_read_results_.find(advertisement_header);
|
||||
if (it == advertisement_read_results_.end()) {
|
||||
NEARBY_LOGS(INFO) << "Received advertisement header="
|
||||
<< absl::BytesToHexString(
|
||||
advertisement_header_bytes.data())
|
||||
<< ", but we have never seen it before. Caller should "
|
||||
"try reading its GATT advertisement.";
|
||||
return true;
|
||||
}
|
||||
|
||||
// Extract the last read result for this particular header.
|
||||
AdvertisementReadResult* advertisement_read_result = it->second.get();
|
||||
|
||||
// Now evaluate if we should retry reading.
|
||||
switch (advertisement_read_result->EvaluateRetryStatus()) {
|
||||
case AdvertisementReadResult::RetryStatus::kRetry:
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Received advertisement header="
|
||||
<< absl::BytesToHexString(advertisement_header_bytes.data())
|
||||
<< ". Caller should retry reading its GATT advertisement.";
|
||||
return true;
|
||||
case AdvertisementReadResult::RetryStatus::kPreviouslySucceeded:
|
||||
NEARBY_LOGS(INFO) << "Received advertisement header="
|
||||
<< absl::BytesToHexString(
|
||||
advertisement_header_bytes.data())
|
||||
<< ", but we have already read its GATT advertisement.";
|
||||
return false;
|
||||
case AdvertisementReadResult::RetryStatus::kTooSoon:
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Received advertisement header="
|
||||
<< absl::BytesToHexString(advertisement_header_bytes.data())
|
||||
<< ", but we have recently failed to read its GATT advertisement.";
|
||||
return false;
|
||||
case AdvertisementReadResult::RetryStatus::kUnknown:
|
||||
// Fall through.
|
||||
break;
|
||||
}
|
||||
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "Received advertisement header="
|
||||
<< absl::BytesToHexString(advertisement_header_bytes.data())
|
||||
<< ", but we do not know whether or not to retry reading "
|
||||
"its GATT advertisement. Caller should retry to be safe.";
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<const ByteArray*>
|
||||
DiscoveredPeripheralTracker::FetchRawAdvertisements(
|
||||
const BleAdvertisementHeader& advertisement_header,
|
||||
BleV2Peripheral& peripheral, AdvertisementFetcher advertisement_fetcher) {
|
||||
// Fetch the raw GATT advertisements and store the results.
|
||||
AdvertisementReadResult* advertisement_read_result = nullptr;
|
||||
const auto it = advertisement_read_results_.find(advertisement_header);
|
||||
if (it != advertisement_read_results_.end()) {
|
||||
advertisement_read_result = it->second.get();
|
||||
}
|
||||
|
||||
std::vector<std::string> service_ids;
|
||||
std::transform(service_id_infos_.begin(), service_id_infos_.end(),
|
||||
std::back_inserter(service_ids),
|
||||
[](auto& kv) { return kv.first; });
|
||||
std::unique_ptr<AdvertisementReadResult> read_result =
|
||||
advertisement_fetcher.fetch_advertisements(
|
||||
advertisement_header.GetNumSlots(), advertisement_header.GetPsm(),
|
||||
service_ids, advertisement_read_result,
|
||||
/*mutated=*/peripheral);
|
||||
if (!read_result) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto iterator_and_result_pair = advertisement_read_results_.insert_or_assign(
|
||||
advertisement_header, std::move(read_result));
|
||||
// Take those results and return all the advertisements we were able to read.
|
||||
std::vector<const ByteArray*> advertisement_bytes_list;
|
||||
if (iterator_and_result_pair.second) {
|
||||
advertisement_bytes_list =
|
||||
iterator_and_result_pair.first->second->GetAdvertisements();
|
||||
}
|
||||
return advertisement_bytes_list;
|
||||
}
|
||||
|
||||
void DiscoveredPeripheralTracker::UpdateCommonStateForFoundBleAdvertisement(
|
||||
const BleAdvertisementHeader& advertisement_header,
|
||||
const std::string& mac_address) {
|
||||
const auto ga_it = gatt_advertisements_.find(advertisement_header);
|
||||
if (ga_it == gatt_advertisements_.end()) {
|
||||
NEARBY_LOGS(INFO)
|
||||
<< "No GATT advertisements found for advertisement header="
|
||||
<< absl::BytesToHexString(ByteArray(advertisement_header).data());
|
||||
return;
|
||||
}
|
||||
|
||||
BleAdvertisementSet saved_gatt_advertisements = ga_it->second;
|
||||
for (const auto& gatt_advertisement : saved_gatt_advertisements) {
|
||||
const auto gai_it = gatt_advertisement_infos_.find(gatt_advertisement);
|
||||
if (gai_it == gatt_advertisement_infos_.end()) {
|
||||
continue;
|
||||
}
|
||||
GattAdvertisementInfo& gatt_advertisement_info = gai_it->second;
|
||||
const auto sii_it =
|
||||
service_id_infos_.find(gatt_advertisement_info.service_id);
|
||||
if (sii_it != service_id_infos_.end()) {
|
||||
auto* lost_entity_tracker = sii_it->second.lost_entity_tracker.get();
|
||||
if (!lost_entity_tracker) {
|
||||
NEARBY_LOGS(WARNING) << "UpdateCommonStateForFoundBleAdvertisement, "
|
||||
"failed to find entity "
|
||||
"tracker for service_id="
|
||||
<< gatt_advertisement_info.service_id;
|
||||
continue;
|
||||
}
|
||||
lost_entity_tracker->RecordFoundEntity(gatt_advertisement);
|
||||
|
||||
gatt_advertisement_info.mac_address = mac_address;
|
||||
gatt_advertisement_infos_[gatt_advertisement] = gatt_advertisement_info;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BlePeripheral DiscoveredPeripheralTracker::GenerateBlePeripheral(
|
||||
const BleAdvertisement& gatt_advertisement, int psm) {
|
||||
return BlePeripheral(ByteArray(gatt_advertisement), psm);
|
||||
}
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
@@ -0,0 +1,282 @@
|
||||
// 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.
|
||||
|
||||
#ifndef CORE_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_TRACKER_H_
|
||||
#define CORE_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_TRACKER_H_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "connections/implementation/mediums//lost_entity_tracker.h"
|
||||
#include "connections/implementation/mediums/ble_v2/advertisement_read_result.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_advertisement.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h"
|
||||
#include "connections/implementation/mediums/ble_v2/discovered_peripheral_callback.h"
|
||||
#include "connections/implementation/mediums/lost_entity_tracker.h"
|
||||
#include "internal/platform/bluetooth_adapter.h"
|
||||
#include "internal/platform/byte_array.h"
|
||||
#include "internal/platform/mutex.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
// Manages all discovered peripheral logic for {@link BluetoothLowEnergy}. This
|
||||
// includes tracking found peripherals, lost peripherals, and MAC addresses
|
||||
// associated with those peripherals.
|
||||
//
|
||||
// See go/ble-on-lost for more information. It includes the algorithms used to
|
||||
// compute found and lost peripherals.
|
||||
class DiscoveredPeripheralTracker {
|
||||
public:
|
||||
// GATT advertisement fetcher.
|
||||
struct AdvertisementFetcher {
|
||||
// Fetches relevant GATT advertisements for the peripheral found in {@link
|
||||
// DiscoveredPeripheralTracker#ProcessFoundBleAdvertisement(}.
|
||||
std::function<std::unique_ptr<AdvertisementReadResult>(
|
||||
int num_slots, int psm,
|
||||
const std::vector<std::string>& interesting_service_ids,
|
||||
AdvertisementReadResult* advertisement_read_result,
|
||||
BleV2Peripheral& peripheral)>
|
||||
fetch_advertisements = [](int, int, const std::vector<std::string>&,
|
||||
AdvertisementReadResult*, BleV2Peripheral&)
|
||||
-> std::unique_ptr<AdvertisementReadResult> { return nullptr; };
|
||||
};
|
||||
|
||||
// Starts tracking discoveries for a particular service Id.
|
||||
//
|
||||
// service_id - The service ID to track.
|
||||
// discovered_peripheral_callback - The callback to invoke for discovery
|
||||
// events.
|
||||
// fast_advertisement_service_uuid - The service UUID to look for fast
|
||||
// advertisements on.
|
||||
// Note: fast_advertisement_service_uuid can be empty string to indicate
|
||||
// that `fast_advertisement_service_uuid` will be ignored for regular
|
||||
// advertisement.
|
||||
void StartTracking(
|
||||
const std::string& service_id,
|
||||
const DiscoveredPeripheralCallback& discovered_peripheral_callback,
|
||||
const std::string& fast_advertisement_service_uuid)
|
||||
ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Stops tracking discoveries for a particular service Id.
|
||||
void StopTracking(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Processes a found BLE advertisement.
|
||||
//
|
||||
// peripheral - To access the status of the operation. The ownership
|
||||
// is moved into 'discovered_peripheral_tracker' object.
|
||||
// advertisement_data - The found BLE advertisement data.
|
||||
// advertisement_fetcher - The advertisement fetcher callback for regular
|
||||
// advertisement if the advertisement header is been
|
||||
// processed.
|
||||
void ProcessFoundBleAdvertisement(
|
||||
BleV2Peripheral peripheral,
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data,
|
||||
AdvertisementFetcher advertisement_fetcher) ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
// Processes the set of lost GATT advertisements and notifies the client of
|
||||
// any lost peripherals.
|
||||
void ProcessLostGattAdvertisements() ABSL_LOCKS_EXCLUDED(mutex_);
|
||||
|
||||
private:
|
||||
using BleAdvertisementSet = absl::flat_hash_set<BleAdvertisement>;
|
||||
|
||||
// A container to hold callback or other informations that bring from BLE
|
||||
// medium when `StartTracking`.
|
||||
struct ServiceIdInfo {
|
||||
// Tracks what service IDs are currently active and gives us client
|
||||
// callbacks to call.
|
||||
DiscoveredPeripheralCallback discovered_peripheral_callback;
|
||||
|
||||
// Used to periodically compute lost GATT advertisements.
|
||||
std::unique_ptr<LostEntityTracker<BleAdvertisement>> lost_entity_tracker;
|
||||
|
||||
// Used to check for fast advertisements delivered through BLE advertisement
|
||||
// service data, under the given UUID.
|
||||
std::string fast_advertisement_service_uuid;
|
||||
};
|
||||
|
||||
// A container to hold the related informations for a GATT advertisement.
|
||||
struct GattAdvertisementInfo {
|
||||
// GATT advertisement to the service ID it's associated with. Tracks what
|
||||
// GATT advertisements are currently active. Used to determine which
|
||||
// LostEntityTracker to invoke when advertisements are rediscovered.
|
||||
std::string service_id;
|
||||
|
||||
// Used to efficiently find advertisement headers to delete when GATT
|
||||
// advertisements are updated. This is a reverse map of
|
||||
// gatt_advertisements_.
|
||||
BleAdvertisementHeader advertisement_header;
|
||||
|
||||
// Used when we need to make a socket connection based off of the GATT
|
||||
// advertisement alone. Entries are modified every time a GATT
|
||||
// advertisement's advertisement header is seen.
|
||||
std::string mac_address;
|
||||
};
|
||||
|
||||
// Clears stale data from any previous sessions.
|
||||
void ClearDataForServiceId(const std::string& service_id)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Clears out all data related to the provided GATT advertisement. This
|
||||
// includes:
|
||||
// 1. Removing the corresponding GATT advertisement from
|
||||
// gatt_advertisement_infos_.
|
||||
// 2. Removing the corresponding advertisement header from
|
||||
// advertisement_read_results.
|
||||
// 3. Removing the corresponding advertisement header from
|
||||
// gatt_advertisements_, only if there are no remaining GATT
|
||||
// advertisements related to that header.
|
||||
void ClearGattAdvertisement(const BleAdvertisement& gatt_advertisement)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Handles the legacy fast advertisement or the extended fast/regular
|
||||
// advertisement.
|
||||
void HandleAdvertisement(
|
||||
const BleV2Peripheral& peripheral,
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Extracts the advertisement byte array from `AdvertisementData`.
|
||||
ByteArray ExtractInterestingAdvertisementBytes(
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Creates an advertisement header that's purely a hash of the fast/regular
|
||||
// advertisement, since they come with no header.
|
||||
BleAdvertisementHeader CreateAdvertisementHeader(
|
||||
const ByteArray& advertisement_bytes);
|
||||
|
||||
// Returns BleAdvertisementHeader, it may be replaced if the header is mock
|
||||
// and there's a psm value in advertisement.
|
||||
BleAdvertisementHeader HandleRawGattAdvertisements(
|
||||
const BleAdvertisementHeader& advertisement_header,
|
||||
const std::vector<const ByteArray*>& gatt_advertisement_bytes_list,
|
||||
const std::string& service_uuid) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns a map of service IDs to GATT advertisements who belong to a tracked
|
||||
// service ID.
|
||||
absl::flat_hash_map<std::string, BleAdvertisement> ParseRawGattAdvertisements(
|
||||
const std::vector<const ByteArray*>& gatt_advertisement_bytes_list,
|
||||
const std::string& service_uuid) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if `new_psm` is not default value and different with
|
||||
// `old_psm`.
|
||||
bool ShouldNotifyForNewPsm(int old_psm, int new_psm) const;
|
||||
|
||||
// Returns true if two headers are not the same.
|
||||
bool ShouldRemoveHeader(
|
||||
const BleAdvertisementHeader& old_advertisement_header,
|
||||
const BleAdvertisementHeader& new_advertisement_header);
|
||||
|
||||
// Returns true if it is a faked advertisement header which is purely a hash
|
||||
// of the fast/regular advertisement, since they come with no header.
|
||||
bool IsDummyAdvertisementHeader(
|
||||
const BleAdvertisementHeader& advertisement_header);
|
||||
|
||||
// Handles the advertisement header for regular advertisement.
|
||||
void HandleAdvertisementHeader(
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data,
|
||||
BleV2Peripheral& peripheral, AdvertisementFetcher advertisement_fetcher)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Extracts the advertisement header byte array from `AdvertisementData`.
|
||||
ByteArray ExtractAdvertisementHeaderBytes(
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if the advertisement header contains a service ID we're
|
||||
// tracking.
|
||||
bool IsInterestingAdvertisementHeader(
|
||||
const BleAdvertisementHeader& advertisement_header)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Returns true if the `advertisement_header` is allowd to callbck to fecth
|
||||
// advertisement.
|
||||
bool ShouldReadRawAdvertisementFromServer(
|
||||
const BleAdvertisementHeader& advertisement_header)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Fetches advertsiement from BLE medium if advertisement header is read in
|
||||
// AdvertisementData.
|
||||
//
|
||||
// advertisement_fetcher : a fetcher passed from BLE medium to read the
|
||||
// advertisemeent from BLE characteristics by GATT server.
|
||||
std::vector<const ByteArray*> FetchRawAdvertisements(
|
||||
const BleAdvertisementHeader& advertisement_header,
|
||||
BleV2Peripheral& peripheral, AdvertisementFetcher advertisement_fetcher)
|
||||
ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Updates `gatt_advertisement_infos_` map no matter whether we read a new
|
||||
// GATT advertisement by the input `advertisement_header` and 'mac_address`.
|
||||
void UpdateCommonStateForFoundBleAdvertisement(
|
||||
const BleAdvertisementHeader& advertisement_header,
|
||||
const std::string& mac_address) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Creates BlePeripheral based on the input of advertisement and psm value.
|
||||
BlePeripheral GenerateBlePeripheral(
|
||||
const BleAdvertisement& gatt_advertisement,
|
||||
int psm = BleAdvertisementHeader::kDefaultPsmValue);
|
||||
|
||||
Mutex mutex_;
|
||||
|
||||
// ------------ SERVICE ID MAPS ------------
|
||||
// Entries in these maps all follow the same lifecycle. Entries are added in
|
||||
// StartTracking, and removed in StopTracking.
|
||||
absl::flat_hash_map<std::string, ServiceIdInfo> service_id_infos_
|
||||
ABSL_GUARDED_BY(mutex_);
|
||||
|
||||
// ------------ ADVERTISEMENT HEADER MAPS ------------
|
||||
// Maps advertisement headers to AdvertisementReadResult. Tells us when to
|
||||
// retry reading a GATT advertisement. If no entry exists for a particular
|
||||
// header, we should try reading a GATT advertisement. Entries are added
|
||||
// whenever a GATT advertisement read is attempted, and removed when GATT
|
||||
// advertisements are lost. Entries are also removed whenever
|
||||
// gattAdvertisements removes its entry.
|
||||
//
|
||||
// The map is also cleared whenever StartTracking is called, due to client
|
||||
// changes. For example, say clients A and B start scanning and discover
|
||||
// advertisements A and B (for both clients) on advertisement header 1. Then,
|
||||
// A restarts scanning, causing us to clear stale advertisement A. However,
|
||||
// since B was still scanning, we don't remove advertisement header 1 from the
|
||||
// map. This causes us to never re-read advertisement A.
|
||||
absl::flat_hash_map<BleAdvertisementHeader,
|
||||
std::unique_ptr<AdvertisementReadResult>>
|
||||
advertisement_read_results_ ABSL_GUARDED_BY(mutex_);
|
||||
|
||||
// Maps advertisement headers to a set of GATT advertisements from a single
|
||||
// peripheral. Used to retrieve GATT advertisements that we need to reprocess
|
||||
// every time a header is seen. Entries are added when GATT advertisements are
|
||||
// read, removed when all associated GATT advertisements are lost or become
|
||||
// stale, and replaced when the advertisement header is updated for a single
|
||||
// remote peripheral.
|
||||
absl::flat_hash_map<BleAdvertisementHeader, BleAdvertisementSet>
|
||||
gatt_advertisements_ ABSL_GUARDED_BY(mutex_);
|
||||
|
||||
// ------------ GATT ADVERTISEMENT MAPS ------------
|
||||
// Entries are added when Gatt advertisements are read, and removed when Gatt
|
||||
// advertisements are lost or become stale.
|
||||
absl::flat_hash_map<BleAdvertisement, GattAdvertisementInfo>
|
||||
gatt_advertisement_infos_ ABSL_GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
|
||||
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_DISCOVERED_PERIPHERAL_TRACKER_H_
|
||||
@@ -0,0 +1,940 @@
|
||||
// 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_v2/discovered_peripheral_tracker.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "connections/implementation/mediums/ble_v2/ble_utils.h"
|
||||
#include "connections/implementation/mediums/ble_v2/bloom_filter.h"
|
||||
#include "internal/platform/bluetooth_adapter.h"
|
||||
#include "internal/platform/count_down_latch.h"
|
||||
#include "internal/platform/mutex.h"
|
||||
#include "internal/platform/mutex_lock.h"
|
||||
|
||||
namespace location {
|
||||
namespace nearby {
|
||||
namespace connections {
|
||||
namespace mediums {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000);
|
||||
constexpr absl::string_view kCopresenceServiceUuid =
|
||||
"0000FEF3-0000-1000-8000-00805F9B34FB";
|
||||
constexpr absl::string_view kFastAdvertisementServiceUuid =
|
||||
"0000FE2C-0000-1000-8000-00805F9B34FB";
|
||||
constexpr absl::string_view kServiceIdA = "A";
|
||||
constexpr absl::string_view kServiceIdB = "B";
|
||||
constexpr absl::string_view kMacAddress1 = "4C:8B:1D:CE:BA:D1";
|
||||
constexpr absl::string_view kData = "\x04\x02\x00";
|
||||
constexpr absl::string_view kData2 = "\x07\x00\x07";
|
||||
constexpr absl::string_view kDeviceToken = "\x04\x20";
|
||||
|
||||
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 CreateBleAdvertisementHeader(const ByteArray& advertisement_hash,
|
||||
int psm,
|
||||
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=*/false,
|
||||
/*num_slots=*/service_ids.size(),
|
||||
ByteArray(service_id_bloom_filter),
|
||||
advertisement_hash, psm));
|
||||
}
|
||||
|
||||
ByteArray CreateBleAdvertisementHeader(const ByteArray& advertisement_hash,
|
||||
std::vector<std::string>& service_ids) {
|
||||
return CreateBleAdvertisementHeader(advertisement_hash,
|
||||
BleAdvertisementHeader::kDefaultPsmValue,
|
||||
service_ids);
|
||||
}
|
||||
|
||||
ByteArray GenerateRandomAdvertisementHash() {
|
||||
ByteArray random_advertisement_hash = Utils::GenerateRandomBytes(
|
||||
BleAdvertisementHeader::kAdvertisementHashByteLength);
|
||||
|
||||
return random_advertisement_hash;
|
||||
}
|
||||
|
||||
// A stub BlePeripheral implementation.
|
||||
class BlePeripheralStub : public api::ble_v2::BlePeripheral {
|
||||
public:
|
||||
explicit BlePeripheralStub(absl::string_view mac_address) {
|
||||
mac_address_ = mac_address;
|
||||
}
|
||||
|
||||
std::string GetId() const override { return mac_address_; }
|
||||
|
||||
private:
|
||||
std::string mac_address_;
|
||||
};
|
||||
|
||||
class DiscoveredPeripheralTrackerTest : public testing::Test {
|
||||
public:
|
||||
void SetUp() override {}
|
||||
|
||||
BleV2Peripheral CreateBlePeripheral(absl::string_view mac_address) {
|
||||
ble_peripheral_ = std::make_unique<BlePeripheralStub>(mac_address);
|
||||
return BleV2Peripheral(ble_peripheral_.get());
|
||||
}
|
||||
|
||||
// Simulates to see a fast advertisement.
|
||||
void FindFastAdvertisement(
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data,
|
||||
const std::vector<ByteArray>& advertisement_bytes_list,
|
||||
CountDownLatch& fetch_latch) {
|
||||
BleV2Peripheral peripheral = CreateBlePeripheral(kMacAddress1);
|
||||
|
||||
discovered_peripheral_tracker_.ProcessFoundBleAdvertisement(
|
||||
peripheral, advertisement_data,
|
||||
GetAdvertisementFetcher(fetch_latch, advertisement_bytes_list));
|
||||
}
|
||||
|
||||
// Simulates to see a regular advertisement.
|
||||
void FindAdvertisement(
|
||||
const api::ble_v2::BleAdvertisementData& advertisement_data,
|
||||
const std::vector<ByteArray>& advertisement_bytes_list,
|
||||
CountDownLatch& fetch_latch) {
|
||||
BleV2Peripheral peripheral = CreateBlePeripheral(kMacAddress1);
|
||||
|
||||
discovered_peripheral_tracker_.ProcessFoundBleAdvertisement(
|
||||
peripheral, advertisement_data,
|
||||
GetAdvertisementFetcher(fetch_latch, advertisement_bytes_list));
|
||||
}
|
||||
|
||||
int GetFetchAdvertisementCallbackCount() const {
|
||||
MutexLock lock(&mutex_);
|
||||
return fetch_count_;
|
||||
}
|
||||
|
||||
protected:
|
||||
// A stub Advertisement fetcher.
|
||||
DiscoveredPeripheralTracker::AdvertisementFetcher GetAdvertisementFetcher(
|
||||
CountDownLatch& fetch_latch,
|
||||
const std::vector<ByteArray>& advertisement_bytes_list) {
|
||||
return {
|
||||
.fetch_advertisements =
|
||||
[this, &fetch_latch, &advertisement_bytes_list](
|
||||
int num_slots, int psm,
|
||||
const std::vector<std::string>& interesting_service_ids,
|
||||
AdvertisementReadResult* arr, BleV2Peripheral& peripheral)
|
||||
-> std::unique_ptr<AdvertisementReadResult> {
|
||||
MutexLock lock(&mutex_);
|
||||
fetch_count_++;
|
||||
auto advertisement_read_result =
|
||||
std::make_unique<AdvertisementReadResult>();
|
||||
int slot = 0;
|
||||
for (const auto& advertisement_bytes : advertisement_bytes_list) {
|
||||
advertisement_read_result->AddAdvertisement(slot++,
|
||||
advertisement_bytes);
|
||||
}
|
||||
advertisement_read_result->RecordLastReadStatus(/*isSuccess=*/true);
|
||||
fetch_latch.CountDown();
|
||||
return advertisement_read_result;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
mutable Mutex mutex_;
|
||||
int fetch_count_ ABSL_GUARDED_BY(mutex_) = 0;
|
||||
std::unique_ptr<BlePeripheralStub> ble_peripheral_;
|
||||
DiscoveredPeripheralTracker discovered_peripheral_tracker_;
|
||||
};
|
||||
|
||||
TEST_F(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),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundFastAdvertisementDuplicateAdvertisements) {
|
||||
ByteArray fast_advertisement_bytes = CreateFastBleAdvertisement(
|
||||
ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken)));
|
||||
CountDownLatch found_latch(3);
|
||||
CountDownLatch fetch_latch(3);
|
||||
int callback_times = 0;
|
||||
|
||||
discovered_peripheral_tracker_.StartTracking(
|
||||
std::string(kServiceIdA),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(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),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
"0000FE2C-0000-1000-8000-00805F9B34FC");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundFastAdvertisementAndGattAdvertisementSimultaneously) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
discovered_peripheral_tracker_.StartTracking(
|
||||
std::string(kServiceIdB),
|
||||
{
|
||||
.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_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(kCopresenceServiceUuid), advertisement_header_bytes});
|
||||
}
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundBleAdvertisementPeripheralDiscovered) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundBleAdvertisementLegacyPeripheralDiscovered) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.peripheral_discovered_cb =
|
||||
[&found_latch](
|
||||
BlePeripheral& peripheral, const std::string& service_id,
|
||||
const ByteArray& advertisement_bytes,
|
||||
bool fast_advertisement) { found_latch.CountDown(); },
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundBleAdvertisementFavorLatestPeripheral) {
|
||||
std::vector<std::string> 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);
|
||||
int callback_times = 0;
|
||||
|
||||
discovered_peripheral_tracker_.StartTracking(
|
||||
std::string(kServiceIdA),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_EQ(callback_times, 1);
|
||||
EXPECT_TRUE(found_latch.Await(kWaitDuration).result());
|
||||
EXPECT_EQ(GetFetchAdvertisementCallbackCount(), 1);
|
||||
}
|
||||
|
||||
TEST_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundBleAdvertisementDuplicateAdvertisements) {
|
||||
std::vector<std::string> 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);
|
||||
int callback_times = 0;
|
||||
|
||||
discovered_peripheral_tracker_.StartTracking(
|
||||
std::string(kServiceIdA),
|
||||
{
|
||||
.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();
|
||||
},
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundBleAdvertisementUntrackedServiceId) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.peripheral_discovered_cb =
|
||||
[&found_latch](
|
||||
BlePeripheral& peripheral, const std::string& service_id,
|
||||
const ByteArray& advertisement_bytes,
|
||||
bool fast_advertisement) { found_latch.CountDown(); },
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
LostPeripheralForFastAdvertisementLost) {
|
||||
std::vector<std::string> 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);
|
||||
int lost_callback_times = 0;
|
||||
|
||||
discovered_peripheral_tracker_.StartTracking(
|
||||
std::string(kServiceIdA),
|
||||
{
|
||||
.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) {
|
||||
lost_callback_times++;
|
||||
lost_latch.CountDown();
|
||||
},
|
||||
},
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(kCopresenceServiceUuid), advertisement_header_bytes});
|
||||
}
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
FoundFastAdvertisementAlmostLostPeripheral) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.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) {
|
||||
lost_latch.CountDown();
|
||||
},
|
||||
},
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(kCopresenceServiceUuid), advertisement_header_bytes});
|
||||
}
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest, LostPeripheralForAdvertisementLost) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.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) {
|
||||
lost_latch.CountDown();
|
||||
},
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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 eripheral 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_F(DiscoveredPeripheralTrackerTest,
|
||||
LostPeripheralForFastAndGattAdvertisementLost) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.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) {
|
||||
lost_latch_a.CountDown();
|
||||
},
|
||||
},
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
discovered_peripheral_tracker_.StartTracking(
|
||||
std::string(kServiceIdB),
|
||||
{
|
||||
.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) {
|
||||
lost_latch_b.CountDown();
|
||||
},
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(kCopresenceServiceUuid), advertisement_header_bytes});
|
||||
}
|
||||
if (!fast_advertisement_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kFastAdvertisementServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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_F(DiscoveredPeripheralTrackerTest,
|
||||
LostPeripheralNotCallbackForUntrackedServiceId) {
|
||||
std::vector<std::string> 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),
|
||||
{
|
||||
.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) {
|
||||
lost_latch.CountDown();
|
||||
},
|
||||
},
|
||||
"");
|
||||
|
||||
api::ble_v2::BleAdvertisementData advertisement_data;
|
||||
if (!advertisement_header_bytes.Empty()) {
|
||||
advertisement_data.service_uuids.insert(
|
||||
std::string(kCopresenceServiceUuid));
|
||||
advertisement_data.service_data.insert(
|
||||
{std::string(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());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace mediums
|
||||
} // namespace connections
|
||||
} // namespace nearby
|
||||
} // namespace location
|
||||
Reference in New Issue
Block a user