[BLE Refactor] Implement CreateAdvertisementHeader in BleV2 medium.

PiperOrigin-RevId: 429550598
This commit is contained in:
edwinwu
2022-02-18 06:30:09 -08:00
committed by Copybara-Service
parent ca172feb55
commit 8326aeef38
12 changed files with 590 additions and 2 deletions
+14
View File
@@ -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"])
+90
View File
@@ -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 <string>
#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
+82
View File
@@ -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 <cstdint>
#include <string>
#include <utility>
#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<std::string, ByteArray> 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<std::string, ByteArray> 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<int, std::pair<std::string, ByteArray>>
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_
@@ -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 <inttypes.h>
#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<char>(base_input_stream.ReadUint8());
// The upper 3 bits are supposed to be the version.
version_ =
static_cast<Version>((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<int>(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<int>(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<char>(version_) << 5) & kVersionBitmask;
// The next 1 bit is extended advertisement flag.
version_and_num_slots_byte |=
(static_cast<char>(extended_advertisement_) << 4) &
kExtendedAdvertismentBitMask;
// The next 5 bits are the number of slots.
version_and_num_slots_byte |=
static_cast<char>(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
@@ -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 <string>
#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_