[Bluetooth] Add UUID class 17/225017/28
authorPawel Wasowski <p.wasowski2@samsung.com>
Fri, 28 Feb 2020 13:02:25 +0000 (14:02 +0100)
committerPawel Wasowski <p.wasowski2@samsung.com>
Fri, 28 Feb 2020 13:02:25 +0000 (14:02 +0100)
UUID class will handle validation of string UUIDs and conversions
between different UUID formats.

Verification: unit tests (added in the next commit) pass rate: 100%
Change-Id: Ie075379d7c5a4567646c04097888d5ae2f560b5b
Signed-off-by: Pawel Wasowski <p.wasowski2@samsung.com>
src/bluetooth/bluetooth.gyp
src/bluetooth/uuid.cc [new file with mode: 0644]
src/bluetooth/uuid.h [new file with mode: 0644]

index 8981306356549b430fe181517db3a263a3f06aa7..07c310ea00db7b74f8e2a21855840a796e851a9e 100644 (file)
@@ -41,6 +41,8 @@
         'bluetooth_util.h',
         'bluetooth_le_device.cc',
         'bluetooth_le_device.h',
+        'uuid.cc',
+        'uuid.h'
       ],
       'includes': [
         '../common/pkg-config.gypi',
diff --git a/src/bluetooth/uuid.cc b/src/bluetooth/uuid.cc
new file mode 100644 (file)
index 0000000..b692b88
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    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
+ *
+ *        http://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 <regex>
+#include <string>
+
+#include "bluetooth_util.h"
+#include "common/scope_exit.h"
+#include "common/tools.h"
+#include "uuid.h"
+
+using common::PlatformResult;
+using common::ErrorCode;
+
+namespace {
+
+const std::regex kCanonical128BitFormat{
+    "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}"};
+const std::regex k16BitFormat{"[0-9a-fA-F]{4}"};
+const std::regex k32BitFormat{"[0-9a-fA-F]{8}"};
+const std::regex kConvertibleTo16Bit{"0000[0-9a-fA-F]{4}-0000-1000-8000-00805[fF]9[bB]34[fF][bB]"};
+const std::regex kConvertibleTo32Bit{"[0-9a-fA-F]{8}-0000-1000-8000-00805[fF]9[bB]34[fF][bB]"};
+const std::string kBaseUuidLast96Bits = "-0000-1000-8000-00805f9b34fb";
+const optional<extension::bluetooth::UUID> NO_UUID{};
+const optional<std::string> NO_UUID_STR{};
+
+const int k16BitUUIDBegin = 4;
+const int k16BitUUIDLen = 4;
+const int k32BitUUIDBegin = 0;
+const int k32BitUUIDLen = 8;
+
+};  // unnamed namespace
+
+namespace extension {
+
+namespace bluetooth {
+
+UUID::UUID(std::string&& uuid_128_bit) : uuid_128_bit{uuid_128_bit} {
+  ScopeLogger("Input: %s", uuid_128_bit.c_str());
+}
+
+optional<UUID> UUID::create(const std::string& uuid) {
+  ScopeLogger("Input: %s", uuid.c_str());
+
+  auto uuid_128_bit = To128Bit(uuid);
+  if (uuid_128_bit.empty()) {
+    return NO_UUID;
+  }
+
+  return UUID{std::move(uuid_128_bit)};
+}
+
+optional<UUID> UUID::createFromGatt(const bt_gatt_h gatt_handle) {
+  ScopeLogger("Input: <a bt_gatt_h>");
+
+  char* uuid_buffer{nullptr};
+  SCOPE_EXIT {
+    free(uuid_buffer);
+  };
+
+  auto ret = bt_gatt_get_uuid(gatt_handle, &uuid_buffer);
+  if (BT_ERROR_NONE != ret) {
+    LoggerE("Couldn't get UUID. Error: %s", util::GetBluetoothErrorMessage(ret).c_str());
+    return NO_UUID;
+  }
+
+  if (!uuid_buffer) {
+    LoggerE("Couldn't get UUID. Returned uuid is a nullptr");
+    return NO_UUID;
+  }
+
+  LoggerD("uuid_buffer: %s", uuid_buffer);
+
+  return {create(std::string{uuid_buffer})};
+}
+
+optional<std::string> UUID::To16Bit() const {
+  ScopeLogger("128 bit UUID: %s", uuid_128_bit.c_str());
+
+  if (std::regex_match(uuid_128_bit, kConvertibleTo16Bit)) {
+    return {uuid_128_bit.substr(k16BitUUIDBegin, k16BitUUIDLen)};
+  }
+
+  return NO_UUID_STR;
+}
+
+optional<std::string> UUID::To32Bit() const {
+  ScopeLogger("128 bit UUID: %s", uuid_128_bit.c_str());
+
+  if (std::regex_match(uuid_128_bit, kConvertibleTo32Bit)) {
+    return {uuid_128_bit.substr(k32BitUUIDBegin, k32BitUUIDLen)};
+  }
+
+  return NO_UUID_STR;
+}
+
+std::string UUID::ShortestPossibleFormat() const {
+  ScopeLogger("128 bit UUID: %s", uuid_128_bit.c_str());
+
+  if (std::regex_match(uuid_128_bit, kConvertibleTo16Bit)) {
+    return {uuid_128_bit.substr(k16BitUUIDBegin, k16BitUUIDLen)};
+  }
+
+  if (std::regex_match(uuid_128_bit, kConvertibleTo32Bit)) {
+    return {uuid_128_bit.substr(k32BitUUIDBegin, k32BitUUIDLen)};
+  }
+
+  return uuid_128_bit;
+}
+
+bool UUID::operator==(const UUID& other) const {
+  return uuid_128_bit == other.uuid_128_bit;
+}
+
+UUID::Format UUID::GetFormat(const std::string& uuid) {
+  ScopeLogger("uuid: %s", uuid.c_str());
+
+  if (std::regex_match(uuid, k16BitFormat)) {
+    return Format::UUID_16_BIT;
+  } else if (std::regex_match(uuid, k32BitFormat)) {
+    return Format::UUID_32_BIT;
+  } else if (std::regex_match(uuid, kCanonical128BitFormat)) {
+    return Format::UUID_128_BIT;
+  }
+
+  return Format::UUID_INVALID;
+}
+
+std::string UUID::To128Bit(const std::string& uuid) {
+  ScopeLogger("uuid: %s", uuid.c_str());
+
+  auto uuid_lower_case = common::tools::ConvertToLowerCase(uuid);
+  switch (GetFormat(uuid_lower_case)) {
+    case Format::UUID_16_BIT:
+      return "0000" + uuid_lower_case + kBaseUuidLast96Bits;
+    case Format::UUID_32_BIT:
+      return uuid_lower_case + kBaseUuidLast96Bits;
+    case Format::UUID_128_BIT:
+      return uuid_lower_case;
+    default:
+      LoggerE("Error: uuid format unknown");
+      return "";
+  }
+}
+
+}  // bluetooth
+}  // extension
diff --git a/src/bluetooth/uuid.h b/src/bluetooth/uuid.h
new file mode 100644 (file)
index 0000000..ddfeb47
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    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
+ *
+ *        http://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.
+ */
+
+#ifndef UUID_H_
+#define UUID_H_
+
+#include <string>
+
+#include <bluetooth.h>
+
+#include "common/platform_result.h"
+#if __cplusplus > 201402L
+#include <optional>
+using std::optional;
+#else
+#include "common/optional.h"
+using common::optional;
+#endif
+
+using common::PlatformResult;
+
+namespace extension {
+
+namespace bluetooth {
+
+struct UUID {
+  /*
+   * A UUID object is only created, if the string containing it
+   * is in a proper format or it can be created from the bt_gatt_h.
+   *
+   * create() functions accept the following formats of strings:
+   * 1. 4 hexadecimal digits, e.g.: "018D"
+   * 2. 8 hexadecimal digits, e.g.: "018Dab17"
+   * 3. 128 bit canonical UUID format defined in
+   * https://tools.ietf.org/html/rfc4122, e.g.:
+   * "f95c593e-efd4-43c4-96e4-97186ee52f83"
+   */
+  static optional<UUID> create(const std::string&);
+
+  /*
+   * This function does not validate passed bt_gatt_h.
+   * If the bt_gatt_h is not initialized, the function will likely crash.
+   *
+   * This function was purposedly not made an overload of create().
+   * If it was an overload, (const) char* could be implicitly converted to
+   * const void* (instead of std::string), which is the underlying type of
+   * bt_gatt_h.
+   */
+  static optional<UUID> createFromGatt(const bt_gatt_h);
+
+  UUID() = delete;
+  UUID(const UUID& uuid) = default;
+  UUID(UUID&& uuid) = default;
+
+  /*
+   * Only UUIDs built with kBaseUUID are convertible to 32 bit format
+   * and only a subset of them are convertible to 16 bit format.
+   *
+   * These functions always return hexadecimal lower case letters as digits.
+   */
+  optional<std::string> To16Bit() const;
+  optional<std::string> To32Bit() const;
+
+  /*
+   * This function checks if the UUID is built with kBaseUUID and tries to
+   * return the UUID in different formats in the following order:
+   * 1. 16 bit
+   * 2. 32 bit
+   * 3. 128 bit
+   */
+  std::string ShortestPossibleFormat() const;
+
+  bool operator==(const UUID&) const;
+  /*
+   * Hexadecimal letter digits are always stored in lower case.
+   */
+  const std::string uuid_128_bit;
+
+ private:
+  /*
+   * These functions don't validate passed arguments.
+   */
+  UUID(std::string&& uuid_128_bit);
+  static std::string To128Bit(const std::string& uuid);
+
+  enum class Format : int { UUID_16_BIT, UUID_32_BIT, UUID_128_BIT, UUID_INVALID };
+
+  static Format GetFormat(const std::string& uuid);
+};
+
+}  // bluetooth
+}  // extension
+
+#endif  // UUID_H_