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

Commit 8ada14fc authored by Ajay Panicker's avatar Ajay Panicker Committed by android-build-merger
Browse files

Merge "Add DataElementReader for SDP"

am: c7188511

Change-Id: I625dabfb5d1c0cf18a58550b6ea35b902cf716e0
parents 707b96d1 c7188511
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