From 8326aeef380a97fa9c0cca0d1c6007ee8b3a115f Mon Sep 17 00:00:00 2001 From: edwinwu Date: Fri, 18 Feb 2022 06:29:13 -0800 Subject: [PATCH] [BLE Refactor] Implement CreateAdvertisementHeader in BleV2 medium. PiperOrigin-RevId: 429550598 --- connections/implementation/mediums/BUILD | 2 + connections/implementation/mediums/ble_v2.cc | 84 +++++++++++ connections/implementation/mediums/ble_v2.h | 52 +++++++ .../ble_v2/ble_advertisement_header.cc | 4 + .../mediums/ble_v2/ble_advertisement_header.h | 5 +- cpp/core/internal/mediums/BUILD | 14 ++ cpp/core/internal/mediums/ble_v2.cc | 90 +++++++++++ cpp/core/internal/mediums/ble_v2.h | 82 ++++++++++ .../ble_v2/ble_advertisement_header.cc | 142 ++++++++++++++++++ .../mediums/ble_v2/ble_advertisement_header.h | 102 +++++++++++++ internal/platform/byte_array.h | 7 + internal/platform/byte_array_test.cc | 8 + 12 files changed, 590 insertions(+), 2 deletions(-) create mode 100644 connections/implementation/mediums/ble_v2.cc create mode 100644 connections/implementation/mediums/ble_v2.h create mode 100644 cpp/core/internal/mediums/BUILD create mode 100644 cpp/core/internal/mediums/ble_v2.cc create mode 100644 cpp/core/internal/mediums/ble_v2.h create mode 100644 cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc create mode 100644 cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h diff --git a/connections/implementation/mediums/BUILD b/connections/implementation/mediums/BUILD index 3784a441..95c21cb1 100644 --- a/connections/implementation/mediums/BUILD +++ b/connections/implementation/mediums/BUILD @@ -17,6 +17,7 @@ cc_library( name = "mediums", srcs = [ "ble.cc", + "ble_v2.cc", "bluetooth_classic.cc", "bluetooth_radio.cc", "mediums.cc", @@ -26,6 +27,7 @@ cc_library( ], hdrs = [ "ble.h", + "ble_v2.h", "bluetooth_classic.h", "bluetooth_radio.h", "lost_entity_tracker.h", diff --git a/connections/implementation/mediums/ble_v2.cc b/connections/implementation/mediums/ble_v2.cc new file mode 100644 index 00000000..f22a93ff --- /dev/null +++ b/connections/implementation/mediums/ble_v2.cc @@ -0,0 +1,84 @@ +// 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.h" + +#include +#include + +#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/bloom_filter.h" +#include "connections/implementation/mediums/utils.h" +#include "internal/platform/prng.h" + +namespace location { +namespace nearby { +namespace connections { + +namespace { + +constexpr int kDummyServiceIdLength = 128; + +ByteArray GenerateAdvertisementHash(const ByteArray& advertisement_bytes) { + return Utils::Sha256Hash( + advertisement_bytes, + mediums::BleAdvertisementHeader::kAdvertisementHashLength); +} + +} // namespace + +BleV2::BleV2(BluetoothRadio& radio) + : radio_(radio), adapter_(radio_.GetBluetoothAdapter()) {} + +ByteArray BleV2::CreateAdvertisementHeader() { + // Create a randomized dummy service id to anonymize the header with. + ByteArray dummy_service_id_bytes = + Utils::GenerateRandomBytes(kDummyServiceIdLength); + std::string dummy_service_id{dummy_service_id_bytes}; + + mediums::BloomFilter< + mediums::BleAdvertisementHeader::kServiceIdBloomFilterLength> + bloom_filter; + bloom_filter.Add(dummy_service_id); + + ByteArray advertisement_hash = + 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; + bloom_filter.Add(service_id); + + // Compute the next hash according to the algorithm in + // https://source.corp.google.com/piper///depot/google3/java/com/google/android/gmscore/integ/modules/nearby/src/com/google/android/gms/nearby/mediums/bluetooth/BluetoothLowEnergy.java;rcl=428397891;l=1043 + ByteArray previous_advertisement_hash = advertisement_hash; + std::string advertisement_bodies = + absl::StrCat(previous_advertisement_hash.AsStringView(), + gatt_advertisement.AsStringView()); + + advertisement_hash = + GenerateAdvertisementHash(ByteArray(std::move(advertisement_bodies))); + } + + return ByteArray(mediums::BleAdvertisementHeader( + mediums::BleAdvertisementHeader::Version::kV2, + /*extended_advertisement=*/false, + /*num_slots=*/gatt_advertisements_.size(), ByteArray(bloom_filter), + advertisement_hash, /*psm=*/0)); +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/connections/implementation/mediums/ble_v2.h b/connections/implementation/mediums/ble_v2.h new file mode 100644 index 00000000..2edc01b4 --- /dev/null +++ b/connections/implementation/mediums/ble_v2.h @@ -0,0 +1,52 @@ +// 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_H_ +#define CORE_INTERNAL_MEDIUMS_BLE_V2_H_ + +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "connections/implementation/mediums/bluetooth_radio.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/mutex.h" + +namespace location { +namespace nearby { +namespace connections { + +// TODO(b/213691253): Add BleV2 medium tests after more functions are ready. +// Provides the operations that can be performed on the Bluetooth Low Energy +// (BLE) medium. +class BleV2 { + public: + explicit BleV2(BluetoothRadio& bluetooth_radio); + + private: + ByteArray CreateAdvertisementHeader() ABSL_SHARED_LOCKS_REQUIRED(mutex_); + + mutable Mutex mutex_; + BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); + BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map> + gatt_advertisements_ ABSL_GUARDED_BY(mutex_); +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_H_ diff --git a/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc b/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc index a1379c18..30b440c8 100644 --- a/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc +++ b/connections/implementation/mediums/ble_v2/ble_advertisement_header.cc @@ -26,6 +26,10 @@ namespace nearby { namespace connections { namespace mediums { +// These definitions are necessary before C++17. +constexpr int BleAdvertisementHeader::kAdvertisementHashLength; +constexpr int BleAdvertisementHeader::kServiceIdBloomFilterLength; + BleAdvertisementHeader::BleAdvertisementHeader( Version version, bool extended_advertisement, int num_slots, const ByteArray &service_id_bloom_filter, diff --git a/connections/implementation/mediums/ble_v2/ble_advertisement_header.h b/connections/implementation/mediums/ble_v2/ble_advertisement_header.h index 24dd3584..b3f83554 100644 --- a/connections/implementation/mediums/ble_v2/ble_advertisement_header.h +++ b/connections/implementation/mediums/ble_v2/ble_advertisement_header.h @@ -51,6 +51,9 @@ class BleAdvertisementHeader { // characteristic so the two are not compatible. }; + static constexpr int kAdvertisementHashLength = 4; + static constexpr int kServiceIdBloomFilterLength = 10; + BleAdvertisementHeader() = default; BleAdvertisementHeader(Version version, bool extended_advertisement, int num_slots, @@ -76,8 +79,6 @@ class BleAdvertisementHeader { private: static constexpr int kVersionAndNumSlotsLength = 1; - static constexpr int kServiceIdBloomFilterLength = 10; - static constexpr int kAdvertisementHashLength = 4; static constexpr int kMinAdvertisementHeaderLength = kVersionAndNumSlotsLength + kServiceIdBloomFilterLength + kAdvertisementHashLength; diff --git a/cpp/core/internal/mediums/BUILD b/cpp/core/internal/mediums/BUILD new file mode 100644 index 00000000..9167825d --- /dev/null +++ b/cpp/core/internal/mediums/BUILD @@ -0,0 +1,14 @@ +# 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. +licenses(["notice"]) diff --git a/cpp/core/internal/mediums/ble_v2.cc b/cpp/core/internal/mediums/ble_v2.cc new file mode 100644 index 00000000..a1e0f5ae --- /dev/null +++ b/cpp/core/internal/mediums/ble_v2.cc @@ -0,0 +1,90 @@ +// 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 "third_party/nearby/cpp/core/internal/mediums/ble_v2.h" + +#include + +#include "absl/strings/str_cat.h" +#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/ble_advertisement.h" +#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h" +#include "third_party/nearby/cpp/core/internal/mediums/ble_v2/bloom_filter.h" +#include "third_party/nearby/cpp/core/internal/mediums/utils.h" +#include "third_party/nearby/cpp/platform/base/prng.h" + +namespace location { +namespace nearby { +namespace connections { + +ByteArray BleV2::GenerateAdvertisementHash( + const ByteArray& advertisement_bytes) { + return Utils::Sha256Hash( + advertisement_bytes, + mediums::BleAdvertisementHeader::kAdvertisementHashLength); +} + +ByteArray BleV2::GenerateHash(const std::string& source, size_t size) { + return Utils::Sha256Hash(source, size); +} + +ByteArray BleV2::GenerateDeviceToken() { + return Utils::Sha256Hash(std::to_string(Prng().NextUint32()), + mediums::BleAdvertisement::kDeviceTokenLength); +} + +BleV2::BleV2(BluetoothRadio& radio) : radio_(radio) {} + +ByteArray BleV2::CreateAdvertisementHeader() { + // Create a randomized dummy service id to anonymize the header with. + ByteArray dummy_service_id_bytes = + Utils::GenerateRandomBytes(kDummyServiceIdLength); + std::string dummy_service_id{dummy_service_id_bytes}; + + // Create a bloom filter with the dummy service id. + mediums::BloomFilter< + mediums::BleAdvertisementHeader::kServiceIdBloomFilterLength> + bloom_filter; + bloom_filter.Add(dummy_service_id); + + // Add the service id for each GATT advertisement into the bloom filter, while + // also creating a hash of dummyServiceIdBytes + all GATT advertisements. + ByteArray advertisement_hash = + GenerateAdvertisementHash(dummy_service_id_bytes); + int num_slots = 0; + for (const auto& item : gatt_advertisement_info.gatt_advertisements) { + std::string service_id = item.second.first; + ByteArray gatt_advertisment = item.second.second; + bloom_filter.Add(service_id); + + // Compute the next hash by taking the hash of [previous hash] + [next + // advertisement data]. + ByteArray previous_advertisement_hash = advertisement_hash; + std::string advertisement_bodies = + absl::StrCat(std::string(previous_advertisement_hash), + std::string(gatt_advertisment)); + + advertisement_hash = + GenerateAdvertisementHash(ByteArray{std::move(advertisement_bodies)}); + num_slots++; + } + + return ByteArray(mediums::BleAdvertisementHeader( + mediums::BleAdvertisementHeader::Version::kV2, + /*extended_advertisement=*/false, num_slots, ByteArray(bloom_filter), + advertisement_hash, /*psm=*/0)); +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core/internal/mediums/ble_v2.h b/cpp/core/internal/mediums/ble_v2.h new file mode 100644 index 00000000..ac400805 --- /dev/null +++ b/cpp/core/internal/mediums/ble_v2.h @@ -0,0 +1,82 @@ +// 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_H_ +#define CORE_INTERNAL_MEDIUMS_BLE_V2_H_ + +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "third_party/nearby/cpp/core/internal/mediums/bluetooth_radio.h" +#include "third_party/nearby/cpp/platform/base/byte_array.h" +#include "third_party/nearby/cpp/platform/public/mutex.h" + +namespace location { +namespace nearby { +namespace connections { + +class BleV2 { + public: + explicit BleV2(BluetoothRadio& bluetooth_radio); + ~BleV2() = default; + + private: + // Container that stores info about a GATT advertisement. + struct GattAdvertisementInfo { + bool Empty() const { return gatt_advertisements.empty(); } + void Clear() { gatt_advertisements.clear(); } + void Add(int slot, std::pair gatt_advertisement) { + gatt_advertisements.insert({slot, gatt_advertisement}); + } + void Remove(int slot) { gatt_advertisements.erase(slot); } + bool Existed(int slot) const { + return gatt_advertisements.contains(slot); + } + std::pair GetAdvertisement(int slot) { + const auto& it = gatt_advertisements.find(slot); + if (it == gatt_advertisements.end()) { + return {}; + } + return it->second; + } + + // A map of slot -> pair of {service_id, gatt_advertisement} + absl::flat_hash_map> + gatt_advertisements; + }; + + static constexpr int kMaxAdvertisementLength = 512; + static constexpr int kDummyServiceIdLength = 128; + + static ByteArray GenerateAdvertisementHash( + const ByteArray& advertisement_bytes); + static ByteArray GenerateHash(const std::string& source, size_t size); + static ByteArray GenerateDeviceToken(); + + ByteArray CreateAdvertisementHeader() ABSL_SHARED_LOCKS_REQUIRED(mutex_); + + mutable Mutex mutex_; + BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); + BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){ + radio_.GetBluetoothAdapter()}; + GattAdvertisementInfo gatt_advertisement_info ABSL_GUARDED_BY(mutex_); +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_H_ diff --git a/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc new file mode 100644 index 00000000..3f123557 --- /dev/null +++ b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.cc @@ -0,0 +1,142 @@ +// Copyright 2020 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 "third_party/nearby/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h" + +#include + +#include "absl/strings/str_cat.h" +#include "third_party/nearby/cpp/platform/base/base64_utils.h" +#include "third_party/nearby/cpp/platform/base/base_input_stream.h" +#include "third_party/nearby/cpp/platform/public/logging.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +// These definitions are necessary before C++17. +constexpr int BleAdvertisementHeader::kAdvertisementHashLength; +constexpr int BleAdvertisementHeader::kServiceIdBloomFilterLength; + +BleAdvertisementHeader::BleAdvertisementHeader( + Version version, bool extended_advertisement, int num_slots, + const ByteArray &service_id_bloom_filter, + const ByteArray &advertisement_hash, int psm) { + if (version != Version::kV2 || num_slots <= 0 || + service_id_bloom_filter.size() != kServiceIdBloomFilterLength || + advertisement_hash.size() != kAdvertisementHashLength) { + return; + } + + version_ = version; + extended_advertisement_ = extended_advertisement; + num_slots_ = num_slots; + service_id_bloom_filter_ = service_id_bloom_filter; + advertisement_hash_ = advertisement_hash; + psm_ = psm; +} + +BleAdvertisementHeader::BleAdvertisementHeader( + const ByteArray &ble_advertisement_header_bytes) { + if (ble_advertisement_header_bytes.Empty()) { + NEARBY_LOG( + ERROR, + "Cannot deserialize BLEAdvertisementHeader: failed Base64 decoding"); + return; + } + + if (ble_advertisement_header_bytes.size() < kMinAdvertisementHeaderLength) { + NEARBY_LOG(ERROR, + "Cannot deserialize BleAdvertisementHeader: expecting min %u " + "raw bytes, got %" PRIu64 " instead", + kMinAdvertisementHeaderLength, + ble_advertisement_header_bytes.size()); + return; + } + + ByteArray advertisement_header_bytes{ble_advertisement_header_bytes}; + BaseInputStream base_input_stream{advertisement_header_bytes}; + // The first 1 byte is supposed to be the version and number of slots. + auto version_and_num_slots_byte = + static_cast(base_input_stream.ReadUint8()); + // The upper 3 bits are supposed to be the version. + version_ = + static_cast((version_and_num_slots_byte & kVersionBitmask) >> 5); + if (version_ != Version::kV2) { + NEARBY_LOG( + ERROR, + "Cannot deserialize BleAdvertisementHeader: unsupported Version %d", + version_); + return; + } + // The next 1 bit is supposed to be the extended advertisement flag. + extended_advertisement_ = + ((version_and_num_slots_byte & kExtendedAdvertismentBitMask) >> 4) == 1; + // The lower 4 bits are supposed to be the number of slots. + num_slots_ = static_cast(version_and_num_slots_byte & kNumSlotsBitmask); + if (num_slots_ <= 0) { + version_ = Version::kUndefined; + return; + } + + // The next 10 bytes are supposed to be the service_id_bloom_filter. + service_id_bloom_filter_ = + base_input_stream.ReadBytes(kServiceIdBloomFilterLength); + + // The next 4 bytes are supposed to be the advertisement_hash. + advertisement_hash_ = base_input_stream.ReadBytes(kAdvertisementHashLength); + + // The next 2 bytes are PSM value. + if (base_input_stream.IsAvailable(sizeof(std::uint16_t))) { + psm_ = static_cast(base_input_stream.ReadUint16()); + } +} + +BleAdvertisementHeader::operator ByteArray() const { + if (!IsValid()) { + return ByteArray(); + } + + // The first 3 bits are the Version. + char version_and_num_slots_byte = + (static_cast(version_) << 5) & kVersionBitmask; + // The next 1 bit is extended advertisement flag. + version_and_num_slots_byte |= + (static_cast(extended_advertisement_) << 4) & + kExtendedAdvertismentBitMask; + // The next 5 bits are the number of slots. + version_and_num_slots_byte |= + static_cast(num_slots_) & kNumSlotsBitmask; + + // Convert psm_ value to 2-bytes. + ByteArray psm_byte{sizeof(std::uint16_t)}; + char *data = psm_byte.data(); + data[0] = psm_ & 0xFF00; + data[1] = psm_ & 0x00FF; + + // clang-format off + std::string out = absl::StrCat(std::string(1, version_and_num_slots_byte), + std::string(service_id_bloom_filter_), + std::string(advertisement_hash_), + std::string(psm_byte)); + // clang-format on + + return ByteArray(std::move(out)); +} + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h new file mode 100644 index 00000000..15ba4aab --- /dev/null +++ b/cpp/core/internal/mediums/ble_v2/ble_advertisement_header.h @@ -0,0 +1,102 @@ +// Copyright 2020 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_BLE_ADVERTISEMENT_HEADER_H_ +#define CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_ + +#include + +#include "third_party/nearby/cpp/platform/base/byte_array.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +// Represents the format of the Mediums BLE Advertisement Header used in +// Advertising + Discovery. +// +// [VERSION][NUM_SLOTS][SERVICE_ID_BLOOM_FILTER][ADVERTISEMENT_HASH][L2_CAP_PSM] +// +// See go/nearby-ble-design for more information. +// +// Note. The object constructed by default constructor or the parameterized +// constructor with invalid value(s) is treated as invalid instance. Caller +// should be responsible to call IsValid() to check the instance is invalid in +// advance before continue on. +class BleAdvertisementHeader { + public: + // Versions of the BleAdvertisementHeader. + enum class Version { + kUndefined = 0, + kV1 = 1, + kV2 = 2, + // Version is only allocated 3 bits in the BleAdvertisementHeader, so this + // can never go beyond V7. + // + // V1 is not present because it's an old format used in Nearby Connections + // before this logic was pushed down into Nearby Mediums. V1 put + // everything in the service data, while V2 puts the data inside a GATT + // characteristic so the two are not compatible. + }; + + static constexpr int kAdvertisementHashLength = 4; + static constexpr int kServiceIdBloomFilterLength = 10; + + BleAdvertisementHeader() = default; + BleAdvertisementHeader(Version version, bool extended_advertisement, + int num_slots, + const ByteArray &service_id_bloom_filter, + const ByteArray &advertisement_hash, int psm); + explicit BleAdvertisementHeader( + const ByteArray &ble_advertisement_header_bytes); + BleAdvertisementHeader(const BleAdvertisementHeader &) = default; + BleAdvertisementHeader &operator=(const BleAdvertisementHeader &) = default; + BleAdvertisementHeader(BleAdvertisementHeader &&) = default; + BleAdvertisementHeader &operator=(BleAdvertisementHeader &&) = default; + ~BleAdvertisementHeader() = default; + + explicit operator ByteArray() const; + + bool IsValid() const { return version_ == Version::kV2; } + Version GetVersion() const { return version_; } + bool IsExtendedAdvertisement() const { return extended_advertisement_; } + int GetNumSlots() const { return num_slots_; } + ByteArray GetServiceIdBloomFilter() const { return service_id_bloom_filter_; } + ByteArray GetAdvertisementHash() const { return advertisement_hash_; } + int GetPsmValue() const { return psm_; } + + private: + static constexpr int kVersionAndNumSlotsLength = 1; + static constexpr int kMinAdvertisementHeaderLength = + kVersionAndNumSlotsLength + kServiceIdBloomFilterLength + + kAdvertisementHashLength; + static constexpr int kVersionBitmask = 0x0E0; + static constexpr int kExtendedAdvertismentBitMask = 0x010; + static constexpr int kNumSlotsBitmask = 0x00F; + + Version version_ = Version::kUndefined; + bool extended_advertisement_ = false; + int num_slots_ = 0; + ByteArray service_id_bloom_filter_; + ByteArray advertisement_hash_; + int psm_ = 0; +}; + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_INTERNAL_MEDIUMS_BLE_V2_BLE_ADVERTISEMENT_HEADER_H_ diff --git a/internal/platform/byte_array.h b/internal/platform/byte_array.h index b9dba2d6..221961bd 100644 --- a/internal/platform/byte_array.h +++ b/internal/platform/byte_array.h @@ -23,6 +23,8 @@ #include #include +#include "absl/strings/str_cat.h" + namespace location { namespace nearby { @@ -92,6 +94,11 @@ class ByteArray { // operation. explicit operator std::string() && { return std::move(data_); } + // Returns the representation of the underlying data as a string view. + absl::string_view AsStringView() const { + return absl::string_view(data(), size()); + } + private: std::string data_; }; diff --git a/internal/platform/byte_array_test.cc b/internal/platform/byte_array_test.cc index 0084f029..366a3199 100644 --- a/internal/platform/byte_array_test.cc +++ b/internal/platform/byte_array_test.cc @@ -87,4 +87,12 @@ TEST(ByteArrayTest, CreateFromNonNullTerminatedStdArray) { EXPECT_EQ(std::string(bytes), std::string(data.data(), data.size())); } +TEST(ByteArrayTest, CreateFromAbslStringReturnsTheSame) { + const absl::string_view kTestString = "Test String"; + ByteArray bytes{std::string(kTestString)}; + + EXPECT_EQ(bytes.size(), kTestString.size()); + EXPECT_EQ(bytes.AsStringView(), kTestString); +} + } // namespace