// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromecast/device/bluetooth/le/le_scan_result.h"
#include <algorithm>
#include "base/logging.h"
#include "chromecast/device/bluetooth/bluetooth_util.h"
namespace chromecast {
namespace bluetooth {
namespace {
template <typename T, bluetooth_v2_shlib::Uuid (*converter)(T)>
std::optional<LeScanResult::UuidList> GetUuidsFromShort(
const std::map<uint8_t, std::vector<std::vector<uint8_t>>>& type_to_data,
uint8_t type) {
auto it = type_to_data.find(type);
if (it == type_to_data.end()) {
return std::nullopt;
}
LeScanResult::UuidList ret;
for (const auto& field : it->second) {
if (field.size() % sizeof(T)) {
LOG(ERROR) << "Invalid length, expected multiple of " << sizeof(T);
return std::nullopt;
}
for (size_t i = 0; i < field.size(); i += sizeof(T)) {
// Uuids are transmitted in little endian byte order. (Bluetooth Core
// Specification v4.0 Vol 3 Part C Section 11.1).
T value = 0;
for (size_t j = 0; j < sizeof(T); ++j) {
value |= field[i + j] << 8 * j;
}
ret.push_back(converter(value));
}
}
return ret;
}
std::optional<LeScanResult::UuidList> GetUuidsAsUuid(
const std::map<uint8_t, std::vector<std::vector<uint8_t>>>& type_to_data,
uint8_t type) {
auto it = type_to_data.find(type);
if (it == type_to_data.end()) {
return std::nullopt;
}
LeScanResult::UuidList ret;
for (const auto& field : it->second) {
if (field.size() % sizeof(bluetooth_v2_shlib::Uuid)) {
LOG(ERROR) << "Invalid length, expected multiple of "
<< sizeof(bluetooth_v2_shlib::Uuid);
return std::nullopt;
}
for (size_t i = 0; i < field.size();
i += sizeof(bluetooth_v2_shlib::Uuid)) {
ret.emplace_back();
// GAP UUIDs are little endian and bluetooth_v2_shlib::Uuid is big endian.
std::reverse_copy(field.begin() + i,
field.begin() + i + sizeof(bluetooth_v2_shlib::Uuid),
ret.back().begin());
}
}
return ret;
}
} // namespace
LeScanResult::LeScanResult() = default;
LeScanResult::LeScanResult(const LeScanResult& other) = default;
LeScanResult::~LeScanResult() = default;
bool LeScanResult::SetAdvData(base::span<const uint8_t> advertisement_data) {
std::map<uint8_t, std::vector<std::vector<uint8_t>>> new_type_to_data;
size_t i = 0;
while (i < advertisement_data.size()) {
if (i + 1 == advertisement_data.size()) {
LOG(ERROR) << "Malformed BLE packet";
return false;
}
// http://www.argenox.com/bluetooth-low-energy-ble-v4-0-development/library/a-ble-advertising-primer/
// Format:
// [size][type][payload ]
// [i ][i+1 ][i+2:i+1+size]
//
// Note: size does not include its own byte
uint8_t size = advertisement_data[i];
uint8_t type = advertisement_data[i + 1];
// Avoid infinite loop if invalid data
if (size == 0 || i + 1 + size > advertisement_data.size()) {
LOG(ERROR) << "Invalid size";
return false;
}
base::span<const uint8_t> data =
advertisement_data.subspan(i + 2, size - 1);
new_type_to_data[type].emplace_back(data.begin(), data.end());
i += (size + 1);
}
adv_data.assign(advertisement_data.begin(), advertisement_data.end());
type_to_data.swap(new_type_to_data);
return true;
}
std::optional<std::string> LeScanResult::Name() const {
auto it = type_to_data.find(kGapCompleteName);
if (it != type_to_data.end()) {
DCHECK_GE(it->second.size(), 1u);
return std::string(reinterpret_cast<const char*>(it->second[0].data()),
it->second[0].size());
}
it = type_to_data.find(kGapShortName);
if (it != type_to_data.end()) {
DCHECK_GE(it->second.size(), 1u);
return std::string(reinterpret_cast<const char*>(it->second[0].data()),
it->second[0].size());
}
return std::nullopt;
}
std::optional<uint8_t> LeScanResult::Flags() const {
auto it = type_to_data.find(kGapFlags);
if (it == type_to_data.end()) {
return std::nullopt;
}
DCHECK_GE(it->second.size(), 1u);
if (it->second[0].size() != 1) {
LOG(ERROR) << "Invalid length for flags";
return std::nullopt;
}
return it->second[0][0];
}
std::optional<LeScanResult::UuidList> LeScanResult::AllServiceUuids() const {
bool any_exist = false;
UuidList ret;
auto insert_if_exists = [&ret, &any_exist](std::optional<UuidList> list) {
if (list) {
any_exist = true;
ret.insert(ret.end(), list->begin(), list->end());
}
};
insert_if_exists(IncompleteListOf16BitServiceUuids());
insert_if_exists(CompleteListOf16BitServiceUuids());
insert_if_exists(IncompleteListOf32BitServiceUuids());
insert_if_exists(CompleteListOf32BitServiceUuids());
insert_if_exists(IncompleteListOf128BitServiceUuids());
insert_if_exists(CompleteListOf128BitServiceUuids());
if (!any_exist) {
return std::nullopt;
}
return ret;
}
std::optional<LeScanResult::UuidList>
LeScanResult::IncompleteListOf16BitServiceUuids() const {
return GetUuidsFromShort<uint16_t, util::UuidFromInt16>(
type_to_data, kGapIncomplete16BitServiceUuids);
}
std::optional<LeScanResult::UuidList>
LeScanResult::CompleteListOf16BitServiceUuids() const {
return GetUuidsFromShort<uint16_t, util::UuidFromInt16>(
type_to_data, kGapComplete16BitServiceUuids);
}
std::optional<LeScanResult::UuidList>
LeScanResult::IncompleteListOf32BitServiceUuids() const {
return GetUuidsFromShort<uint32_t, util::UuidFromInt32>(
type_to_data, kGapIncomplete32BitServiceUuids);
}
std::optional<LeScanResult::UuidList>
LeScanResult::CompleteListOf32BitServiceUuids() const {
return GetUuidsFromShort<uint32_t, util::UuidFromInt32>(
type_to_data, kGapComplete32BitServiceUuids);
}
std::optional<LeScanResult::UuidList>
LeScanResult::IncompleteListOf128BitServiceUuids() const {
return GetUuidsAsUuid(type_to_data, kGapIncomplete128BitServiceUuids);
}
std::optional<LeScanResult::UuidList>
LeScanResult::CompleteListOf128BitServiceUuids() const {
return GetUuidsAsUuid(type_to_data, kGapComplete128BitServiceUuids);
}
LeScanResult::ServiceDataMap LeScanResult::AllServiceData() const {
ServiceDataMap ret;
auto sd16 = ServiceData16Bit();
ret.insert(sd16.begin(), sd16.end());
auto sd32 = ServiceData32Bit();
ret.insert(sd32.begin(), sd32.end());
auto sd128 = ServiceData128Bit();
ret.insert(sd128.begin(), sd128.end());
return ret;
}
LeScanResult::ServiceDataMap LeScanResult::ServiceData16Bit() const {
ServiceDataMap ret;
auto it = type_to_data.find(kGapServicesData16bit);
if (it == type_to_data.end()) {
return ret;
}
for (const auto& data : it->second) {
uint16_t uuid = 0;
if (data.size() < sizeof(uuid)) {
LOG(ERROR) << "Invalid service data, too short";
ret.clear();
return ret;
}
uuid = data[1] << 8 | data[0];
ret[util::UuidFromInt16(uuid)] =
std::vector<uint8_t>(data.begin() + sizeof(uuid), data.end());
}
return ret;
}
LeScanResult::ServiceDataMap LeScanResult::ServiceData32Bit() const {
ServiceDataMap ret;
auto it = type_to_data.find(kGapServicesData32bit);
if (it == type_to_data.end()) {
return ret;
}
for (const auto& data : it->second) {
uint32_t uuid = 0;
if (data.size() < sizeof(uuid)) {
LOG(ERROR) << "Invalid service data, too short";
ret.clear();
return ret;
}
uuid = data[3] << 24 | data[2] << 16 | data[1] << 8 | data[0];
ret[util::UuidFromInt32(uuid)].assign(data.begin() + sizeof(uuid),
data.end());
}
return ret;
}
LeScanResult::ServiceDataMap LeScanResult::ServiceData128Bit() const {
ServiceDataMap ret;
auto it = type_to_data.find(kGapServicesData128bit);
if (it == type_to_data.end()) {
return ret;
}
for (const auto& data : it->second) {
bluetooth_v2_shlib::Uuid uuid;
if (data.size() < sizeof(uuid)) {
LOG(ERROR) << "Invalid service data, too short";
ret.clear();
return ret;
}
std::reverse_copy(data.begin(), data.begin() + sizeof(uuid), uuid.begin());
ret[uuid].assign(data.begin() + sizeof(uuid), data.end());
}
return ret;
}
std::map<uint16_t, std::vector<uint8_t>> LeScanResult::ManufacturerData()
const {
std::map<uint16_t, std::vector<uint8_t>> ret;
auto it = type_to_data.find(kGapManufacturerData);
if (it == type_to_data.end()) {
return ret;
}
for (const auto& data : it->second) {
uint16_t id = 0;
if (data.size() < sizeof(id)) {
LOG(ERROR) << "Invalid manufacturer data, too short";
ret.clear();
return ret;
}
id = data[1] << 8 | data[0];
ret[id].assign(data.begin() + sizeof(id), data.end());
}
return ret;
}
} // namespace bluetooth
} // namespace chromecast