Skip to content

Commit

Permalink
device/bluetooth: Introduce class bluetooth_utils::UUID.
Browse files Browse the repository at this point in the history
Added an opaque container for Bluetooth universally unique identifiers, which
are internally represented as a string. The class abstracts away conversion from
16 and 32-bit UUIDs to the 128-bit format using the existing
bluetooth_utils::CanonicalUuid function. This provides a more simple object
oriented interface for passing around UUIDs.

BUG=340524
TEST=device_unittests

Review URL: https://codereview.chromium.org/154883002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@249333 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
armansito@chromium.org committed Feb 6, 2014
1 parent 07f04ff commit 6e26d08
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 44 deletions.
94 changes: 74 additions & 20 deletions device/bluetooth/bluetooth_utils.cc
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,103 @@
#include "base/logging.h"
#include "base/strings/string_util.h"

namespace {
static const char* kCommonUuidPostfix = "-0000-1000-8000-00805f9b34fb";
static const char* kCommonUuidPrefix = "0000";
static const int kUuidSize = 36;
} // namespace

namespace device {
namespace bluetooth_utils {

std::string CanonicalUuid(std::string uuid) {
namespace {

const char* kCommonUuidPostfix = "-0000-1000-8000-00805f9b34fb";
const char* kCommonUuidPrefix = "0000";
const int kUuidSize = 36;

// Returns the canonical, 128-bit canonical, and the format of the UUID
// in |canonical|, |canonical_128|, and |format| based on |uuid|.
void GetCanonicalUuid(std::string uuid,
std::string* canonical,
std::string* canonical_128,
UUID::Format* format) {
// Initialize the values for the failure case.
canonical->clear();
canonical_128->clear();
*format = UUID::kFormatInvalid;

if (uuid.empty())
return std::string();
return;

if (uuid.size() < 11 && uuid.find("0x") == 0)
uuid = uuid.substr(2);

if (!(uuid.size() == 4 || uuid.size() == 8 || uuid.size() == 36))
return std::string();
return;

if (uuid.size() == 4 || uuid.size() == 8) {
for (size_t i = 0; i < uuid.size(); ++i) {
if (!IsHexDigit(uuid[i]))
return std::string();
return;
}

if (uuid.size() == 4)
return kCommonUuidPrefix + uuid + kCommonUuidPostfix;

return uuid + kCommonUuidPostfix;
if (uuid.size() == 4) {
canonical->assign(uuid);
canonical_128->assign(kCommonUuidPrefix + uuid + kCommonUuidPostfix);
*format = UUID::kFormat16Bit;
return;
}
canonical->assign(uuid);
canonical_128->assign(uuid + kCommonUuidPostfix);
*format = UUID::kFormat32Bit;
return;
}

std::string uuid_result(uuid);
for (int i = 0; i < kUuidSize; ++i) {
if (i == 8 || i == 13 || i == 18 || i == 23) {
if (uuid[i] != '-')
return std::string();
return;
} else {
if (!IsHexDigit(uuid[i]))
return std::string();
uuid_result[i] = tolower(uuid[i]);
return;
uuid[i] = tolower(uuid[i]);
}
}
return uuid_result;

canonical->assign(uuid);
canonical_128->assign(uuid);
*format = UUID::kFormat128Bit;
}

} // namespace


UUID::UUID(const std::string& uuid) {
GetCanonicalUuid(uuid, &value_, &canonical_value_, &format_);
}

UUID::UUID() : format_(kFormatInvalid) {
}

UUID::~UUID() {
}

bool UUID::IsValid() const {
return format_ != kFormatInvalid;
}

bool UUID::operator<(const UUID& uuid) const {
return canonical_value_ < uuid.canonical_value_;
}

bool UUID::operator==(const UUID& uuid) const {
return canonical_value_ == uuid.canonical_value_;
}

bool UUID::operator!=(const UUID& uuid) const {
return canonical_value_ != uuid.canonical_value_;
}

std::string CanonicalUuid(std::string uuid) {
std::string value;
std::string canonical_value;
UUID::Format format;
GetCanonicalUuid(uuid, &value, &canonical_value, &format);
return canonical_value;
}

} // namespace bluetooth_utils
Expand Down
78 changes: 78 additions & 0 deletions device/bluetooth/bluetooth_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,84 @@
namespace device {
namespace bluetooth_utils {

// Opaque wrapper around a Bluetooth UUID. Instances of UUID represent the
// 128-bit universally unique identifiers (UUIDs) of profiles and attributes
// used in Bluetooth based communication, such as a peripheral's services,
// characteristics, and characteristic descriptors. An instance are
// constructed using a string representing 16, 32, or 128 bit UUID formats.
class UUID {
public:
// Possible representation formats used during construction.
enum Format {
kFormatInvalid,
kFormat16Bit,
kFormat32Bit,
kFormat128Bit
};

// Single argument constructor. |uuid| can be a 16, 32, or 128 bit UUID
// represented as a 4, 8, or 36 character string with the following
// formats:
// XXXX
// 0xXXXX
// XXXXXXXX
// 0xXXXXXXXX
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
//
// 16 and 32 bit UUIDs will be internally converted to a 128 bit UUID using
// the base UUID defined in the Bluetooth specification, hence custom UUIDs
// should be provided in the 128-bit format. If |uuid| is in an unsupported
// format, the result might be invalid. Use IsValid to check for validity
// after construction.
explicit UUID(const std::string& uuid);
~UUID();

// Returns true, if the UUID is in a valid canonical format.
bool IsValid() const;

// Returns the representation format of the UUID. This reflects the format
// that was provided during construction.
Format format() const { return format_; }

// Returns the value of the UUID as a string. The representation format is
// based on what was passed in during construction. For the supported sizes,
// this representation can have the following formats:
// - 16 bit: XXXX
// - 32 bit: XXXXXXXX
// - 128 bit: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// where X is a lowercase hex digit.
const std::string& value() const { return value_; }

// Returns the underlying 128-bit value as a string in the following format:
// XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
// where X is a lowercase hex digit.
const std::string& canonical_value() const { return canonical_value_; }

// Permit sufficient comparison to allow a UUID to be used as a key in a
// std::map.
bool operator<(const UUID& uuid) const;

// Equality operators.
bool operator==(const UUID& uuid) const;
bool operator!=(const UUID& uuid) const;

private:
UUID();

// String representation of the UUID that was used during construction. For
// the supported sizes, this representation can have the following formats:
// - 16 bit: XXXX
// - 32 bit: XXXXXXXX
// - 128 bit: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
Format format_;
std::string value_;

// The 128-bit string representation of the UUID.
std::string canonical_value_;
};

// DEPRECATED. Use bluetooth_utils::UUID instead.
//
// Takes a 4, 8 or 36 character UUID, validates it and returns it in 36
// character format with all hex digits lower case. If |uuid| is invalid, the
// empty string is returned.
Expand Down
101 changes: 77 additions & 24 deletions device/bluetooth/bluetooth_utils_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,47 +7,100 @@
#include "testing/gtest/include/gtest/gtest.h"

namespace device {
namespace bluetooth_utils {

TEST(BluetoothUtilsTest, CanonicalUuid) {
// Does nothing for an already canonical UUID
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805f9b34fb"));
CanonicalUuid("00001101-0000-1000-8000-00805f9b34fb"));

// Rejects misformatted
EXPECT_EQ("", bluetooth_utils::CanonicalUuid("1101a"));
EXPECT_EQ("", bluetooth_utils::CanonicalUuid("Z101"));
EXPECT_EQ("", bluetooth_utils::CanonicalUuid("0000-1101"));
EXPECT_EQ("", bluetooth_utils::CanonicalUuid("0000Z101"));
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("0001101-0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("Z0001101-0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("00001101 0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("00001101-0000:1000-8000-00805f9b34fb"));
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("00001101-0000-1000;8000-00805f9b34fb"));
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000000805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("1101a"));
EXPECT_EQ("", CanonicalUuid("Z101"));
EXPECT_EQ("", CanonicalUuid("0000-1101"));
EXPECT_EQ("", CanonicalUuid("0000Z101"));
EXPECT_EQ("", CanonicalUuid("0001101-0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("Z0001101-0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("00001101 0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("00001101-0000:1000-8000-00805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("00001101-0000-1000;8000-00805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("00001101-0000-1000-8000000805f9b34fb"));

// Lower case
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
bluetooth_utils::CanonicalUuid("00001101-0000-1000-8000-00805F9B34FB"));
CanonicalUuid("00001101-0000-1000-8000-00805F9B34FB"));

// Short to full
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
bluetooth_utils::CanonicalUuid("1101"));
CanonicalUuid("1101"));
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
bluetooth_utils::CanonicalUuid("0x1101"));
CanonicalUuid("0x1101"));
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
bluetooth_utils::CanonicalUuid("00001101"));
CanonicalUuid("00001101"));
EXPECT_EQ("00001101-0000-1000-8000-00805f9b34fb",
bluetooth_utils::CanonicalUuid("0x00001101"));
CanonicalUuid("0x00001101"));

// No 0x prefix on 36 character
EXPECT_EQ("",
bluetooth_utils::CanonicalUuid("0x00001101-0000-1000-8000-00805f9b34fb"));
EXPECT_EQ("", CanonicalUuid("0x00001101-0000-1000-8000-00805f9b34fb"));
}

