[BLE Refactor] Tune BleAdvertisement, BleAdvertisementHeader, BlePeripheral, AdvertisementReadResult, BleUtils.

PiperOrigin-RevId: 439365106
This commit is contained in:
edwinwu
2022-04-04 11:19:24 -07:00
committed by Copybara-Service
parent 7eded6c621
commit 0747c0a349
21 changed files with 411 additions and 135 deletions
+5 -3
View File
@@ -21,7 +21,6 @@ cc_library(
"bluetooth_classic.cc",
"bluetooth_radio.cc",
"mediums.cc",
"uuid.cc",
"webrtc_stub.cc",
"wifi_lan.cc",
],
@@ -30,9 +29,7 @@ cc_library(
"ble_v2.h",
"bluetooth_classic.h",
"bluetooth_radio.h",
"lost_entity_tracker.h",
"mediums.h",
"uuid.h",
"webrtc_stub.h",
"wifi_lan.h",
],
@@ -64,10 +61,13 @@ cc_library(
name = "utils",
srcs = [
"utils.cc",
"uuid.cc",
"webrtc_peer_id.cc",
],
hdrs = [
"lost_entity_tracker.h",
"utils.h",
"uuid.h",
"webrtc_peer_id.h",
"webrtc_socket_stub.h",
],
@@ -75,12 +75,14 @@ cc_library(
visibility = [
"//connections/implementation:__pkg__",
"//connections/implementation/mediums:__pkg__",
"//connections/implementation/mediums/ble_v2:__subpackages__",
"//connections/implementation/mediums/webrtc:__pkg__",
],
deps = [
"//connections/implementation/proto:offline_wire_formats_cc_proto",
"//internal/platform:base",
"//internal/platform:types",
"@com_google_absl//absl/container:flat_hash_set",
"@com_google_absl//absl/strings",
],
)
+28 -63
View File
@@ -22,6 +22,7 @@
#include "absl/strings/str_cat.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 "connections/implementation/mediums/bluetooth_radio.h"
#include "connections/implementation/mediums/utils.h"
@@ -29,7 +30,6 @@
#include "internal/platform/byte_array.h"
#include "internal/platform/logging.h"
#include "internal/platform/mutex_lock.h"
#include "internal/platform/prng.h"
namespace location {
namespace nearby {
@@ -43,33 +43,6 @@ using ::location::nearby::api::ble_v2::PowerMode;
constexpr int kMaxAdvertisementLength = 512;
constexpr int kDummyServiceIdLength = 128;
constexpr absl::string_view kCopresenceServiceUuid =
"0000FEF3-0000-1000-8000-00805F9B34FB";
// These two values make up the base UUID we use when advertising a slot.
// The base is an all zero Version-3 name-based UUID. To turn this into an
// advertisement slot UUID, we simply OR the least significant bits with the
// slot number.
//
// More info about the format can be found here:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)
constexpr std::int64_t kAdvertisementUuidMsb = 0x0000000000003000;
constexpr std::int64_t kAdvertisementUuidLsb = 0x8000000000000000;
ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes) {
return Utils::Sha256Hash(
advertisement_bytes,
mediums::BleAdvertisementHeader::kAdvertisementHashLength);
}
ByteArray GenerateHash(const std::string& source, size_t size) {
return Utils::Sha256Hash(source, size);
}
ByteArray GenerateDeviceToken() {
return Utils::Sha256Hash(std::to_string(Prng().NextUint32()),
mediums::BleAdvertisement::kDeviceTokenLength);
}
} // namespace
@@ -122,14 +95,14 @@ bool BleV2::StartAdvertising(
// Wrap the connections advertisement to the medium advertisement.
const bool is_fast_advertisement = !fast_advertisement_service_uuid.empty();
ByteArray service_id_hash{GenerateHash(
service_id, mediums::BleAdvertisement::kServiceIdHashLength)};
ByteArray service_id_hash = mediums::bleutils::GenerateHash(
service_id, mediums::BleAdvertisement::kServiceIdHashLength);
ByteArray medium_advertisement_bytes{mediums::BleAdvertisement{
mediums::BleAdvertisement::Version::kV2,
mediums::BleAdvertisement::SocketVersion::kV2,
/* service_id_hash= */
is_fast_advertisement ? ByteArray{} : service_id_hash,
advertisement_bytes, GenerateDeviceToken()}};
advertisement_bytes, mediums::bleutils::GenerateDeviceToken()}};
if (medium_advertisement_bytes.Empty()) {
NEARBY_LOGS(INFO) << "Failed to BLE advertise because we could not wrap a "
"connection advertisement to medium advertisement.";
@@ -202,9 +175,10 @@ bool BleV2::StartAdvertising(
scan_response_data.tx_power_level =
BleAdvertisementData::kUnspecifiedTxPowerLevel;
scan_response_data.service_uuids.insert(
std::string(kCopresenceServiceUuid));
std::string(mediums::bleutils::kCopresenceServiceUuid));
scan_response_data.service_data.insert(
{std::string(kCopresenceServiceUuid), advertisement_header_bytes});
{std::string(mediums::bleutils::kCopresenceServiceUuid),
advertisement_header_bytes});
}
if (!medium_.StartAdvertising(advertising_data, scan_response_data,
@@ -243,10 +217,10 @@ bool BleV2::StopAdvertising(const std::string& service_id) {
// TODO(b/213835576): Check the BLE Connections is off. We set the fake
// value for the time being till connections is implemented.
bool no_incoming_ble_sockets = true;
// Restart the BLE advertisement if there is still an advertiser.
if (!subscribed_gatt_characteristics_.empty()) {
// Set the value of characteristic to empty if there is still an advertiser.
if (!hosted_gatt_characteristics_.empty()) {
ByteArray empty_value = {};
for (const auto& characteristic : subscribed_gatt_characteristics_) {
for (const auto& characteristic : hosted_gatt_characteristics_) {
if (!gatt_server_->UpdateCharacteristic(characteristic, empty_value)) {
NEARBY_LOGS(ERROR)
<< "Failed to clear characteristic uuid=" << characteristic.uuid
@@ -254,7 +228,7 @@ bool BleV2::StopAdvertising(const std::string& service_id) {
<< service_id;
}
}
subscribed_gatt_characteristics_.clear();
hosted_gatt_characteristics_.clear();
} else if (no_incoming_ble_sockets) {
// Otherwise, if we aren't restarting the BLE advertisement, then shutdown
// the gatt server if it's not in use.
@@ -318,7 +292,8 @@ bool BleV2::StartScanning(const std::string& service_id, PowerLevel power_level,
scanned_service_ids_.insert(service_id);
// TODO(b/213835576): We should re-start scanning once the power level is
// changed.
std::vector<std::string> service_uuids{std::string(kCopresenceServiceUuid)};
std::vector<std::string> service_uuids{
std::string(mediums::bleutils::kCopresenceServiceUuid)};
if (!medium_.StartScanning(
service_uuids, PowerLevelToPowerMode(power_level),
{
@@ -393,20 +368,14 @@ bool BleV2::StartAdvertisementGattServerLocked(
std::unique_ptr<GattServer> gatt_server = medium_.StartGattServer({
.characteristic_subscription_cb =
[this](const ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
MutexLock lock(&mutex_);
subscribed_gatt_characteristics_.insert(characteristic);
[](const ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
// TODO(b/213835576): Impl or remove.
},
.characteristic_unsubscription_cb =
[this](const ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
MutexLock lock(&mutex_);
const auto char_it =
subscribed_gatt_characteristics_.find(characteristic);
if (char_it != subscribed_gatt_characteristics_.end()) {
subscribed_gatt_characteristics_.erase(char_it);
}
[](const ServerGattConnection& connection,
const GattCharacteristic& characteristic) {
// TODO(b/213835576): Impl or remove.
},
});
if (!gatt_server || !gatt_server->IsValid()) {
@@ -436,21 +405,21 @@ bool BleV2::GenerateAdvertisementCharacteristic(
GattCharacteristic::Property::kRead};
absl::optional<GattCharacteristic> gatt_characteristic =
gatt_server.CreateCharacteristic(std::string(kCopresenceServiceUuid),
GenerateAdvertisementUuid(slot),
permissions, properties);
gatt_server.CreateCharacteristic(
std::string(mediums::bleutils::kCopresenceServiceUuid),
mediums::bleutils::GenerateAdvertisementUuid(slot), permissions,
properties);
if (!gatt_characteristic.has_value()) {
NEARBY_LOGS(INFO) << "Unable to create and add a characterstic to the gatt "
"server for the advertisement.";
return false;
}
if (!gatt_server.UpdateCharacteristic(*gatt_characteristic,
if (!gatt_server.UpdateCharacteristic(gatt_characteristic.value(),
gatt_advertisement)) {
NEARBY_LOGS(INFO) << "Unable to write a value to the GATT characteristic.";
return false;
}
hosted_gatt_characteristics_.insert(gatt_characteristic.value());
return true;
}
@@ -478,7 +447,7 @@ ByteArray BleV2::CreateAdvertisementHeader() {
bloom_filter.Add(dummy_service_id);
ByteArray advertisement_hash =
GenerateAdvertisementHash(dummy_service_id_bytes);
mediums::bleutils::GenerateAdvertisementHash(dummy_service_id_bytes);
for (const auto& item : gatt_advertisements_) {
const std::string& service_id = item.second.first;
const ByteArray& gatt_advertisement = item.second.second;
@@ -489,8 +458,8 @@ ByteArray BleV2::CreateAdvertisementHeader() {
std::string advertisement_bodies = absl::StrCat(
advertisement_hash.AsStringView(), gatt_advertisement.AsStringView());
advertisement_hash =
GenerateAdvertisementHash(ByteArray(std::move(advertisement_bodies)));
advertisement_hash = mediums::bleutils::GenerateAdvertisementHash(
ByteArray(std::move(advertisement_bodies)));
}
return ByteArray(mediums::BleAdvertisementHeader(
@@ -500,10 +469,6 @@ ByteArray BleV2::CreateAdvertisementHeader() {
advertisement_hash, /*psm=*/0));
}
std::string BleV2::GenerateAdvertisementUuid(int slot) {
return std::string(Uuid(kAdvertisementUuidMsb, kAdvertisementUuidLsb | slot));
}
PowerMode BleV2::PowerLevelToPowerMode(PowerLevel power_level) {
switch (power_level) {
case PowerLevel::kHighPower:
+1 -1
View File
@@ -124,7 +124,7 @@ class BleV2 final {
absl::flat_hash_map<int, std::pair<std::string, ByteArray>>
gatt_advertisements_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_set<api::ble_v2::GattCharacteristic>
subscribed_gatt_characteristics_ ABSL_GUARDED_BY(mutex_);
hosted_gatt_characteristics_ ABSL_GUARDED_BY(mutex_);
absl::flat_hash_set<std::string> scanned_service_ids_ ABSL_GUARDED_BY(mutex_);
};
@@ -20,6 +20,7 @@ cc_library(
"ble_advertisement.cc",
"ble_advertisement_header.cc",
"ble_packet.cc",
"ble_utils.cc",
"bloom_filter.cc",
],
hdrs = [
@@ -28,6 +29,7 @@ cc_library(
"ble_advertisement_header.h",
"ble_packet.h",
"ble_peripheral.h",
"ble_utils.h",
"bloom_filter.h",
"discovered_peripheral_callback.h",
],
@@ -37,6 +39,7 @@ cc_library(
],
deps = [
"//connections:core_types",
"//connections/implementation/mediums:utils",
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform:types",
@@ -58,6 +61,7 @@ cc_test(
"ble_advertisement_test.cc",
"ble_packet_test.cc",
"ble_peripheral_test.cc",
"ble_utils_test.cc",
"bloom_filter_test.cc",
],
deps = [
@@ -66,6 +70,7 @@ cc_test(
"//internal/platform:comm",
"//internal/platform/implementation/g3", # buildcleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/hash:hash_testing",
"@com_google_absl//absl/time",
"@com_google_googletest//:gtest_main",
],
@@ -32,36 +32,28 @@ const AdvertisementReadResult::Config AdvertisementReadResult::kDefaultConfig{
.max_backoff_duration = absl::Minutes(5),
};
// Adds a successfully read advertisement for the specified slot to this read
// result. This is fundamentally different from RecordLastReadStatus() because
// we can report a read failure, but still manage to read some advertisements.
void AdvertisementReadResult::AddAdvertisement(std::int32_t slot,
void AdvertisementReadResult::AddAdvertisement(int slot,
const ByteArray& advertisement) {
MutexLock lock(&mutex_);
// Blindly remove from the advertisements map to make sure any existing
// key-value pair is destroyed.
advertisements_.emplace(slot, advertisement);
// Replace if existed.
advertisements_.insert_or_assign(slot, advertisement);
}
// Determines whether or not an advertisement was successfully read at the
// specified slot.
bool AdvertisementReadResult::HasAdvertisement(std::int32_t slot) const {
bool AdvertisementReadResult::HasAdvertisement(int slot) const {
MutexLock lock(&mutex_);
return advertisements_.contains(slot);
}
// Retrieves all raw advertisements that were successfully read.
std::vector<const ByteArray*> AdvertisementReadResult::GetAdvertisements()
const {
MutexLock lock(&mutex_);
std::vector<const ByteArray*> all_advertisements;
all_advertisements.reserve(advertisements_.size());
for (const auto& item : advertisements_) {
all_advertisements.emplace_back(&item.second);
}
std::transform(advertisements_.begin(), advertisements_.end(),
std::back_inserter(all_advertisements),
[](auto& kv) { return &kv.second; });
return all_advertisements;
}
@@ -73,7 +65,7 @@ AdvertisementReadResult::EvaluateRetryStatus() const {
MutexLock lock(&mutex_);
// Check if we have already succeeded reading this advertisement.
if (status_ == Status::kSuccess) {
if (result_.has_value() && *result_ == Result::kSuccess) {
return RetryStatus::kPreviouslySucceeded;
}
@@ -85,9 +77,6 @@ AdvertisementReadResult::EvaluateRetryStatus() const {
return RetryStatus::kRetry;
}
// Records the status of the latest read, and updates the next backoff
// duration for subsequent reads. Be sure to also call
// AddAdvertisement() if any advertisements were read.
void AdvertisementReadResult::RecordLastReadStatus(bool is_success) {
MutexLock lock(&mutex_);
@@ -101,7 +90,7 @@ void AdvertisementReadResult::RecordLastReadStatus(bool is_success) {
} else {
// Determine whether or not we were already failing before. If we were, we
// should increase the backoff duration.
if (status_ == Status::kFailure) {
if (result_.has_value() && *result_ == Result::kFailure) {
// Use exponential backoff to determine the next backoff duration. This
// simply involves multiplying our current backoff duration by some
// multiplier.
@@ -119,11 +108,9 @@ void AdvertisementReadResult::RecordLastReadStatus(bool is_success) {
}
// Update the internal result.
status_ = is_success ? Status::kSuccess : Status::kFailure;
result_ = is_success ? Result::kSuccess : Result::kFailure;
}
// Returns how much time has passed since we last tried reading from an
// advertisement GATT server.
absl::Duration AdvertisementReadResult::GetDurationSinceRead() const {
MutexLock lock(&mutex_);
return GetDurationSinceReadLocked();
@@ -30,17 +30,10 @@ namespace nearby {
namespace connections {
namespace mediums {
// Representation of a GATT advertisement read result. This object helps us
// determine whether or not we need to retry GATT reads.
// Representation of a GATT/L2CAP advertisement read result. This object helps
// us determine whether or not we need to retry GATT reads.
class AdvertisementReadResult {
public:
// We need a long enough duration such that we always trigger a read
// retry AND we always connect to it without delay. The former case
// helps us initialize an AdvertisementReadResult so that we
// unconditionally try reading on the first sighting. And the latter
// case helps us connect immediately when we initialize a dummy read
// result for fast advertisements (which don't use the GATT server).
struct Config {
// How much to multiply the backoff duration by with every failure to read
// from the advertisement GATT server. This should never be below 1!
@@ -54,10 +47,11 @@ class AdvertisementReadResult {
};
static const Config kDefaultConfig;
explicit AdvertisementReadResult(const Config& config = kDefaultConfig)
: config_(config) {}
~AdvertisementReadResult() = default;
// Indicator for deciding if we should retry reading the advertisement.
enum class RetryStatus {
kUnknown = 0,
kRetry = 1,
@@ -65,20 +59,38 @@ class AdvertisementReadResult {
kTooSoon = 3,
};
void AddAdvertisement(std::int32_t slot, const ByteArray& advertisement)
// Adds a successfully read advertisement for the specified slot to this read
// result. This is fundamentally different from RecordLastReadStatus()
// because we can report a read failure, but still manage to read some
// advertisements.
void AddAdvertisement(int slot, const ByteArray& advertisement)
ABSL_LOCKS_EXCLUDED(mutex_);
bool HasAdvertisement(std::int32_t slot) const ABSL_LOCKS_EXCLUDED(mutex_);
// Determines whether or not an advertisement was successfully read at the
// specified slot.
bool HasAdvertisement(int slot) const ABSL_LOCKS_EXCLUDED(mutex_);
// Retrieves all raw advertisements that were successfully read.
std::vector<const ByteArray*> GetAdvertisements() const
ABSL_LOCKS_EXCLUDED(mutex_);
// Determines what stage we're in for retrying a read from an advertisement
// GATT/L2CAP server.
RetryStatus EvaluateRetryStatus() const ABSL_LOCKS_EXCLUDED(mutex_);
// Records the status of the latest read, and updates the next backoff
// duration for subsequent reads. Be sure to also call AddAdvertisement(...)
// if any advertisements were read.
void RecordLastReadStatus(bool is_success) ABSL_LOCKS_EXCLUDED(mutex_);
// Returns how much time has passed since we last tried reading from an
// advertisement GATT server.
absl::Duration GetDurationSinceRead() const ABSL_LOCKS_EXCLUDED(mutex_);
private:
enum class Status {
kUnknown = 0,
enum class Result {
kFailure = 0,
kSuccess = 1,
kFailure = 2,
};
absl::Duration GetDurationSinceReadLocked() const
@@ -87,13 +99,21 @@ class AdvertisementReadResult {
mutable Mutex mutex_;
// Maps slot numbers to the GATT advertisement found in that slot.
absl::flat_hash_map<std::int32_t, ByteArray> advertisements_
ABSL_GUARDED_BY(mutex_);
absl::flat_hash_map<int, ByteArray> advertisements_ ABSL_GUARDED_BY(mutex_);
Config config_;
absl::Duration backoff_duration_ ABSL_GUARDED_BY(mutex_);
absl::Time last_read_timestamp_ ABSL_GUARDED_BY(mutex_);
Status status_ ABSL_GUARDED_BY(mutex_) = Status::kUnknown;
absl::Duration backoff_duration_ ABSL_GUARDED_BY(mutex_) =
config_.base_backoff_duration;
// We need a long enough duration such that we always trigger a read retry AND
// we always connect to it without delay. The former case helps us initialize
// an AdvertisementReadResult so that we unconditionally try reading on the
// first sighting. And the latter case helps us connect immediately when we
// initialize a dummy read result for fast advertisements (which don't use the
// GATT server).
absl::Time last_read_timestamp_ ABSL_GUARDED_BY(mutex_) =
SystemClock::ElapsedRealtime() - config_.max_backoff_duration;
absl::optional<Result> result_ ABSL_GUARDED_BY(mutex_);
};
} // namespace mediums
@@ -41,7 +41,7 @@ TEST(AdvertisementReadResultTest, AdvertisementExists) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ true);
std::int32_t slot = 6;
int slot = 6;
advertisement_read_result.AddAdvertisement(slot,
ByteArray(kAdvertisementBytes));
@@ -52,7 +52,7 @@ TEST(AdvertisementReadResultTest, AdvertisementNonExistent) {
AdvertisementReadResult advertisement_read_result(test_config);
advertisement_read_result.RecordLastReadStatus(/* is_success= */ true);
std::int32_t slot = 6;
int slot = 6;
EXPECT_FALSE(advertisement_read_result.HasAdvertisement(slot));
}
@@ -114,7 +114,7 @@ TEST(AdvertisementReadResultTest, ReportStatusExponentialBackoffMax) {
advertisement_read_result.RecordLastReadStatus(/* is_success= */ false);
// Record an absurd amount of failures so we hit the maximum backoff duration.
for (std::int32_t i = 0; i < 1000; i++) {
for (int i = 0; i < 1000; ++i) {
advertisement_read_result.RecordLastReadStatus(/* is_success= */ false);
}
@@ -192,27 +192,12 @@ BleAdvertisement::operator ByteArray() const {
bool BleAdvertisement::operator==(const BleAdvertisement &rhs) const {
return this->GetVersion() == rhs.GetVersion() &&
this->GetSocketVersion() == rhs.GetSocketVersion() &&
this->IsFastAdvertisement() == rhs.IsFastAdvertisement() &&
this->GetServiceIdHash() == rhs.GetServiceIdHash() &&
this->GetData() == rhs.GetData() &&
this->GetDeviceToken() == rhs.GetDeviceToken();
}
bool BleAdvertisement::operator<(const BleAdvertisement &rhs) const {
if (this->GetVersion() != rhs.GetVersion()) {
return this->GetVersion() < rhs.GetVersion();
}
if (this->GetSocketVersion() != rhs.GetSocketVersion()) {
return this->GetSocketVersion() < rhs.GetSocketVersion();
}
if (this->GetServiceIdHash() != rhs.GetServiceIdHash()) {
return this->GetServiceIdHash() < rhs.GetServiceIdHash();
}
if (this->GetDeviceToken() != rhs.GetDeviceToken()) {
return this->GetDeviceToken() < rhs.GetDeviceToken();
}
return this->GetData() < rhs.GetData();
}
bool BleAdvertisement::IsSupportedVersion(Version version) const {
return version >= Version::kV1 && version <= Version::kV2;
}
@@ -57,6 +57,15 @@ class BleAdvertisement {
static constexpr int kServiceIdHashLength = 3;
static constexpr int kDeviceTokenLength = 2;
// Hashable
bool operator==(const BleAdvertisement &rhs) const;
template <typename H>
friend H AbslHashValue(H h, const BleAdvertisement &b) {
return H::combine(std::move(h), b.version_, b.socket_version_,
b.fast_advertisement_, b.service_id_hash_, b.data_,
b.device_token_);
}
BleAdvertisement() = default;
BleAdvertisement(Version version, SocketVersion socket_version,
const ByteArray &service_id_hash, const ByteArray &data,
@@ -69,9 +78,6 @@ class BleAdvertisement {
~BleAdvertisement() = default;
explicit operator ByteArray() const;
// Operator overloads when comparing BleAdvertisement.
bool operator==(const BleAdvertisement &rhs) const;
bool operator<(const BleAdvertisement &rhs) const;
bool IsValid() const { return IsSupportedVersion(version_); }
Version GetVersion() const { return version_; }
@@ -136,6 +136,16 @@ BleAdvertisementHeader::operator ByteArray() const {
return ByteArray(std::move(out));
}
bool BleAdvertisementHeader::operator==(
const BleAdvertisementHeader &rhs) const {
return GetVersion() == rhs.GetVersion() &&
IsExtendedAdvertisement() == rhs.IsExtendedAdvertisement() &&
GetNumSlots() == rhs.GetNumSlots() &&
GetServiceIdBloomFilter() == rhs.GetServiceIdBloomFilter() &&
GetAdvertisementHash() == rhs.GetAdvertisementHash() &&
GetPsmValue() == rhs.GetPsmValue();
}
} // namespace mediums
} // namespace connections
} // namespace nearby
@@ -54,6 +54,15 @@ class BleAdvertisementHeader {
static constexpr int kAdvertisementHashLength = 4;
static constexpr int kServiceIdBloomFilterLength = 10;
// Hashable
bool operator==(const BleAdvertisementHeader &rhs) const;
template <typename H>
friend H AbslHashValue(H h, const BleAdvertisementHeader &b) {
return H::combine(std::move(h), b.version_, b.extended_advertisement_,
b.num_slots_, b.service_id_bloom_filter_,
b.advertisement_hash_, b.psm_);
}
BleAdvertisementHeader() = default;
BleAdvertisementHeader(Version version, bool extended_advertisement,
int num_slots,
@@ -15,6 +15,7 @@
#include "connections/implementation/mediums/ble_v2/ble_advertisement_header.h"
#include "gtest/gtest.h"
#include "absl/hash/hash_testing.h"
#include "internal/platform/base64_utils.h"
namespace location {
@@ -214,6 +215,23 @@ TEST(BleAdvertisementHeaderTest, ConstructionFromShortLengthFails) {
EXPECT_FALSE(short_ble_advertisement_header.IsValid());
}
TEST(BleAdvertisementHeaderTest, Hash) {
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
BleAdvertisementHeader(),
BleAdvertisementHeader(kVersion, false, 0,
ByteArray(std::string(kServiceIDBloomFilter)),
ByteArray(std::string(kAdvertisementHash)), 0),
BleAdvertisementHeader(
kVersion, true, 2,
ByteArray("\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15"),
ByteArray("\x0E\x0F\x0G\x0H"), 1),
BleAdvertisementHeader(
kVersion, false, 4,
ByteArray("\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29"),
ByteArray("\x0i\x0j\x0k\x0l"), 2),
}));
}
} // namespace
} // namespace mediums
} // namespace connections
@@ -17,6 +17,7 @@
#include <algorithm>
#include "gtest/gtest.h"
#include "absl/hash/hash_testing.h"
namespace location {
namespace nearby {
@@ -448,6 +449,21 @@ TEST(BleAdvertisementTest,
EXPECT_FALSE(corrupted_ble_advertisement.IsValid());
}
TEST(BleAdvertisementTest, Hash) {
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
BleAdvertisement(),
BleAdvertisement(kVersion, kSocketVersion, ByteArray{},
ByteArray(std::string(kFastData)),
ByteArray(std::string(kDeviceToken))),
BleAdvertisement(
kVersion, kSocketVersion, ByteArray(std::string(kServiceIDHashBytes)),
ByteArray(std::string(kData)), ByteArray(std::string(kDeviceToken))),
BleAdvertisement(kVersion, BleAdvertisement::SocketVersion::kV1,
ByteArray{}, ByteArray(std::string(kFastData)),
ByteArray(std::string(kDeviceToken))),
}));
}
} // namespace
} // namespace mediums
} // namespace connections
@@ -30,7 +30,8 @@ namespace mediums {
class BlePeripheral {
public:
BlePeripheral() = default;
explicit BlePeripheral(const ByteArray& id) : id_(id) {}
explicit BlePeripheral(const ByteArray& id) : BlePeripheral(id, 0) {}
BlePeripheral(const ByteArray& id, int psm) : id_(id), psm_(psm) {}
BlePeripheral(const BlePeripheral&) = default;
BlePeripheral& operator=(const BlePeripheral&) = default;
BlePeripheral(BlePeripheral&&) = default;
@@ -39,11 +40,17 @@ class BlePeripheral {
bool IsValid() const { return !id_.Empty(); }
ByteArray GetId() const { return id_; }
int GetPsm() const { return psm_; }
private:
// A unique identifier for this peripheral. It is the BLE advertisement it
// was found on.
// was found on, or even simply the BLE MAC address.
ByteArray id_;
// The psm (protocol service multiplexer) value is used for create data
// connection on L2CAP socket. It only exists when remote device supports
// L2CAP socket feature.
int psm_;
};
} // namespace mediums
@@ -23,14 +23,27 @@ namespace mediums {
namespace {
constexpr absl::string_view kId{"AB12"};
constexpr int kDefaultPsm = 0;
TEST(BlePeripheralTest, ConstructionWorks) {
ByteArray id{std::string(kId)};
BlePeripheral ble_peripheral{id};
BlePeripheral ble_peripheral(id);
EXPECT_TRUE(ble_peripheral.IsValid());
EXPECT_EQ(id, ble_peripheral.GetId());
EXPECT_EQ(kDefaultPsm, ble_peripheral.GetPsm());
}
TEST(BlePeripheralTest, ConstructionWorksWithPsm) {
ByteArray id{std::string(kId)};
int psm = 1;
BlePeripheral ble_peripheral(id, psm);
EXPECT_TRUE(ble_peripheral.IsValid());
EXPECT_EQ(id, ble_peripheral.GetId());
EXPECT_EQ(psm, ble_peripheral.GetPsm());
}
TEST(BlePeripheralTest, ConstructionEmptyFails) {
@@ -0,0 +1,71 @@
// 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/ble_utils.h"
#include <string>
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace bleutils {
namespace {
// These two values make up the base UUID we use when advertising a slot.
// The base is an all zero Version-3 name-based UUID. To turn this into an
// advertisement slot UUID, we simply OR the least significant bits with the
// slot number.
//
// More info about the format can be found here:
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)
constexpr std::int64_t kAdvertisementUuidMsb = 0x0000000000003000;
constexpr std::int64_t kAdvertisementUuidLsb = 0x8000000000000000;
} // namespace
const absl::string_view kCopresenceServiceUuid =
"0000FEF3-0000-1000-8000-00805F9B34FB";
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 GenerateDeviceToken() {
return Utils::Sha256Hash(std::to_string(Prng().NextUint32()),
mediums::BleAdvertisement::kDeviceTokenLength);
}
ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes) {
return Utils::Sha256Hash(advertisement_bytes,
BleAdvertisementHeader::kAdvertisementHashLength);
}
std::string GenerateAdvertisementUuid(int slot) {
if (slot < 0) {
return {};
}
return std::string(Uuid(kAdvertisementUuidMsb, kAdvertisementUuidLsb | slot));
}
} // namespace bleutils
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,47 @@
// 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_UTILS_H_
#define CORE_INTERNAL_MEDIUMS_BLE_V2_UTILS_H_
#include <string>
#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"
#include "connections/implementation/mediums/utils.h"
#include "connections/implementation/mediums/uuid.h"
#include "internal/platform/prng.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace bleutils {
ABSL_CONST_INIT extern const absl::string_view kCopresenceServiceUuid;
ByteArray GenerateHash(const std::string& source, size_t size);
ByteArray GenerateServiceIdHash(const std::string& service_id);
ByteArray GenerateDeviceToken();
ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes);
std::string GenerateAdvertisementUuid(int slot);
} // namespace bleutils
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_UTILS_H_
@@ -0,0 +1,98 @@
// 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/ble_utils.h"
#include <string>
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace bleutils {
namespace {
TEST(BleUtilsTest, CanGenerateHash) {
std::string service_id = {"service_id"};
ByteArray generated_bytes = GenerateHash(service_id, 4);
// Can generate a non-empty byte array.
EXPECT_FALSE(generated_bytes.Empty());
generated_bytes = GenerateHash(service_id, 0);
// Cannot generate a non-empty byte array for size 0.
EXPECT_TRUE(generated_bytes.Empty());
}
TEST(BleUtilsTest, CanGenerateServiceIdHash) {
std::string service_id = {"service_id"};
ByteArray generated_bytes = GenerateServiceIdHash(service_id);
// Can generate a non-empty byte array.
EXPECT_FALSE(generated_bytes.Empty());
}
TEST(BleUtilsTest, CanGenerateDeviceToken) {
ByteArray generated_bytes = GenerateDeviceToken();
// Can generate a non-empty byte array.
EXPECT_FALSE(generated_bytes.Empty());
}
TEST(BleUtilsTest, CanGenerateAdvertisementHash) {
ByteArray empty_advertisement_bytes = {};
ByteArray non_empty_advertisement_bytes("abcd");
ByteArray generated_bytes_1 =
GenerateAdvertisementHash(empty_advertisement_bytes);
ByteArray generated_bytes_2 =
GenerateAdvertisementHash(non_empty_advertisement_bytes);
// Can generate a non-empty byte array.
EXPECT_FALSE(generated_bytes_1.Empty());
EXPECT_FALSE(generated_bytes_2.Empty());
}
TEST(BleUtilsTest, CanGenerateAdvertisementUuid) {
std::string generated_string = GenerateAdvertisementUuid(0);
EXPECT_EQ("00000000-0000-3000-8000-000000000000", generated_string);
generated_string = GenerateAdvertisementUuid(1);
EXPECT_EQ("00000000-0000-3000-8000-000000000001", generated_string);
generated_string = GenerateAdvertisementUuid(10);
EXPECT_EQ("00000000-0000-3000-8000-00000000000a", generated_string);
generated_string = GenerateAdvertisementUuid(-1);
// Can't generate an advertisement uuid for slot < 0. The result is empty.
EXPECT_TRUE(generated_string.empty());
}
} // namespace
} // namespace bleutils
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
+1
View File
@@ -197,6 +197,7 @@ cc_test(
":test_util",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/hash:hash_testing",
"@com_google_googletest//:gtest_main",
],
)
+6
View File
@@ -99,6 +99,12 @@ class ByteArray {
return absl::string_view(data(), size());
}
// Hashable
template <typename H>
friend H AbslHashValue(H h, const ByteArray& m) {
return H::combine(std::move(h), m.data_);
}
private:
std::string data_;
};
+10
View File
@@ -17,6 +17,7 @@
#include <cstring>
#include "gtest/gtest.h"
#include "absl/hash/hash_testing.h"
namespace {
@@ -95,4 +96,13 @@ TEST(ByteArrayTest, CreateFromAbslStringReturnsTheSame) {
EXPECT_EQ(bytes.AsStringView(), kTestString);
}
TEST(ByteArrayTest, Hash) {
EXPECT_TRUE(absl::VerifyTypeImplementsAbslHashCorrectly({
ByteArray(),
ByteArray("12345"),
ByteArray("ABCDE"),
ByteArray("A1B2Z"),
}));
}
} // namespace