Files
nearby/cpp/core/internal/mediums/ble_advertisement.cc
T
Alexey Polyudov 18c09c5abe Fix build and tests
Signed-off-by: Alexey Polyudov <apolyudov@google.com>
Change-Id: I3671e40fd0f8f028e3c8e46c4a47de0d2cc9aeff
2020-04-04 12:55:06 -07:00

291 lines
10 KiB
C++

#include "core/internal/mediums/ble_advertisement.h"
#include <cstring>
#include "platform/logging.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
const std::uint32_t BLEAdvertisement::kServiceIdHashLength = 3;
const std::uint32_t BLEAdvertisement::kVersionLength = 1;
// Length of one int. Be sure to re-evaluate how we compute data size in this
// class if this constant ever changes!
const std::uint32_t BLEAdvertisement::kDataSizeLength = 4;
const std::uint32_t BLEAdvertisement::kMinAdvertisementLength =
kVersionLength + kServiceIdHashLength + kDataSizeLength;
// The maximum length for a GATT characteristic value is 512 bytes, so make sure
// the entire advertisement is less than that. The data can take up whatever
// space is remaining after the bytes preceding it.
const std::uint32_t BLEAdvertisement::kMaxDataSize =
512 - kMinAdvertisementLength;
const std::uint16_t BLEAdvertisement::kVersionBitmask = 0x0E0;
const std::uint16_t BLEAdvertisement::kSocketVersionBitmask = 0x01C;
ConstPtr<BLEAdvertisement> BLEAdvertisement::fromBytes(
ConstPtr<ByteArray> ble_advertisement_bytes) {
if (ble_advertisement_bytes.isNull()) {
NEARBY_LOG(INFO,
"Cannot deserialize BLEAdvertisement: null bytes passed in");
return ConstPtr<BLEAdvertisement>();
}
if (ble_advertisement_bytes->size() < kMinAdvertisementLength) {
NEARBY_LOG(INFO,
"Cannot deserialize BLEAdvertisement: expecting min %u raw "
"bytes, got %zu",
kMinAdvertisementLength, ble_advertisement_bytes->size());
return ConstPtr<BLEAdvertisement>();
}
// Now, time to read the bytes!
const char *ble_advertisement_bytes_read_ptr =
ble_advertisement_bytes->getData();
// 1. Version.
Version::Value version = parseVersionFromByte(
static_cast<std::uint16_t>(*ble_advertisement_bytes_read_ptr));
if (!isSupportedVersion(version)) {
NEARBY_LOG(INFO,
"Cannot deserialize BLEAdvertisement: unsupported Version %u",
version);
return ConstPtr<BLEAdvertisement>();
}
// 2. Socket Version.
SocketVersion::Value socket_version = parseSocketVersionFromByte(
static_cast<std::uint16_t>(*ble_advertisement_bytes_read_ptr));
if (!isSupportedSocketVersion(socket_version)) {
NEARBY_LOG(
INFO,
"Cannot deserialize BLEAdvertisement: unsupported SocketVersion %u",
socket_version);
return ConstPtr<BLEAdvertisement>();
}
ble_advertisement_bytes_read_ptr += kVersionLength;
// 3. Service ID hash.
ScopedPtr<ConstPtr<ByteArray> > scoped_service_id_hash(MakeConstPtr(
new ByteArray(ble_advertisement_bytes_read_ptr, kServiceIdHashLength)));
ble_advertisement_bytes_read_ptr += kServiceIdHashLength;
// 4.1. Data size.
size_t expected_data_size =
deserializeDataSize(ble_advertisement_bytes_read_ptr);
if (expected_data_size < 0) {
NEARBY_LOG(INFO,
"Cannot deserialize BLEAdvertisement: negative data size %zu",
expected_data_size);
return ConstPtr<BLEAdvertisement>();
}
ble_advertisement_bytes_read_ptr += kDataSizeLength;
// Check that the stated data size is the same as what we received.
size_t actual_data_size = computeDataSize(ble_advertisement_bytes);
if (actual_data_size < expected_data_size) {
NEARBY_LOG(INFO,
"Cannot deserialize BLEAdvertisement: expected data to be %zu "
"bytes, got %zu bytes",
expected_data_size, actual_data_size);
return ConstPtr<BLEAdvertisement>();
}
// 4.2. Data.
ScopedPtr<ConstPtr<ByteArray> > scoped_data(MakeConstPtr(
new ByteArray(ble_advertisement_bytes_read_ptr, expected_data_size)));
ble_advertisement_bytes_read_ptr += expected_data_size;
return MakeRefCountedConstPtr(new BLEAdvertisement(
version, socket_version, scoped_service_id_hash.release(),
scoped_data.release()));
}
ConstPtr<ByteArray> BLEAdvertisement::toBytes(
Version::Value version, SocketVersion::Value socket_version,
ConstPtr<ByteArray> service_id_hash, ConstPtr<ByteArray> data) {
// Check that the given input is valid.
if (!isSupportedVersion(version)) {
NEARBY_LOG(INFO,
"Cannot serialize BLEAdvertisement: unsupported Version %u",
version);
return ConstPtr<ByteArray>();
}
if (!isSupportedSocketVersion(socket_version)) {
NEARBY_LOG(
INFO, "Cannot serialize BLEAdvertisement: unsupported SocketVersion %u",
socket_version);
return ConstPtr<ByteArray>();
}
if (service_id_hash->size() != kServiceIdHashLength) {
NEARBY_LOG(INFO,
"Cannot serialize BLEAdvertisement: expected a service_id_hash "
"of %u bytes, but got %zu",
kServiceIdHashLength, service_id_hash->size());
return ConstPtr<ByteArray>();
}
if (data->size() > kMaxDataSize) {
NEARBY_LOG(INFO,
"Cannot serialize BLEAdvertisement: expected data of at most %u "
"bytes, but got %zu",
kMaxDataSize, data->size());
return ConstPtr<ByteArray>();
}
// Initialize the bytes.
size_t advertisement_length = computeAdvertisementLength(data);
Ptr<ByteArray> advertisement_bytes{new ByteArray{advertisement_length}};
char *advertisement_bytes_write_ptr = advertisement_bytes->getData();
// 1. Version.
serializeVersionByte(advertisement_bytes_write_ptr, version);
// 2. SocketVersion.
serializeSocketVersionByte(advertisement_bytes_write_ptr, socket_version);
advertisement_bytes_write_ptr += kVersionLength;
// 3. Service ID hash.
memcpy(advertisement_bytes_write_ptr, service_id_hash->getData(),
kServiceIdHashLength);
advertisement_bytes_write_ptr += kServiceIdHashLength;
// 4.1. Data length.
serializeDataSize(advertisement_bytes_write_ptr, data->size());
advertisement_bytes_write_ptr += kDataSizeLength;
// 4.2. Data.
memcpy(advertisement_bytes_write_ptr, data->getData(), data->size());
advertisement_bytes_write_ptr += data->size();
return ConstifyPtr(advertisement_bytes);
}
bool BLEAdvertisement::isSupportedVersion(Version::Value version) {
return version >= Version::V1 && version <= Version::V2;
}
bool BLEAdvertisement::isSupportedSocketVersion(
SocketVersion::Value socket_version) {
return socket_version >= SocketVersion::V1 &&
socket_version <= SocketVersion::V2;
}
BLEAdvertisement::Version::Value BLEAdvertisement::parseVersionFromByte(
std::uint16_t byte) {
return static_cast<BLEAdvertisement::Version::Value>(
(byte & kVersionBitmask) >> 5);
}
BLEAdvertisement::SocketVersion::Value
BLEAdvertisement::parseSocketVersionFromByte(std::uint16_t byte) {
return static_cast<SocketVersion::Value>((byte & kSocketVersionBitmask) >> 2);
}
size_t BLEAdvertisement::deserializeDataSize(
const char *data_size_bytes_read_ptr) {
// Allocate a chunk of memory to store our deserialized size.
char data_size_bytes[kDataSizeLength];
// Assign the bits of our size from the given raw bytes, keeping in mind that
// we need to convert from Big Endian to Little Endian in the process.
for (int i = 0; i < kDataSizeLength; ++i) {
data_size_bytes[i] = data_size_bytes_read_ptr[kDataSizeLength - i - 1];
}
// Interpret the char array as a single int.
return static_cast<size_t>(
*(reinterpret_cast<std::uint32_t *>(&data_size_bytes)));
}
size_t BLEAdvertisement::computeDataSize(
ConstPtr<ByteArray> ble_advertisement_bytes) {
return ble_advertisement_bytes->size() - kMinAdvertisementLength;
}
size_t BLEAdvertisement::computeAdvertisementLength(ConstPtr<ByteArray> data) {
// The advertisement length is the minimum length + the length of the data.
return kMinAdvertisementLength + data->size();
}
void BLEAdvertisement::serializeVersionByte(char *version_byte_write_ptr,
Version::Value version) {
*version_byte_write_ptr |=
static_cast<char>((version << 5) & kVersionBitmask);
}
void BLEAdvertisement::serializeSocketVersionByte(
char *socket_version_byte_write_ptr, SocketVersion::Value socket_version) {
*socket_version_byte_write_ptr |=
static_cast<char>((socket_version << 2) & kSocketVersionBitmask);
}
void BLEAdvertisement::serializeDataSize(char *data_size_bytes_write_ptr,
size_t data_size) {
// Get a raw representation of the data size bytes in memory.
char *data_size_bytes = reinterpret_cast<char *>(&data_size);
// Append these raw bytes to advertisement bytes, keeping in mind that we need
// to convert from Little Endian to Big Endian in the process.
for (int i = 0; i < kDataSizeLength; ++i) {
data_size_bytes_write_ptr[i] = data_size_bytes[kDataSizeLength - i - 1];
}
}
BLEAdvertisement::BLEAdvertisement(Version::Value version,
SocketVersion::Value socket_version,
ConstPtr<ByteArray> service_id_hash,
ConstPtr<ByteArray> data)
: version_(version),
socket_version_(socket_version),
service_id_hash_(service_id_hash),
data_(data) {}
BLEAdvertisement::~BLEAdvertisement() {
// Nothing to do.
}
BLEAdvertisement::Version::Value BLEAdvertisement::getVersion() const {
return version_;
}
BLEAdvertisement::SocketVersion::Value BLEAdvertisement::getSocketVersion()
const {
return socket_version_;
}
ConstPtr<ByteArray> BLEAdvertisement::getServiceIdHash() const {
return service_id_hash_.get();
}
ConstPtr<ByteArray> BLEAdvertisement::getData() const { return data_.get(); }
bool BLEAdvertisement::operator==(const BLEAdvertisement &rhs) const {
return this->getVersion() == rhs.getVersion() &&
this->getSocketVersion() == rhs.getSocketVersion() &&
*(this->getServiceIdHash()) == *(rhs.getServiceIdHash()) &&
*(this->getData()) == *(rhs.getData());
}
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());
}
return *(this->getData()) < *(rhs.getData());
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location