TEST(BluetoothUtilsTest, UUID) {
const char kValid128Bit0[] = "12345678-1234-5678-9abc-def123456789";
const char kValid128Bit1[] = "00001101-0000-1000-8000-00805f9b34fb";
const char kInvalid36Char[] = "1234567-1234-5678-9abc-def123456789";
const char kInvalid4Char[] = "Z101";
const char kValid16Bit[] = "0x1101";
const char kValid32Bit[] = "00001101";

// Valid 128-bit custom UUID.
UUID uuid0(kValid128Bit0);
EXPECT_TRUE(uuid0.IsValid());
EXPECT_EQ(UUID::kFormat128Bit, uuid0.format());
EXPECT_EQ(uuid0.value(), uuid0.canonical_value());

// Valid 128-bit UUID.
UUID uuid1(kValid128Bit1);
EXPECT_TRUE(uuid1.IsValid());
EXPECT_EQ(UUID::kFormat128Bit, uuid1.format());
EXPECT_EQ(uuid1.value(), uuid1.canonical_value());

EXPECT_NE(uuid0, uuid1);

// Invalid 128-bit UUID.
UUID uuid2(kInvalid36Char);
EXPECT_FALSE(uuid2.IsValid());
EXPECT_EQ(UUID::kFormatInvalid, uuid2.format());
EXPECT_TRUE(uuid2.value().empty());
EXPECT_TRUE(uuid2.canonical_value().empty());

// Invalid 16-bit UUID.
UUID uuid3(kInvalid4Char);
EXPECT_FALSE(uuid3.IsValid());
EXPECT_EQ(UUID::kFormatInvalid, uuid3.format());
EXPECT_TRUE(uuid3.value().empty());
EXPECT_TRUE(uuid3.canonical_value().empty());

// Valid 16-bit UUID.
UUID uuid4(kValid16Bit);
EXPECT_TRUE(uuid4.IsValid());
EXPECT_EQ(UUID::kFormat16Bit, uuid4.format());
EXPECT_NE(uuid4.value(), uuid4.canonical_value());
EXPECT_EQ("1101", uuid4.value());
EXPECT_EQ(kValid128Bit1, uuid4.canonical_value());

// Valid 32-bit UUID.
UUID uuid5(kValid32Bit);
EXPECT_TRUE(uuid5.IsValid());
EXPECT_EQ(UUID::kFormat32Bit, uuid5.format());
EXPECT_NE(uuid5.value(), uuid5.canonical_value());
EXPECT_EQ("00001101", uuid5.value());
EXPECT_EQ(kValid128Bit1, uuid5.canonical_value());

// uuid4, uuid5, and uuid1 are equivalent.
EXPECT_EQ(uuid4, uuid5);
EXPECT_EQ(uuid1, uuid4);
EXPECT_EQ(uuid1, uuid5);
}

} // namespace bluetooth_utils
} // namespace device

0 comments on commit 6e26d08

Please sign in to comment.