OSDN Git Service

Add DataElementReader for SDP
authorAjay Panicker <apanicke@google.com>
Fri, 14 Sep 2018 22:10:13 +0000 (15:10 -0700)
committerAjay Panicker <apanicke@google.com>
Mon, 8 Oct 2018 17:35:48 +0000 (10:35 -0700)
DataElementReader extracts DataElement's from packets which are the
basic container for all data in the SDP protocol.

Bug: 114751344
Test: run host test bluetooth_test_sdp
Change-Id: If01f22d81af1b80e7c9ac8993ee161eba779bfa3

profile/sdp/Android.bp [new file with mode: 0644]
profile/sdp/common/data_element_reader.cc [new file with mode: 0644]
profile/sdp/common/data_element_reader.h [new file with mode: 0644]
profile/sdp/common/test/data_element_reader_test.cc [new file with mode: 0644]
profile/sdp/sdp_common.h [new file with mode: 0644]
profile/sdp/sdp_logging_helper.h [new file with mode: 0644]

diff --git a/profile/sdp/Android.bp b/profile/sdp/Android.bp
new file mode 100644 (file)
index 0000000..4c794df
--- /dev/null
@@ -0,0 +1,34 @@
+cc_library_static {
+  name: "sdp_service",
+  defaults: ["fluoride_defaults"],
+  host_supported: true,
+  include_dirs: [
+    "system/bt/",
+  ],
+  srcs: [
+    "common/data_element_reader.cc",
+  ],
+  static_libs: [
+    "lib-bt-packets",
+    "libbluetooth-types",
+  ],
+}
+
+cc_test {
+  name: "bluetooth_test_sdp",
+  test_suites: ["general-tests"],
+  defaults: ["fluoride_defaults"],
+  host_supported: true,
+  include_dirs: [
+    "system/bt/",
+  ],
+  srcs: [
+    "common/test/data_element_reader_test.cc",
+  ],
+  static_libs: [
+    "libgmock",
+    "sdp_service",
+    "lib-bt-packets",
+    "libbluetooth-types",
+  ],
+}
diff --git a/profile/sdp/common/data_element_reader.cc b/profile/sdp/common/data_element_reader.cc
new file mode 100644 (file)
index 0000000..675100f
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 "data_element_reader.h"
+
+#include <base/logging.h>
+#include <type_traits>
+
+#include "sdp_logging_helper.h"
+
+// A helper macro that can verify that there is enough data remaining in the
+// reader to extract without overflowing. end_ - it_ should never be negative
+// so casting it to a size_t is always safe. If it does fail, set it_ to end_
+// so that all additional readings fail.
+#define CHECK_REMAINING_LEN(x)                                              \
+  do {                                                                      \
+    if ((size_t)(end_ - it_) < x) {                                         \
+      LOG(WARNING) << __func__ << ": Extract would read past end of data."; \
+      return ParseFail();                                                   \
+    }                                                                       \
+  } while (0)
+
+namespace bluetooth {
+namespace sdp {
+
+DataElementReader::DataElement DataElementReader::ReadNext() {
+  if (it_ > end_) LOG(FATAL) << "Beginning of buffer is past end of buffer.";
+  if (it_ == end_) return std::monostate();
+
+  uint8_t descriptor = *it_++;
+  DataElementType type = static_cast<DataElementType>(descriptor >> 3);
+  DataElementSize size = static_cast<DataElementSize>(descriptor & 0b00000111);
+
+  // All types with a value greater than URL are currently reserved.
+  if (type > DataElementType::MAX_VALUE) {
+    LOG(WARNING) << __func__ << ": Trying to use a reserved data element type";
+    return ParseFail();
+  }
+
+  switch (type) {
+    case DataElementType::BOOLEAN:
+      if (size != DataElementSize::BYTE1) {
+        LOG(WARNING) << __func__ << ": Invalid size for bool: " << size;
+        return ParseFail();
+      }
+
+      CHECK_REMAINING_LEN(1);
+      return (it_.extract<uint8_t>() != 0);
+    case DataElementType::SIGNED_INT:
+      return ReadSignedInt(size);
+    case DataElementType::UNSIGNED_INT:
+      return ReadUnsignedInt(size);
+    case DataElementType::UUID:
+      return ReadUuid(size);
+    case DataElementType::STRING:
+      return ReadString(size);
+    case DataElementType::DATA_ELEMENT_SEQUENCE:
+      return ReadSequence(size);
+    default:
+      // TODO: The other data element types are never used in the previous SDP
+      // implementation. We should properly handle them in the future though
+      // for completeness.
+      LOG(ERROR) << __func__ << ": Unhandled Data Element Type: " << type;
+  }
+
+  return ParseFail();
+}
+
+DataElementReader::DataElement DataElementReader::ParseFail() {
+  it_ = end_;
+  return std::monostate();
+}
+
+template <class IntegerType>
+DataElementReader::DataElement DataElementReader::ReadInteger() {
+  static_assert(std::is_integral<IntegerType>::value,
+                "ReadInteger requires an integral type.");
+
+  CHECK_REMAINING_LEN(sizeof(IntegerType));
+  return it_.extractBE<IntegerType>();
+}
+
+DataElementReader::DataElement DataElementReader::ReadLargeInt() {
+  CHECK_REMAINING_LEN(16);
+
+  std::array<uint8_t, 16> array;
+  for (size_t i = 0; i < sizeof(uint8_t[16]); i++) {
+    array[i] = it_.extract<uint8_t>();
+  }
+
+  return array;
+}
+
+DataElementReader::DataElement DataElementReader::ReadSignedInt(
+    DataElementSize size) {
+  switch (size) {
+    case DataElementSize::BYTE1:
+      return ReadInteger<int8_t>();
+    case DataElementSize::BYTE2:
+      return ReadInteger<int16_t>();
+    case DataElementSize::BYTE4:
+      return ReadInteger<int32_t>();
+    case DataElementSize::BYTE8:
+      return ReadInteger<int64_t>();
+    case DataElementSize::BYTE16:
+      return ReadLargeInt();
+    default:
+      LOG(WARNING) << __func__ << ": Invalid size for int: " << size;
+  }
+
+  return ParseFail();
+}
+
+DataElementReader::DataElement DataElementReader::ReadUnsignedInt(
+    DataElementSize size) {
+  switch (size) {
+    case DataElementSize::BYTE1:
+      return ReadInteger<uint8_t>();
+    case DataElementSize::BYTE2:
+      return ReadInteger<uint16_t>();
+    case DataElementSize::BYTE4:
+      return ReadInteger<uint32_t>();
+    case DataElementSize::BYTE8:
+      return ReadInteger<uint64_t>();
+    case DataElementSize::BYTE16:
+      return ReadLargeInt();
+    default:
+      LOG(WARNING) << __func__ << ": Invalid size for uint: " << size;
+  }
+
+  return ParseFail();
+}
+
+DataElementReader::DataElement DataElementReader::ReadUuid(
+    DataElementSize size) {
+  if (size == DataElementSize::BYTE2) {
+    CHECK_REMAINING_LEN(2);
+    return Uuid::From16Bit(it_.extractBE<uint16_t>());
+  }
+
+  if (size == DataElementSize::BYTE4) {
+    CHECK_REMAINING_LEN(4);
+    return Uuid::From32Bit(it_.extractBE<uint32_t>());
+  }
+
+  if (size == DataElementSize::BYTE16) {
+    CHECK_REMAINING_LEN(16);
+
+    Uuid::UUID128Bit uuid_array;
+    for (int i = 0; i < 16; i++) {
+      uuid_array[i] = it_.extract<uint8_t>();
+    }
+
+    return Uuid::From128BitBE(uuid_array);
+  }
+
+  LOG(WARNING) << __func__ << ": Invalid size for UUID: " << size;
+  return ParseFail();
+}
+
+DataElementReader::DataElement DataElementReader::ReadString(
+    DataElementSize size) {
+  uint32_t num_bytes = 0;
+
+  switch (size) {
+    case DataElementSize::ADDITIONAL_8BIT:
+      CHECK_REMAINING_LEN(1);
+      num_bytes = it_.extractBE<uint8_t>();
+      break;
+    case DataElementSize::ADDITIONAL_16BIT:
+      CHECK_REMAINING_LEN(2);
+      num_bytes = it_.extractBE<uint16_t>();
+      break;
+    case DataElementSize::ADDITIONAL_32BIT:
+      CHECK_REMAINING_LEN(4);
+      num_bytes = it_.extractBE<uint32_t>();
+      break;
+    default:
+      LOG(WARNING) << __func__ << ": Invalid size for string: " << size;
+      return ParseFail();
+  }
+
+  CHECK_REMAINING_LEN(num_bytes);
+
+  std::string str;
+  for (uint32_t i = 0; i < num_bytes; i++) {
+    str.push_back(it_.extractBE<uint8_t>());
+  }
+
+  return str;
+}
+
+DataElementReader::DataElement DataElementReader::ReadSequence(
+    DataElementSize size) {
+  uint32_t num_bytes = 0;
+
+  switch (size) {
+    case DataElementSize::ADDITIONAL_8BIT:
+      CHECK_REMAINING_LEN(1);
+      num_bytes = it_.extractBE<uint8_t>();
+      break;
+    case DataElementSize::ADDITIONAL_16BIT:
+      CHECK_REMAINING_LEN(2);
+      num_bytes = it_.extractBE<uint16_t>();
+      break;
+    case DataElementSize::ADDITIONAL_32BIT:
+      CHECK_REMAINING_LEN(4);
+      num_bytes = it_.extractBE<uint32_t>();
+      break;
+    default:
+      LOG(WARNING) << __func__ << ": Invalid size for string: " << size;
+      return ParseFail();
+  }
+
+  CHECK_REMAINING_LEN(num_bytes);
+
+  // Create a parser that points to the beginning of the next sequence and move
+  // the iterator to past the end of the new sequence.
+  auto&& temp = DataElementReader(it_, it_ + num_bytes);
+  it_ += num_bytes;
+  return std::move(temp);
+}
+
+}  // namespace sdp
+}  // namespace bluetooth
diff --git a/profile/sdp/common/data_element_reader.h b/profile/sdp/common/data_element_reader.h
new file mode 100644 (file)
index 0000000..13346bd
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <array>
+#include <variant>
+
+#include "bluetooth/uuid.h"
+#include "packet.h"
+#include "sdp_common.h"
+#include "stack/include/bt_types.h"
+
+namespace bluetooth {
+namespace sdp {
+
+// A helper class that helps extract data element objects from SDP packets.
+class DataElementReader {
+ public:
+  // If the DataElement contains monostate, that means parsing has failed.
+  using DataElement =
+      std::variant<std::monostate, bool, int8_t, int16_t, int32_t, int64_t,
+                   uint8_t, uint16_t, uint32_t, uint64_t, Octet16, Uuid,
+                   std::string, DataElementReader>;
+
+  DataElementReader(Iterator begin, Iterator end) : it_(begin), end_(end){};
+
+  // Get the next Data Element in the data. If reading fails for any reason,
+  // the DataElementReader becomes invalid and will continuously fail to read
+  // from that point onward.
+  DataElement ReadNext();
+
+ private:
+  // Extraction Helpers
+  DataElement ParseFail();
+  template <class IntegerType>
+  DataElement ReadInteger();
+  DataElement ReadLargeInt();
+
+  // Extraction Functions
+  DataElement ReadSignedInt(DataElementSize size);
+  DataElement ReadUnsignedInt(DataElementSize size);
+  DataElement ReadUuid(DataElementSize size);
+  DataElement ReadString(DataElementSize size);
+  DataElement ReadSequence(DataElementSize size);
+
+  Iterator it_;
+  Iterator end_;
+};
+
+}  // namespace sdp
+}  // namespace bluetooth
diff --git a/profile/sdp/common/test/data_element_reader_test.cc b/profile/sdp/common/test/data_element_reader_test.cc
new file mode 100644 (file)
index 0000000..9361ac7
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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 <base/logging.h>
+#include <gtest/gtest.h>
+
+#include "common/data_element_reader.h"
+
+namespace bluetooth {
+namespace sdp {
+
+using namespace testing;
+using DataElement = DataElementReader::DataElement;
+
+// A helper class to help work with the Data Element classes.
+class ReaderPacket : public ::bluetooth::Packet {
+ public:
+  using Packet::Packet;
+
+  static std::shared_ptr<ReaderPacket> Make(std::vector<uint8_t> payload) {
+    auto pkt = std::shared_ptr<ReaderPacket>(new ReaderPacket());
+    pkt->packet_start_index_ = 0;
+    pkt->packet_end_index_ = payload.size();
+    pkt->data_ = std::make_shared<std::vector<uint8_t>>(std::move(payload));
+    return pkt;
+  }
+
+  std::string ToString() const override { return ""; }
+  bool IsValid() const override { return true; }
+  std::pair<size_t, size_t> GetPayloadIndecies() const override {
+    return std::pair<size_t, size_t>(packet_start_index_, packet_end_index_);
+  }
+};
+
+bool operator!=(DataElementReader a, DataElementReader b);
+
+// A helper function to help compare DataElementReader objects.
+bool operator==(DataElementReader a, DataElementReader b) {
+  while (true) {
+    DataElement a_elem = a.ReadNext();
+    DataElement b_elem = b.ReadNext();
+
+    if (a_elem != b_elem) return false;
+
+    // If we get here that means both a and b have reached the end.
+    if (a_elem == DataElement(std::monostate())) break;
+  }
+
+  return true;
+}
+
+bool operator!=(DataElementReader a, DataElementReader b) { return !(a == b); }
+
+// A helper function to convert a type and a size to a descriptor byte.
+constexpr uint8_t Desc(DataElementType t, DataElementSize s) {
+  return static_cast<uint8_t>(t) << 3 | static_cast<uint8_t>(s);
+}
+
+// Helper that can create a Data Element reader from a vector.
+DataElementReader CreateReader(std::vector<uint8_t> payload) {
+  auto packet = ReaderPacket::Make(std::move(payload));
+  return DataElementReader(packet->begin(), packet->end());
+}
+
+// Test all the valid cases of reading the next Data Element.
+using ValidTestParam = std::tuple<std::vector<uint8_t>, DataElement>;
+class ValidReadTest : public TestWithParam<ValidTestParam> {};
+
+std::vector<ValidTestParam> valid_values = {
+    // Boolean Tests
+    ValidTestParam{
+        {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01},
+        true,
+    },
+    ValidTestParam{
+        {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00},
+        false,
+    },
+
+    // Signed Integer Tests
+    ValidTestParam{
+        {Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE1), 0xFF},
+        static_cast<int8_t>(-1)},
+    ValidTestParam{
+        {Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0xFF, 0xFF},
+        static_cast<int16_t>(-1)},
+    ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE4),
+                    0xFF, 0xFF, 0xFF, 0xFF},
+                   static_cast<int32_t>(-1)},
+    ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE8),
+                    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+                   static_cast<int64_t>(-1)},
+    ValidTestParam{{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE16),
+                    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF},
+                   std::array<uint8_t, 16>{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                                           0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
+                                           0xFF, 0xFF, 0xFF, 0xFF}},
+
+    // Unsigned Integer Tests
+    ValidTestParam{
+        {Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE1), 0x01},
+        static_cast<uint8_t>(1)},
+    ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE2),
+                    0x00, 0x01},
+                   static_cast<uint16_t>(1)},
+    ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE4),
+                    0x00, 0x00, 0x00, 0x01},
+                   static_cast<uint32_t>(1)},
+    ValidTestParam{{Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE8),
+                    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01},
+                   static_cast<uint64_t>(1)},
+    ValidTestParam{
+        {Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE16), 0x00,
+         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+         0x00, 0x00, 0x01},
+        std::array<uint8_t, 16>{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                                0x01}},
+
+    // UUID Tests
+    ValidTestParam{
+        {Desc(DataElementType::UUID, DataElementSize::BYTE2), 0x01, 0x02},
+        Uuid::From16Bit(0x0102)},
+    ValidTestParam{{Desc(DataElementType::UUID, DataElementSize::BYTE4), 0x01,
+                    0x02, 0x03, 0x04},
+                   Uuid::From32Bit(0x01020304)},
+    ValidTestParam{
+        {Desc(DataElementType::UUID, DataElementSize::BYTE16), 0x00, 0x01, 0x02,
+         0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E,
+         0x0F},
+        Uuid::From128BitBE({0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+                            0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F})},
+
+    // String Tests
+    ValidTestParam{
+        {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT), 0x05,
+         'T', 'e', 's', 't', '1'},
+        std::string("Test1")},
+    ValidTestParam{
+        {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_16BIT), 0x00,
+         0x05, 'T', 'e', 's', 't', '2'},
+        std::string("Test2")},
+    ValidTestParam{
+        {Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_32BIT), 0x00,
+         0x00, 0x00, 0x05, 'T', 'e', 's', 't', '3'},
+        std::string("Test3")},
+
+    // Nested Data Element List Tests
+    ValidTestParam{
+        {Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+              DataElementSize::ADDITIONAL_8BIT),
+         0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01,
+         Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00},
+        CreateReader(
+            {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01,
+             Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})},
+    ValidTestParam{
+        {Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+              DataElementSize::ADDITIONAL_16BIT),
+         0x00, 0x04, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1),
+         0x01, Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00},
+        CreateReader(
+            {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01,
+             Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})},
+    ValidTestParam{
+        {Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+              DataElementSize::ADDITIONAL_32BIT),
+         0x00, 0x00, 0x00, 0x04,
+         Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01,
+         Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00},
+        CreateReader(
+            {Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01,
+             Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00})},
+};
+
+INSTANTIATE_TEST_CASE_P(ReadNext, ValidReadTest, ValuesIn(valid_values));
+TEST_P(ValidReadTest, Test) {
+  auto packet = ReaderPacket::Make(std::get<0>(GetParam()));
+  auto value = std::get<1>(GetParam());
+
+  DataElementReader reader(packet->begin(), packet->end());
+  auto read_value = reader.ReadNext();
+
+  ASSERT_EQ(value, read_value);
+
+  // Test that there is no additional data to read.
+  ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate()));
+}
+
+// Test that a nested reader is correctly bounded and can't read past its
+// defined end.
+TEST(ReadNext, BoundedSubreaderTest) {
+  std::vector<uint8_t> payload = {
+      // Subsequence descriptor byte.
+      Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+           DataElementSize::ADDITIONAL_8BIT),
+      // Subsequence length.
+      0x04,
+      // Subsequence that contains two booleans with values true and false.
+      Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x01,
+      Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1), 0x00,
+      // Additional int16 at the end of the original sequence.
+      Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2), 0x01, 0x23};
+
+  auto packet = ReaderPacket::Make(payload);
+  DataElementReader reader(packet->begin(), packet->end());
+
+  // The first thing read should be the subsequence.
+  auto data_element = reader.ReadNext();
+  ASSERT_TRUE(std::holds_alternative<DataElementReader>(data_element));
+
+  // Check that the subsequence matches the premade sequence.
+  auto subreader = std::get<DataElementReader>(data_element);
+  data_element = subreader.ReadNext();
+  ASSERT_TRUE(std::holds_alternative<bool>(data_element));
+  ASSERT_TRUE(std::get<bool>(data_element));
+  data_element = subreader.ReadNext();
+  ASSERT_TRUE(std::holds_alternative<bool>(data_element));
+  ASSERT_FALSE(std::get<bool>(data_element));
+
+  // Check that there is no additional data to be read from the subreader.
+  ASSERT_EQ(subreader.ReadNext(), DataElement(std::monostate()));
+
+  // Check that we can still read the int16 from the original reader.
+  data_element = reader.ReadNext();
+  ASSERT_TRUE(std::holds_alternative<int16_t>(data_element));
+  auto int16_value = std::get<int16_t>(data_element);
+  ASSERT_EQ(int16_value, 0x0123);
+
+  // Check that there is no additional data to be read from the base reader.
+  ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate()));
+}
+
+// Test that trying to read an empty packet fails.
+TEST(ReadNext, NoDataTest) {
+  auto packet = ReaderPacket::Make({});
+  DataElementReader reader(packet->begin(), packet->end());
+
+  ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate()));
+}
+
+// Test that using a reserved value for type fails.
+TEST(ReadNext, InvalidTypeTest) {
+  auto packet = ReaderPacket::Make({0xFF});
+  DataElementReader reader(packet->begin(), packet->end());
+
+  ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate()));
+}
+
+// Test all invalid parses due to incorrect lengths or invalid sizes. All tests
+// should return std::monostate.
+using InvalidTestParam = std::vector<uint8_t>;
+class InvalidReadTest : public TestWithParam<InvalidTestParam> {};
+
+std::vector<InvalidTestParam> invalid_values = {
+    // Boolean Tests:
+    //   Invalid size field.
+    InvalidTestParam{
+        Desc(DataElementType::BOOLEAN, DataElementSize::BYTE2),
+    },
+    //   Insufficient data.
+    InvalidTestParam{Desc(DataElementType::BOOLEAN, DataElementSize::BYTE1)},
+
+    // Signed Integer Tests:
+    //   Invalid size field.
+    InvalidTestParam{
+        Desc(DataElementType::SIGNED_INT, DataElementSize::ADDITIONAL_8BIT)},
+    //   1 byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE1)},
+    //   2 byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE2),
+                     0x00},
+    //   4 byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE4),
+                     0x00, 0x00, 0x00},
+    //  8 Byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE8),
+                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+    //  16 Byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::SIGNED_INT, DataElementSize::BYTE16),
+                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                     0x00, 0x00, 0x00},
+
+    // Unsigned Integer Tests:
+    //   Invalid size field.
+    InvalidTestParam{
+        Desc(DataElementType::UNSIGNED_INT, DataElementSize::ADDITIONAL_8BIT)},
+    //   1 byte insufficient data.
+    InvalidTestParam{
+        Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE1)},
+    //   2 byte insufficient data.
+    InvalidTestParam{
+        Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE2), 0x00},
+    //   4 byte insufficient data.
+    InvalidTestParam{
+        Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE4), 0x00, 0x00,
+        0x00},
+    //  8 Byte insufficient data.
+    InvalidTestParam{
+        Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE8), 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00},
+    //  16 Byte insufficient data.
+    InvalidTestParam{
+        Desc(DataElementType::UNSIGNED_INT, DataElementSize::BYTE16), 0x00,
+        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+
+    // UUID Tests:
+    //   Invalid size field.
+    InvalidTestParam{
+        Desc(DataElementType::UUID, DataElementSize::ADDITIONAL_8BIT)},
+    //   2 byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE2), 0x00},
+    //   4 byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE4), 0x00,
+                     0x00, 0x00},
+    //  16 Byte insufficient data.
+    InvalidTestParam{Desc(DataElementType::UUID, DataElementSize::BYTE16), 0x00,
+                     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                     0x00, 0x00},
+
+    // String Tests:
+    //   Invalid size field.
+    InvalidTestParam{Desc(DataElementType::STRING, DataElementSize::BYTE1)},
+    //   Insufficient data for additional 8 bits len.
+    InvalidTestParam{
+        Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT)},
+    //   Insufficient data for additional 16 bits len.
+    InvalidTestParam{
+        Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_16BIT),
+        0x00,
+    },
+    //   Insufficient data for additional 32 bit len.
+    InvalidTestParam{
+        Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_32BIT),
+        0x00,
+        0x00,
+        0x00,
+    },
+    //   Insufficient data for reported length.
+    InvalidTestParam{
+        Desc(DataElementType::STRING, DataElementSize::ADDITIONAL_8BIT), 0x04,
+        '1', '2', '3'},
+
+    // Nested Data Element List Tests:
+    //   Invalid size field.
+    InvalidTestParam{
+        Desc(DataElementType::DATA_ELEMENT_SEQUENCE, DataElementSize::BYTE1)},
+    //   Insufficient data for additional 8 bits len.
+    InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+                          DataElementSize::ADDITIONAL_8BIT)},
+    //   Insufficient data for additional 16 bits len.
+    InvalidTestParam{
+        Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+             DataElementSize::ADDITIONAL_16BIT),
+        0x00,
+    },
+    //   Insufficient data for additional 32 bit len.
+    InvalidTestParam{
+        Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+             DataElementSize::ADDITIONAL_32BIT),
+        0x00,
+        0x00,
+        0x00,
+    },
+    //   Insufficient data for reported length.
+    InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_SEQUENCE,
+                          DataElementSize::ADDITIONAL_8BIT),
+                     0x04, 0x00, 0x00, 0x00},
+
+    // Unhandled Data Element Types Tests:
+    // NOTE: These tests should go away as we begin to handle the types.
+    //   Nil Type.
+    InvalidTestParam{Desc(DataElementType::NIL, DataElementSize::BYTE1)},
+    //   Data Element Alternative List Type.
+    InvalidTestParam{Desc(DataElementType::DATA_ELEMENT_ALTERNATIVE,
+                          DataElementSize::ADDITIONAL_8BIT),
+                     0x00},
+    //   URL Type.
+    InvalidTestParam{
+        Desc(DataElementType::URL, DataElementSize::ADDITIONAL_8BIT), 0x00}};
+
+INSTANTIATE_TEST_CASE_P(ReadNext, InvalidReadTest, ValuesIn(invalid_values));
+TEST_P(InvalidReadTest, Test) {
+  auto packet = ReaderPacket::Make(GetParam());
+  DataElementReader reader(packet->begin(), packet->end());
+
+  ASSERT_EQ(reader.ReadNext(), DataElement(std::monostate()));
+}
+
+// Test that trying to read from a reader with start > end crashes.
+TEST(DataElementReader, BadBoundsDeathTest) {
+  auto packet = ReaderPacket::Make({0x00, 0x00, 0x00, 0x00});
+  DataElementReader reader(packet->end(), packet->begin());
+  ASSERT_DEATH(reader.ReadNext(), "Beginning of buffer is past end of buffer.");
+}
+
+}  // namespace sdp
+}  // namespace bluetooth
diff --git a/profile/sdp/sdp_common.h b/profile/sdp/sdp_common.h
new file mode 100644 (file)
index 0000000..4a07a63
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+namespace bluetooth {
+namespace sdp {
+
+enum class PduId : uint8_t {
+  RESERVED = 0x00,
+  ERROR = 0x01,
+  SERVICE_SEARCH_REQUEST = 0x02,
+  SERVICE_SEARCH_RESPONSE = 0x03,
+  SERVICE_ATTRIBUTE_REQUEST = 0x04,
+  SERVICE_ATTRIBUTE_RESPONSE = 0x05,
+  SERVICE_SEARCH_ATTRIBUTE_REQUEST = 0x06,
+  SERVICE_SEARCH_ATTRIBUTE_RESPONSE = 0x07,
+  MAX_VALUE = 0x07,
+};
+
+enum class AttributeId : uint16_t {
+  SERVICE_RECORD_HANDLE = 0x0000,
+  SERVICE_CLASS_ID_LIST = 0x0001,
+  SERVICE_RECORD_STATE = 0x0002,
+  SERVICE_ID = 0x0003,
+  PROTOCOL_DESCRIPTOR_LIST = 0x0004,
+  BROWSE_GROUP_LIST = 0x0005,
+  LANGUAGE_BASE_ATTRIBUTE_ID_LIST = 0x0006,
+  SERVICE_INFO_TIME_TO_LIVE = 0x0007,
+  SERVICE_AVAILABILITY = 0x0008,
+  PROFILE_DESCRIPTOR_LIST = 0x0009,
+  DOCUMENTATION_URL = 0x000A,
+  CLIENT_EXECUTABLE_URL = 0x000B,
+  ICON_URL = 0x000C,
+  ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST = 0x000D,
+
+  // The following attributes are only used in the SDP server service record.
+  // They are only valid if ServiceDiscoveryServerServiceClassID is in the
+  // ServiceClassIDList. See Bluetooth Core v5.0 Section 5.2.
+  VERSION_NUMBER_LIST = 0x0200,
+  SERVICE_DATABASE_STATE = 0x0201,
+};
+
+// The Attribute ID's of these attributes are calculated by adding the offset
+// value for the attribute to the attribute ID base (contained in the
+// LanguageBaseAttributeIDList attribute value).
+enum AttributeIdOffset : uint16_t {
+  SERVICE_NAME = 0x0000,
+  SERVICE_DESCRIPTION = 0x0001,
+  PROVIDER_NAME = 0x0002,
+};
+
+// Constant that define the different types of data element.
+enum class DataElementType : uint8_t {
+  NIL = 0x00,
+  UNSIGNED_INT = 0x01,
+  SIGNED_INT = 0x02,
+  UUID = 0x03,
+  STRING = 0x04,
+  BOOLEAN = 0x05,
+  DATA_ELEMENT_SEQUENCE = 0x06,
+  DATA_ELEMENT_ALTERNATIVE = 0x07,
+  URL = 0x08,
+  MAX_VALUE = 0x08,
+};
+
+// Constant that define the different sizes of data element.
+enum class DataElementSize : uint8_t {
+  BYTE1 = 0x0,  // Exception: If the data element is NIL then size is 0 bytes
+  BYTE2 = 0x1,
+  BYTE4 = 0x2,
+  BYTE8 = 0x3,
+  BYTE16 = 0x4,
+  // The size types below represent that the first X bits of the value
+  // represents the size of the remaining data.
+  ADDITIONAL_8BIT = 0x5,
+  ADDITIONAL_16BIT = 0x6,
+  ADDITIONAL_32BIT = 0x7,
+};
+
+}  // namespace sdp
+}  // namespace bluetooth
diff --git a/profile/sdp/sdp_logging_helper.h b/profile/sdp/sdp_logging_helper.h
new file mode 100644 (file)
index 0000000..e947329
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2018 The Android Open Source Project
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <type_traits>
+
+#include "bt_trace.h"
+#include "sdp_common.h"
+
+namespace bluetooth {
+namespace sdp {
+
+#ifndef CASE_RETURN_TEXT
+#define CASE_RETURN_TEXT(code) \
+  case code:                   \
+    return #code
+#endif
+
+inline std::string PduIdText(const PduId& id) {
+  switch (id) {
+    CASE_RETURN_TEXT(PduId::RESERVED);
+    CASE_RETURN_TEXT(PduId::ERROR);
+    CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_REQUEST);
+    CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_RESPONSE);
+    CASE_RETURN_TEXT(PduId::SERVICE_ATTRIBUTE_REQUEST);
+    CASE_RETURN_TEXT(PduId::SERVICE_ATTRIBUTE_RESPONSE);
+    CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_ATTRIBUTE_REQUEST);
+    CASE_RETURN_TEXT(PduId::SERVICE_SEARCH_ATTRIBUTE_RESPONSE);
+    default:
+      return "Unknown PduId: " + loghex((uint8_t)id);
+  }
+}
+
+inline std::ostream& operator<<(std::ostream& os, const PduId& id) {
+  return os << PduIdText(id);
+}
+
+inline std::string AttributeIdText(const AttributeId& id) {
+  switch (id) {
+    CASE_RETURN_TEXT(AttributeId::SERVICE_RECORD_HANDLE);
+    CASE_RETURN_TEXT(AttributeId::SERVICE_CLASS_ID_LIST);
+    CASE_RETURN_TEXT(AttributeId::SERVICE_RECORD_STATE);
+    CASE_RETURN_TEXT(AttributeId::SERVICE_ID);
+    CASE_RETURN_TEXT(AttributeId::PROTOCOL_DESCRIPTOR_LIST);
+    CASE_RETURN_TEXT(AttributeId::BROWSE_GROUP_LIST);
+    CASE_RETURN_TEXT(AttributeId::LANGUAGE_BASE_ATTRIBUTE_ID_LIST);
+    CASE_RETURN_TEXT(AttributeId::SERVICE_INFO_TIME_TO_LIVE);
+    CASE_RETURN_TEXT(AttributeId::SERVICE_AVAILABILITY);
+    CASE_RETURN_TEXT(AttributeId::PROFILE_DESCRIPTOR_LIST);
+    CASE_RETURN_TEXT(AttributeId::DOCUMENTATION_URL);
+    CASE_RETURN_TEXT(AttributeId::CLIENT_EXECUTABLE_URL);
+    CASE_RETURN_TEXT(AttributeId::ICON_URL);
+    CASE_RETURN_TEXT(AttributeId::ADDITIONAL_PROTOCOL_DESCRIPTOR_LIST);
+    CASE_RETURN_TEXT(AttributeId::VERSION_NUMBER_LIST);
+    CASE_RETURN_TEXT(AttributeId::SERVICE_DATABASE_STATE);
+    default:
+      return "Unknown AttributeId: " + loghex((uint16_t)id);
+  }
+}
+
+inline std::ostream& operator<<(std::ostream& os, const AttributeId& id) {
+  return os << AttributeIdText(id);
+}
+
+inline std::string DataElementTypeText(const DataElementType& type) {
+  switch (type) {
+    CASE_RETURN_TEXT(DataElementType::NIL);
+    CASE_RETURN_TEXT(DataElementType::UNSIGNED_INT);
+    CASE_RETURN_TEXT(DataElementType::SIGNED_INT);
+    CASE_RETURN_TEXT(DataElementType::UUID);
+    CASE_RETURN_TEXT(DataElementType::STRING);
+    CASE_RETURN_TEXT(DataElementType::BOOLEAN);
+    CASE_RETURN_TEXT(DataElementType::DATA_ELEMENT_SEQUENCE);
+    CASE_RETURN_TEXT(DataElementType::DATA_ELEMENT_ALTERNATIVE);
+    CASE_RETURN_TEXT(DataElementType::URL);
+    default:
+      return "Unknown DataElementType: " + loghex((uint8_t)type);
+  }
+}
+
+inline std::ostream& operator<<(std::ostream& os, const DataElementType& type) {
+  return os << DataElementTypeText(type);
+}
+
+inline std::string DataElementSizeText(const DataElementSize& size) {
+  switch (size) {
+    CASE_RETURN_TEXT(DataElementSize::BYTE1);
+    CASE_RETURN_TEXT(DataElementSize::BYTE2);
+    CASE_RETURN_TEXT(DataElementSize::BYTE4);
+    CASE_RETURN_TEXT(DataElementSize::BYTE8);
+    CASE_RETURN_TEXT(DataElementSize::BYTE16);
+    CASE_RETURN_TEXT(DataElementSize::ADDITIONAL_8BIT);
+    CASE_RETURN_TEXT(DataElementSize::ADDITIONAL_16BIT);
+    CASE_RETURN_TEXT(DataElementSize::ADDITIONAL_32BIT);
+    default:
+      return "Unknown DataElementSize: " + loghex((uint8_t)size);
+  }
+}
+
+inline std::ostream& operator<<(std::ostream& os, const DataElementSize& size) {
+  return os << DataElementSizeText(size);
+}
+
+}  // namespace sdp
+}  // namespace bluetooth