chromium/chromecast/device/bluetooth/bluetooth_util.cc

// 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/bluetooth_util.h"

#include "base/strings/stringprintf.h"
#include "third_party/abseil-cpp/absl/strings/ascii.h"

namespace chromecast {
namespace bluetooth {
namespace util {

namespace {

const int kMacAddrStrLen = 17;
const int kUuid16bitLen = 4;
const int kUuidHexNumChars = 32;
const int kUuidNumDashes = 4;

const char kFmtUuid[] =
    "%02hhx%02hhx%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx"
    "-%02hhx%02hhx-%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx";

const char kFmtUuidNoDashes[] =
    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
    "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx";

const char kFmtAddr[] = "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx";

}  // namespace

// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
const bluetooth_v2_shlib::Uuid kUuidBase = {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                                             0x10, 0x00, 0x80, 0x00, 0x00, 0x80,
                                             0x5F, 0x9B, 0x34, 0xFB}};

std::string AddrToString(const bluetooth_v2_shlib::Addr& addr) {
  return base::StringPrintf(kFmtAddr, addr[5], addr[4], addr[3], addr[2],
                            addr[1], addr[0]);
}

std::string AddrLastByteString(const bluetooth_v2_shlib::Addr& addr) {
  return base::StringPrintf("%02hhx", addr[0]);
}

bool ParseAddr(const std::string& str, bluetooth_v2_shlib::Addr* addr) {
  // sscanf will incorrectly succeed if all characters except the last one are
  // correct.
  if (str.size() != kMacAddrStrLen ||
      !absl::ascii_isxdigit(static_cast<unsigned char>(str.back()))) {
    return false;
  }

  int ret = sscanf(str.c_str(), kFmtAddr, &(*addr)[5], &(*addr)[4], &(*addr)[3],
                   &(*addr)[2], &(*addr)[1], &(*addr)[0]);

  return ret == static_cast<int>(addr->size());
}

std::string UuidToString(const bluetooth_v2_shlib::Uuid& uuid) {
  return base::StringPrintf(kFmtUuid, uuid[0], uuid[1], uuid[2], uuid[3],
                            uuid[4], uuid[5], uuid[6], uuid[7], uuid[8],
                            uuid[9], uuid[10], uuid[11], uuid[12], uuid[13],
                            uuid[14], uuid[15]);
}

bool ParseUuid(const std::string& str, bluetooth_v2_shlib::Uuid* uuid) {
  if (str.empty()) {
    return false;
  }

  for (char c : str) {
    if (c != '-' && !absl::ascii_isxdigit(static_cast<unsigned char>(c))) {
      return false;
    }
  }

  // Check for 16-bit UUID
  if (str.size() == kUuid16bitLen) {
    *uuid = kUuidBase;
    return sscanf(str.c_str(), "%02hhx%02hhx", &(*uuid)[2], &(*uuid)[3]) == 2;
  }

  if (str.size() > kUuidHexNumChars &&
      str.size() != kUuidHexNumChars + kUuidNumDashes) {
    return false;
  }

  std::string no_dashes = str;
  no_dashes.erase(std::remove(no_dashes.begin(), no_dashes.end(), '-'),
                  no_dashes.end());

  if (no_dashes.size() != kUuidHexNumChars) {
    return false;
  }

  int ret =
      sscanf(no_dashes.c_str(), kFmtUuidNoDashes, &(*uuid)[0], &(*uuid)[1],
             &(*uuid)[2], &(*uuid)[3], &(*uuid)[4], &(*uuid)[5], &(*uuid)[6],
             &(*uuid)[7], &(*uuid)[8], &(*uuid)[9], &(*uuid)[10], &(*uuid)[11],
             &(*uuid)[12], &(*uuid)[13], &(*uuid)[14], &(*uuid)[15]);

  return ret == static_cast<int>(uuid->size());
}

bluetooth_v2_shlib::Uuid UuidFromInt16(uint16_t uuid) {
  bluetooth_v2_shlib::Uuid ret = kUuidBase;
  ret[2] = (uuid >> 8) & 0xff;
  ret[3] = uuid & 0xff;
  return ret;
}

bluetooth_v2_shlib::Uuid UuidFromInt32(uint32_t uuid) {
  bluetooth_v2_shlib::Uuid ret = kUuidBase;
  ret[0] = (uuid >> 24) & 0xff;
  ret[1] = (uuid >> 16) & 0xff;
  ret[2] = (uuid >> 8) & 0xff;
  ret[3] = uuid & 0xff;
  return ret;
}

}  // namespace util
}  // namespace bluetooth
}  // namespace chromecast