blob: ece405a8bfdb82dda771ed8e8c2e2dea22849825 [file] [log] [blame]
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "bluetooth/common/util.h"
#include <newblue/bt.h>
#include <algorithm>
#include <regex> // NOLINT(build/c++11)
#include <base/files/file_util.h>
#include <base/stl_util.h>
#include <base/strings/stringprintf.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/string_number_conversions.h>
namespace {
constexpr char kNewblueConfigFile[] = "/var/lib/bluetooth/newblue";
uint64_t GetNumFromLE(const uint8_t* buf, uint8_t bits) {
uint64_t val = 0;
uint8_t bytes = bits / 8;
CHECK(buf);
buf += bytes;
while (bytes--)
val = (val << 8) | *--buf;
return val;
}
} // namespace
namespace bluetooth {
// True if the kernel is configured to split LE traffic.
bool IsBleSplitterEnabled() {
std::string content;
// LE splitter is enabled iff /var/lib/bluetooth/newblue starts with "1".
if (base::ReadFileToString(base::FilePath(kNewblueConfigFile), &content)) {
base::TrimWhitespaceASCII(content, base::TRIM_TRAILING, &content);
if (content == "1")
return true;
}
// Current LE splitter default = disabled.
return false;
}
// Turns the content of |buf| into a uint16_t in host order. This should be used
// when reading the little-endian data from Bluetooth packet.
uint16_t GetNumFromLE16(const uint8_t* buf) {
return static_cast<uint16_t>(GetNumFromLE(buf, 16));
}
// Turns the content of |buf| into a uint32_t in host order. This should be used
// when reading the little-endian data from Bluetooth packet.
uint32_t GetNumFromLE24(const uint8_t* buf) {
return static_cast<uint32_t>(GetNumFromLE(buf, 24));
}
// Reverses the content of |buf| and returns bytes in big-endian order. This
// should be used when reading the little-endian data from Bluetooth packet.
std::vector<uint8_t> GetBytesFromLE(const uint8_t* buf, size_t buf_len) {
std::vector<uint8_t> ret;
CHECK(buf);
if (!buf_len)
return ret;
ret.assign(buf, buf + buf_len);
std::reverse(ret.begin(), ret.end());
return ret;
}
UniqueId GetNextId() {
static UniqueId next_id = 1;
uint64_t id = next_id++;
if (id)
return id;
next_id -= 1;
LOG(ERROR) << "Run out of unique IDs";
return 0;
}
bool ConvertToBtAddr(bool is_random_address,
const std::string& address,
struct bt_addr* result) {
CHECK(result);
std::vector<std::string> tokens = base::SplitString(
address, ":", base::KEEP_WHITESPACE, base::SPLIT_WANT_ALL);
if (tokens.size() != BT_MAC_LEN)
return false;
uint8_t addr[BT_MAC_LEN];
uint8_t* ptr = addr + BT_MAC_LEN;
for (const auto& token : tokens) {
uint32_t value;
if (token.size() != 2 || !base::HexStringToUInt(token, &value))
return false;
*(--ptr) = static_cast<uint8_t>(value);
}
memcpy(result->addr, addr, BT_MAC_LEN);
result->type =
is_random_address ? BT_ADDR_TYPE_LE_RANDOM : BT_ADDR_TYPE_LE_PUBLIC;
return true;
}
bool TrimAdapterFromObjectPath(std::string* path) {
std::regex rgx("^/org/bluez/hci[0-9]+$");
std::smatch match;
if (!std::regex_search(*path, match, rgx) || match.size() != 1)
return false;
path->clear();
return true;
}
std::string TrimDeviceFromObjectPath(std::string* device) {
std::regex rgx("/dev_([0-9a-fA-F]{2}_){5}[0-9a-fA-F]{2}$");
std::smatch m;
std::string address;
if (!std::regex_search(*device, m, rgx) || m.empty())
return "";
address = m.str(0).substr(5);
std::replace(address.begin(), address.end(), '_', ':');
*device = device->substr(0, device->size() - m.str(0).size());
return address;
}
int32_t TrimServiceFromObjectPath(std::string* service) {
std::regex rgx("/service[0-9a-fA-F]{4}$");
std::smatch m;
std::string srv;
if (!std::regex_search(*service, m, rgx) || m.empty())
return kInvalidServiceHandle;
srv = m.str(0).substr(8, 4);
*service = service->substr(0, service->size() - m.str(0).size());
return std::stol(srv, nullptr, 16);
}
int32_t TrimCharacteristicFromObjectPath(std::string* characteristic) {
std::regex rgx("/char[0-9a-fA-F]{4}$");
std::smatch m;
std::string charac;
if (!std::regex_search(*characteristic, m, rgx) || m.empty())
return kInvalidCharacteristicHandle;
charac = m.str(0).substr(5, 4);
*characteristic =
characteristic->substr(0, characteristic->size() - m.str(0).size());
return std::stol(charac, nullptr, 16);
}
int32_t TrimDescriptorFromObjectPath(std::string* descriptor) {
std::regex rgx("/descriptor[0-9a-fA-F]{4}$");
std::smatch m;
std::string desc;
if (!std::regex_search(*descriptor, m, rgx) || m.empty())
return kInvalidDescriptorHandle;
desc = m.str(0).substr(11);
*descriptor = descriptor->substr(0, descriptor->size() - m.str(0).size());
return std::stol(desc, nullptr, 16);
}
std::string ConvertDeviceObjectPathToAddress(const std::string& path) {
std::string p(path);
std::string address = TrimDeviceFromObjectPath(&p);
if (address.empty() || p.empty())
return "";
if (!TrimAdapterFromObjectPath(&p))
return "";
return address;
}
std::string ConvertDeviceAddressToObjectPath(const std::string& address) {
std::string path;
if (address.empty())
return "";
path = base::StringPrintf("%s/dev_%s", kAdapterObjectPath, address.c_str());
std::replace(path.begin(), path.end(), ':', '_');
return path;
}
bool ConvertServiceObjectPathToHandle(std::string* address,
uint16_t* handle,
const std::string& path) {
std::string p(path);
std::string addr;
int32_t h = TrimServiceFromObjectPath(&p);
if (h == kInvalidServiceHandle || p.empty())
return false;
addr = ConvertDeviceObjectPathToAddress(p);
if (addr.empty())
return false;
*address = addr;
*handle = h;
return true;
}
std::string ConvertServiceHandleToObjectPath(const std::string& address,
uint16_t handle) {
std::string dev = ConvertDeviceAddressToObjectPath(address);
std::string s = base::StringPrintf("/service%04X", handle);
if (dev.empty() || s.empty())
return "";
return dev + s;
}
bool ConvertCharacteristicObjectPathToHandles(std::string* address,
uint16_t* service_handle,
uint16_t* char_handle,
const std::string& path) {
std::string p(path);
std::string addr;
uint16_t sh;
int32_t ch = TrimCharacteristicFromObjectPath(&p);
if (ch == kInvalidCharacteristicHandle || p.empty())
return false;
if (!ConvertServiceObjectPathToHandle(&addr, &sh, p))
return false;
*address = addr;
*service_handle = sh;
*char_handle = ch;
return true;
}
std::string ConvertCharacteristicHandleToObjectPath(const std::string& address,
uint16_t service_handle,
uint16_t char_handle) {
std::string s = ConvertServiceHandleToObjectPath(address, service_handle);
std::string c = base::StringPrintf("/char%04X", char_handle);
if (s.empty() || c.empty())
return "";
return s + c;
}
bool ConvertDescriptorObjectPathToHandles(std::string* address,
uint16_t* service_handle,
uint16_t* char_handle,
uint16_t* desc_handle,
const std::string& path) {
std::string p(path);
std::string addr;
uint16_t sh;
uint16_t ch;
int32_t dh = TrimDescriptorFromObjectPath(&p);
if (dh == kInvalidDescriptorHandle || p.empty())
return false;
if (!ConvertCharacteristicObjectPathToHandles(&addr, &sh, &ch, p))
return false;
*address = addr;
*service_handle = sh;
*char_handle = ch;
*desc_handle = dh;
return true;
}
std::string ConvertDescriptorHandleToObjectPath(const std::string& address,
uint16_t service_handle,
uint16_t char_handle,
uint16_t desc_handle) {
std::string c = ConvertCharacteristicHandleToObjectPath(
address, service_handle, char_handle);
std::string d = base::StringPrintf("/descriptor%04X", desc_handle);
if (c.empty() || d.empty())
return "";
return c + d;
}
void OnInterfaceExported(std::string object_path,
std::string interface_name,
bool success) {
VLOG(1) << "Completed interface export " << interface_name << " of object "
<< object_path << ", success = " << success;
}
} // namespace bluetooth