// Copyright 2020-2023 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/ble_advertisement_header.h" #include #include #include "absl/strings/str_cat.h" #include "connections/implementation/flags/nearby_connections_feature_flags.h" #include "internal/flags/nearby_flags.h" #include "internal/platform/base64_utils.h" #include "internal/platform/byte_array.h" #include "internal/platform/logging.h" #include "internal/platform/stream_reader.h" namespace nearby { namespace connections { namespace mediums { // These definitions are necessary before C++17. constexpr int BleAdvertisementHeader::kAdvertisementHashByteLength; constexpr int BleAdvertisementHeader::kServiceIdBloomFilterByteLength; constexpr int BleAdvertisementHeader::kDefaultPsmValue; constexpr int BleAdvertisementHeader::kPsmValueByteLength; BleAdvertisementHeader::BleAdvertisementHeader( Version version, bool support_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() != kServiceIdBloomFilterByteLength || advertisement_hash.size() != kAdvertisementHashByteLength) { return; } version_ = version; support_extended_advertisement_ = support_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) { ByteArray advertisement_header_bytes = Base64Utils::Decode(ble_advertisement_header_bytes.AsStringView()); if (advertisement_header_bytes.Empty()) { // The BLE advertisement header is not encoded in base64, but still try to // parse it as raw bytes. if (ble_advertisement_header_bytes.size() == kMinAdvertisementHeaderLength || ble_advertisement_header_bytes.size() == kMinAdvertisementHeaderLength + 2) { advertisement_header_bytes = ble_advertisement_header_bytes; } else { VLOG(1) << "Cannot deserialize BLEAdvertisementHeader. " "Invalid advertising data."; return; } } if (advertisement_header_bytes.size() < kMinAdvertisementHeaderLength) { LOG(INFO) << "Cannot deserialize BleAdvertisementHeader: expecting min " << kMinAdvertisementHeaderLength << "raw bytes, got " << advertisement_header_bytes.size(); return; } StreamReader stream_reader(&advertisement_header_bytes); // The first 1 byte is supposed to be the version and number of slots. auto version_and_num_slots_byte = stream_reader.ReadUint8(); if (!version_and_num_slots_byte.has_value()) { LOG(INFO) << "Cannot deserialize BleAdvertisementHeader: version_and_num."; return; } // The upper 3 bits are supposed to be the version. version_ = static_cast( (*version_and_num_slots_byte & kVersionBitmask) >> 5); if (version_ != Version::kV2) { LOG(INFO) << "Cannot deserialize BleAdvertisementHeader: unsupported Version " << static_cast(version_); return; } // The next 1 bit is supposed to be the extended advertisement flag. support_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_ = stream_reader.ReadBytes(kServiceIdBloomFilterByteLength) .value_or(ByteArray()); // The next 4 bytes are supposed to be the advertisement_hash. advertisement_hash_ = stream_reader.ReadBytes(kAdvertisementHashByteLength) .value_or(ByteArray()); // The next 2 bytes are PSM value. if (stream_reader.IsAvailable(kPsmValueByteLength)) { psm_ = stream_reader.ReadInt16().value_or(0); } } 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(support_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_bytes{kPsmValueByteLength}; char* data = psm_bytes.data(); data[0] = (psm_ & 0xFF00) >> 8; 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_bytes)); // clang-format on return ByteArray(std::move(out)); } bool BleAdvertisementHeader::operator==( const BleAdvertisementHeader& rhs) const { return GetVersion() == rhs.GetVersion() && IsSupportExtendedAdvertisement() == rhs.IsSupportExtendedAdvertisement() && GetNumSlots() == rhs.GetNumSlots() && GetServiceIdBloomFilter() == rhs.GetServiceIdBloomFilter() && GetAdvertisementHash() == rhs.GetAdvertisementHash() && GetPsm() == rhs.GetPsm(); } } // namespace mediums } // namespace connections } // namespace nearby