Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c7188511 authored by Ajay Panicker's avatar Ajay Panicker Committed by Gerrit Code Review
Browse files

Merge "Add DataElementReader for SDP"

parents aa8ef0ab 3190c6ec
Loading
Loading
Loading
Loading
+34 −0
Original line number Diff line number Diff line
cc_library_static {
  name: "sdp_service",
  defaults: ["fluoride_defaults"],
  host_supported: true,
  include_dirs: [
    "packages/modules/Bluetooth/system/",
  ],
  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: [
    "packages/modules/Bluetooth/system/",
  ],
  srcs: [
    "common/test/data_element_reader_test.cc",
  ],
  static_libs: [
    "libgmock",
    "sdp_service",
    "lib-bt-packets",
    "libbluetooth-types",
  ],
}
+238 −0
Original line number Diff line number Diff line
/*
 * 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
+65 −0
Original line number Diff line number Diff line
/*
 * 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
+412 −0

File added.

Preview size limit exceeded, changes collapsed.

+95 −0
Original line number Diff line number Diff line
/*
 * 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
Loading