chromium/ash/components/arc/bluetooth/bluetooth_type_converters.cc

// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <iomanip>
#include <ios>
#include <memory>
#include <sstream>
#include <string>
#include <vector>

#include "ash/components/arc/bluetooth/bluetooth_type_converters.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "device/bluetooth/bluetooth_device.h"
#include "device/bluetooth/bluetooth_gatt_service.h"
#include "device/bluetooth/public/cpp/bluetooth_address.h"
#include "device/bluetooth/public/cpp/bluetooth_uuid.h"

namespace {

constexpr size_t kAddressSize = 6;
constexpr char kInvalidAddress[] = "00:00:00:00:00:00";

// SDP Service attribute IDs.
constexpr uint16_t kServiceClassIDList = 0x0001;
constexpr uint16_t kProtocolDescriptorList = 0x0004;
constexpr uint16_t kBrowseGroupList = 0x0005;
constexpr uint16_t kBluetoothProfileDescriptorList = 0x0009;
constexpr uint16_t kServiceName = 0x0100;

}  // namespace

namespace mojo {

// static
arc::mojom::BluetoothAddressPtr
TypeConverter<arc::mojom::BluetoothAddressPtr, std::string>::Convert(
    const std::string& address) {
  arc::mojom::BluetoothAddressPtr mojo_addr =
      arc::mojom::BluetoothAddress::New();

  mojo_addr->address.resize(kAddressSize);
  if (!device::ParseBluetoothAddress(address, mojo_addr->address))
    mojo_addr->address.clear();

  return mojo_addr;
}

// static
std::string TypeConverter<std::string, arc::mojom::BluetoothAddress>::Convert(
    const arc::mojom::BluetoothAddress& address) {
  std::ostringstream addr_stream;
  addr_stream << std::setfill('0') << std::hex << std::uppercase;

  const std::vector<uint8_t>& bytes = address.address;

  if (address.address.size() != kAddressSize)
    return std::string(kInvalidAddress);

  for (size_t k = 0; k < bytes.size(); k++) {
    addr_stream << std::setw(2) << (unsigned int)bytes[k];
    addr_stream << ((k == bytes.size() - 1) ? "" : ":");
  }

  return addr_stream.str();
}

// static
arc::mojom::BluetoothAddressPtr
TypeConverter<arc::mojom::BluetoothAddressPtr, bdaddr_t>::Convert(
    const bdaddr_t& address) {
  arc::mojom::BluetoothAddressPtr mojo_addr =
      arc::mojom::BluetoothAddress::New();
  mojo_addr->address.resize(kAddressSize);
  std::reverse_copy(std::begin(address.b), std::end(address.b),
                    std::begin(mojo_addr->address));

  return mojo_addr;
}

// static
bdaddr_t TypeConverter<bdaddr_t, arc::mojom::BluetoothAddress>::Convert(
    const arc::mojom::BluetoothAddress& address) {
  bdaddr_t ret;
  std::reverse_copy(std::begin(address.address), std::end(address.address),
                    std::begin(ret.b));

  return ret;
}

// static
arc::mojom::BluetoothSdpAttributePtr
TypeConverter<arc::mojom::BluetoothSdpAttributePtr,
              bluez::BluetoothServiceAttributeValueBlueZ>::
    Convert(const bluez::BluetoothServiceAttributeValueBlueZ& attr_bluez,
            size_t depth) {
  auto result = arc::mojom::BluetoothSdpAttribute::New();
  result->type = attr_bluez.type();
  result->type_size = attr_bluez.size();
  switch (result->type) {
    case bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE:
      result->value = base::Value();
      return result;
    case bluez::BluetoothServiceAttributeValueBlueZ::UINT:
    case bluez::BluetoothServiceAttributeValueBlueZ::INT:
      result->value = base::Value(attr_bluez.value().GetInt());
      return result;
    case bluez::BluetoothServiceAttributeValueBlueZ::URL:
    case bluez::BluetoothServiceAttributeValueBlueZ::UUID:
    case bluez::BluetoothServiceAttributeValueBlueZ::STRING:
      result->value = base::Value(attr_bluez.value().GetString());
      return result;
    case bluez::BluetoothServiceAttributeValueBlueZ::BOOL:
      result->value = base::Value(attr_bluez.value().GetBool());
      return result;
    case bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE:
      if (depth + 1 >= arc::kBluetoothSDPMaxDepth)
        return Convert(bluez::BluetoothServiceAttributeValueBlueZ(), 0);
      for (const auto& child : attr_bluez.sequence())
        result->sequence.push_back(Convert(child, depth + 1));
      result->type_size = result->sequence.size();
      return result;
    default:
      NOTREACHED();
  }
}

// static
bluez::BluetoothServiceAttributeValueBlueZ
TypeConverter<bluez::BluetoothServiceAttributeValueBlueZ,
              arc::mojom::BluetoothSdpAttributePtr>::
    Convert(const arc::mojom::BluetoothSdpAttributePtr& attr, size_t depth) {
  bluez::BluetoothServiceAttributeValueBlueZ::Type type = attr->type;
  if (type != bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE &&
      !attr->value.has_value()) {
    return bluez::BluetoothServiceAttributeValueBlueZ();
  }

  switch (type) {
    case bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE:
      return bluez::BluetoothServiceAttributeValueBlueZ();
    case bluez::BluetoothServiceAttributeValueBlueZ::UINT:
    case bluez::BluetoothServiceAttributeValueBlueZ::INT:
    case bluez::BluetoothServiceAttributeValueBlueZ::URL:
    case bluez::BluetoothServiceAttributeValueBlueZ::UUID:
    case bluez::BluetoothServiceAttributeValueBlueZ::STRING:
    case bluez::BluetoothServiceAttributeValueBlueZ::BOOL:
      return bluez::BluetoothServiceAttributeValueBlueZ(type, attr->type_size,
                                                        attr->value->Clone());
    case bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE: {
      if (depth + 1 >= arc::kBluetoothSDPMaxDepth || attr->sequence.empty())
        return bluez::BluetoothServiceAttributeValueBlueZ();
      auto sequence = std::make_unique<
          bluez::BluetoothServiceAttributeValueBlueZ::Sequence>();
      for (const auto& child : attr->sequence)
        sequence->emplace_back(Convert(child, depth + 1));
      return bluez::BluetoothServiceAttributeValueBlueZ(std::move(sequence));
    }
    default:
      NOTREACHED();
  }
}

// static
arc::mojom::BluetoothSdpRecordPtr
TypeConverter<arc::mojom::BluetoothSdpRecordPtr,
              bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& record_bluez) {
  arc::mojom::BluetoothSdpRecordPtr result =
      arc::mojom::BluetoothSdpRecord::New();

  for (auto id : record_bluez.GetAttributeIds()) {
    switch (id) {
      case kServiceClassIDList:
      case kProtocolDescriptorList:
      case kBrowseGroupList:
      case kBluetoothProfileDescriptorList:
      case kServiceName:
        result->attrs[id] = arc::mojom::BluetoothSdpAttribute::From(
            record_bluez.GetAttributeValue(id));
        break;
      default:
        // Android does not support this.
        break;
    }
  }

  return result;
}

// static
bluez::BluetoothServiceRecordBlueZ
TypeConverter<bluez::BluetoothServiceRecordBlueZ,
              arc::mojom::BluetoothSdpRecordPtr>::
    Convert(const arc::mojom::BluetoothSdpRecordPtr& record) {
  bluez::BluetoothServiceRecordBlueZ record_bluez;

  for (const auto& pair : record->attrs) {
    switch (pair.first) {
      case kServiceClassIDList:
      case kProtocolDescriptorList:
      case kBrowseGroupList:
      case kBluetoothProfileDescriptorList:
      case kServiceName:
        record_bluez.AddRecordEntry(
            pair.first,
            pair.second.To<bluez::BluetoothServiceAttributeValueBlueZ>());
        break;
      default:
        NOTREACHED();
    }
  }

  return record_bluez;
}

// Floss BtSdpRecord conversions adapted from
// aosp/packages/modules/Bluetooth/system/bta/sdp/bta_sdp_act.cc

// static
floss::BtSdpHeaderOverlay
TypeConverter<floss::BtSdpHeaderOverlay, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpHeaderOverlay record_overlay{};
  // Set some default values that will be updated if bluez_record actually
  // contains relevant data.
  // The caller may change this type but for now assume this is generic record.
  record_overlay.sdp_type = floss::BtSdpType::kRaw;
  record_overlay.service_name_length = 0;
  record_overlay.service_name = "";
  record_overlay.rfcomm_channel_number = 0;
  record_overlay.l2cap_psm = -1;
  record_overlay.profile_version = 0;

  if (bluez_record.IsAttributePresented(ATTR_ID_SERVICE_NAME)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_SERVICE_NAME);
    const std::string* service_name = attribute.value().GetIfString();
    if (service_name) {
      record_overlay.service_name = *service_name;
      record_overlay.service_name_length = attribute.size();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_SERVICE_CLASS_ID_LIST)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_SERVICE_CLASS_ID_LIST);
    for (const auto& serv_id : attribute.sequence()) {
      const std::string* uuid = serv_id.value().GetIfString();
      if (uuid) {
        record_overlay.uuid = device::BluetoothUUID(*uuid);
        break;
      }
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_PROTOCOL_DESC_LIST)) {
    const bluez::BluetoothServiceAttributeValueBlueZ maybe_protocol_list =
        bluez_record.GetAttributeValue(ATTR_ID_PROTOCOL_DESC_LIST);
    if (maybe_protocol_list.type() ==
        bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE) {
      auto protocol_list = maybe_protocol_list.sequence();
      for (auto protocol_record : protocol_list) {
        if (protocol_record.type() !=
            bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE) {
          continue;
        }
        auto protocol_record_sequence = protocol_record.sequence();
        // We expect at least two values: the UUID itself and the channel number
        if (protocol_record_sequence.size() < 2) {
          continue;
        }
        const std::string* uuid =
            protocol_record_sequence[0].value().GetIfString();
        if (!uuid) {
          continue;
        }
        std::vector<uint8_t> uuid_as_bytes =
            device::BluetoothUUID(*uuid).GetBytes();
        if (uuid_as_bytes.empty()) {
          break;
        }
        if ((static_cast<uint16_t>(uuid_as_bytes[2] << 8) |
             static_cast<uint16_t>(uuid_as_bytes[3])) != UUID_PROTOCOL_RFCOMM) {
          continue;
        }
        std::optional<int> channel_number =
            protocol_record_sequence[1].value().GetIfInt();
        if (!channel_number) {
          continue;
        }
        record_overlay.rfcomm_channel_number = *channel_number;
      }
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_GOEP_L2CAP_PSM)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_GOEP_L2CAP_PSM);
    std::optional<int> l2cap_psm = attribute.value().GetIfInt();
    if (l2cap_psm) {
      record_overlay.l2cap_psm = l2cap_psm.value();
    }
  }

  return record_overlay;
}

namespace {

// Following Core Specification V 5.3 | Vol 3, Part B
// Section 5.1.11 BluetoothProfileDescriptorList attribute
std::optional<int> GetProfileVersionFromBlueZRecord(
    const bluez::BluetoothServiceRecordBlueZ& bluez_record,
    const uint16_t profile_uuid) {
  if (!bluez_record.IsAttributePresented(ATTR_ID_BT_PROFILE_DESC_LIST)) {
    return std::nullopt;
  }
  if (!bluez_record.GetAttributeValue(ATTR_ID_BT_PROFILE_DESC_LIST)
           .is_sequence()) {
    return std::nullopt;
  }
  const auto profile_list =
      bluez_record.GetAttributeValue(ATTR_ID_BT_PROFILE_DESC_LIST).sequence();
  for (const auto& profile : profile_list) {
    if (!profile.is_sequence()) {
      continue;
    }
    const auto profile_descriptor = profile.sequence();
    if (profile_descriptor.size() < 2) {
      continue;
    }
    if (profile_descriptor[0].type() !=
        bluez::BluetoothServiceAttributeValueBlueZ::UUID) {
      continue;
    }
    if (profile_descriptor[0].value().GetInt() != profile_uuid) {
      continue;
    }
    if (profile_descriptor[1].type() !=
        bluez::BluetoothServiceAttributeValueBlueZ::UINT) {
      continue;
    }
    return profile_descriptor[1].value().GetIfInt();
  }
  return std::nullopt;
}

bluez::BluetoothServiceAttributeValueBlueZ MakeDescListForBlueZRecord(
    const uint16_t profile_or_protocol_uuid,
    const int version) {
  std::string maybe_short_uuid = base::NumberToString(profile_or_protocol_uuid);
  // L2CAP and RFCOMM ports/channels will not exceed 0x7FFF, but must be 4 hex
  // digits long for BluetoothUUID class to accept them.
  maybe_short_uuid.insert(maybe_short_uuid.begin(),
                          4 - maybe_short_uuid.length(), '0');
  const std::string full_uuid =
      device::BluetoothUUID(maybe_short_uuid).canonical_value();
  auto desc_list =
      std::make_unique<bluez::BluetoothServiceAttributeValueBlueZ::Sequence>();
  auto sequence =
      std::make_unique<bluez::BluetoothServiceAttributeValueBlueZ::Sequence>();
  sequence->emplace_back(bluez::BluetoothServiceAttributeValueBlueZ(
      bluez::BluetoothServiceAttributeValueBlueZ::UUID, full_uuid.size(),
      std::optional<base::Value>(full_uuid)));
  sequence->emplace_back(bluez::BluetoothServiceAttributeValueBlueZ(
      bluez::BluetoothServiceAttributeValueBlueZ::UINT, sizeof(version),
      std::optional<base::Value>(version)));
  desc_list->emplace_back(
      bluez::BluetoothServiceAttributeValueBlueZ(std::move(sequence)));
  return bluez::BluetoothServiceAttributeValueBlueZ(std::move(desc_list));
}

}  // namespace

// static
floss::BtSdpMasRecord
TypeConverter<floss::BtSdpMasRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpMasRecord mas_record{};
  mas_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  mas_record.hdr.sdp_type = floss::BtSdpType::kMapMas;
  mas_record.mas_instance_id = 0;
  mas_record.supported_features = 0x0000001F;
  mas_record.supported_message_types = 0;

  if (bluez_record.IsAttributePresented(ATTR_ID_MAS_INSTANCE_ID)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_MAS_INSTANCE_ID);
    std::optional<int> mas_instance_id = attribute.value().GetIfInt();
    if (mas_instance_id) {
      mas_record.mas_instance_id = mas_instance_id.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_SUPPORTED_MSG_TYPE)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_SUPPORTED_MSG_TYPE);
    std::optional<int> supported_message_types = attribute.value().GetIfInt();
    if (supported_message_types) {
      mas_record.supported_message_types = supported_message_types.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_MAP_SUPPORTED_FEATURES)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_MAP_SUPPORTED_FEATURES);
    std::optional<int> supported_features = attribute.value().GetIfInt();
    if (supported_features) {
      mas_record.supported_features = supported_features.value();
    }
  }

  const std::optional<int> profile_version = GetProfileVersionFromBlueZRecord(
      bluez_record, UUID_SERVCLASS_MAP_PROFILE);
  if (profile_version.has_value()) {
    mas_record.hdr.profile_version = *profile_version;
  }

  return mas_record;
}

// static
floss::BtSdpMnsRecord
TypeConverter<floss::BtSdpMnsRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpMnsRecord mns_record{};
  mns_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  mns_record.hdr.sdp_type = floss::BtSdpType::kMapMns;
  mns_record.supported_features = 0x0000001F;

  if (bluez_record.IsAttributePresented(ATTR_ID_MAP_SUPPORTED_FEATURES)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_MAP_SUPPORTED_FEATURES);
    std::optional<int> supported_features = attribute.value().GetIfInt();
    if (supported_features) {
      mns_record.supported_features = supported_features.value();
    }
  }

  const std::optional<int> profile_version = GetProfileVersionFromBlueZRecord(
      bluez_record, UUID_SERVCLASS_MAP_PROFILE);
  if (profile_version.has_value()) {
    mns_record.hdr.profile_version = *profile_version;
  }

  return mns_record;
}

// static
floss::BtSdpPseRecord
TypeConverter<floss::BtSdpPseRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpPseRecord pse_record{};
  pse_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  ;
  pse_record.hdr.sdp_type = floss::BtSdpType::kPbapPse;
  pse_record.supported_features = 0x00000003;
  pse_record.supported_repositories = 0;

  if (bluez_record.IsAttributePresented(ATTR_ID_SUPPORTED_REPOSITORIES)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_SUPPORTED_REPOSITORIES);
    std::optional<int> supported_repositories = attribute.value().GetIfInt();
    if (supported_repositories) {
      pse_record.supported_repositories = supported_repositories.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_SUPPORTED_FEATURES)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_SUPPORTED_FEATURES);
    std::optional<int> supported_features = attribute.value().GetIfInt();
    if (supported_features) {
      pse_record.supported_features = supported_features.value();
    }
  }

  const std::optional<int> profile_version = GetProfileVersionFromBlueZRecord(
      bluez_record, UUID_SERVCLASS_PHONE_ACCESS);
  if (profile_version.has_value()) {
    pse_record.hdr.profile_version = *profile_version;
  }

  return pse_record;
}

// static
floss::BtSdpPceRecord
TypeConverter<floss::BtSdpPceRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpPceRecord pce_record{};
  pce_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  pce_record.hdr.sdp_type = floss::BtSdpType::kPbapPce;

  const std::optional<int> profile_version = GetProfileVersionFromBlueZRecord(
      bluez_record, UUID_SERVCLASS_PHONE_ACCESS);
  if (profile_version.has_value()) {
    pce_record.hdr.profile_version = *profile_version;
  }

  return pce_record;
}

// static
floss::BtSdpOpsRecord
TypeConverter<floss::BtSdpOpsRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpOpsRecord ops_record{};
  ops_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  ops_record.hdr.sdp_type = floss::BtSdpType::kOppServer;

  const std::optional<int> profile_version = GetProfileVersionFromBlueZRecord(
      bluez_record, UUID_SERVCLASS_OBEX_OBJECT_PUSH);
  if (profile_version.has_value()) {
    ops_record.hdr.profile_version = *profile_version;
  }

  // TODO(b/277105543): Determine the correct structure for
  // supported_formats_list and implement conversion.

  return ops_record;
}

// static
floss::BtSdpSapRecord
TypeConverter<floss::BtSdpSapRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpSapRecord sap_record{};
  sap_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  sap_record.hdr.sdp_type = floss::BtSdpType::kSapServer;

  const std::optional<int> profile_version =
      GetProfileVersionFromBlueZRecord(bluez_record, UUID_SERVCLASS_SAP);
  if (profile_version.has_value()) {
    sap_record.hdr.profile_version = *profile_version;
  }

  return sap_record;
}

// static
floss::BtSdpDipRecord
TypeConverter<floss::BtSdpDipRecord, bluez::BluetoothServiceRecordBlueZ>::
    Convert(const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  floss::BtSdpDipRecord dip_record{};
  dip_record.hdr =
      TypeConverter<floss::BtSdpHeaderOverlay,
                    bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  dip_record.hdr.sdp_type = floss::BtSdpType::kDip;
  dip_record.spec_id = 0;
  dip_record.vendor = 0;
  dip_record.vendor_id_source = 0;
  dip_record.product = 0;
  dip_record.version = 0;
  dip_record.primary_record = false;

  if (bluez_record.IsAttributePresented(ATTR_ID_SPECIFICATION_ID)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_SPECIFICATION_ID);
    std::optional<int> spec_id = attribute.value().GetIfInt();
    if (spec_id) {
      dip_record.spec_id = spec_id.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_VENDOR_ID)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_VENDOR_ID);
    std::optional<int> vendor = attribute.value().GetIfInt();
    if (vendor) {
      dip_record.vendor = vendor.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_VENDOR_ID_SOURCE)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_VENDOR_ID_SOURCE);
    std::optional<int> vendor_id_source = attribute.value().GetIfInt();
    if (vendor_id_source) {
      dip_record.vendor_id_source = vendor_id_source.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_PRODUCT_ID)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_PRODUCT_ID);
    std::optional<int> product = attribute.value().GetIfInt();
    if (product) {
      dip_record.product = product.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_PRODUCT_VERSION)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_PRODUCT_VERSION);
    std::optional<int> version = attribute.value().GetIfInt();
    if (version) {
      dip_record.version = version.value();
    }
  }

  if (bluez_record.IsAttributePresented(ATTR_ID_PRIMARY_RECORD)) {
    const bluez::BluetoothServiceAttributeValueBlueZ attribute =
        bluez_record.GetAttributeValue(ATTR_ID_PRIMARY_RECORD);
    std::optional<bool> primary_record = attribute.value().GetIfBool();
    if (primary_record) {
      dip_record.primary_record = primary_record.value();
    }
  }

  return dip_record;
}

// static
floss::BtSdpRecord
TypeConverter<floss::BtSdpRecord, bluez::BluetoothServiceRecordBlueZ>::Convert(
    const bluez::BluetoothServiceRecordBlueZ& bluez_record) {
  if (!bluez_record.IsAttributePresented(ATTR_ID_SERVICE_ID)) {
    return TypeConverter<
        floss::BtSdpHeaderOverlay,
        bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
  }
  std::optional<int> service_id =
      bluez_record.GetAttributeValue(ATTR_ID_SERVICE_ID).value().GetIfInt();
  if (!service_id.has_value()) {
    return floss::BtSdpRecord();
  }
  switch (*service_id) {
    case UUID_MAP_MAS:
      return TypeConverter<
          floss::BtSdpMasRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    case UUID_MAP_MNS:
      return TypeConverter<
          floss::BtSdpMnsRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    case UUID_PBAP_PSE:
      return TypeConverter<
          floss::BtSdpPseRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    case UUID_PBAP_PCE:
      return TypeConverter<
          floss::BtSdpPceRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    case UUID_SPP:
      return TypeConverter<
          floss::BtSdpOpsRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    case UUID_SAP:
      return TypeConverter<
          floss::BtSdpSapRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    case UUID_DIP:
      return TypeConverter<
          floss::BtSdpDipRecord,
          bluez::BluetoothServiceRecordBlueZ>::Convert(bluez_record);
    default:
      return floss::BtSdpRecord();
  }
}

// static
bluez::BluetoothServiceRecordBlueZ
TypeConverter<bluez::BluetoothServiceRecordBlueZ, floss::BtSdpHeaderOverlay>::
    Convert(const floss::BtSdpHeaderOverlay& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record;

  bluez_record.AddRecordEntry(
      ATTR_ID_SERVICE_NAME,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::STRING,
          record.service_name_length,
          std::optional<base::Value>(record.service_name)));

  auto seq =
      std::make_unique<bluez::BluetoothServiceAttributeValueBlueZ::Sequence>();
  seq->emplace_back(bluez::BluetoothServiceAttributeValueBlueZ(
      bluez::BluetoothServiceAttributeValueBlueZ::UUID,
      record.uuid.canonical_value().length(),
      std::optional<base::Value>(record.uuid.canonical_value())));
  bluez_record.AddRecordEntry(
      ATTR_ID_SERVICE_CLASS_ID_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(std::move(seq)));

  if (record.rfcomm_channel_number > 0) {
    bluez_record.AddRecordEntry(
        ATTR_ID_PROTOCOL_DESC_LIST,
        bluez::BluetoothServiceAttributeValueBlueZ(
            std::move(MakeDescListForBlueZRecord(
                UUID_PROTOCOL_RFCOMM, record.rfcomm_channel_number))));
  }

  if (record.l2cap_psm > -1) {
    bluez_record.AddRecordEntry(
        ATTR_ID_GOEP_L2CAP_PSM,
        bluez::BluetoothServiceAttributeValueBlueZ(
            bluez::BluetoothServiceAttributeValueBlueZ::UINT,
            sizeof(record.l2cap_psm),
            std::optional<base::Value>(record.l2cap_psm)));

    bluez_record.AddRecordEntry(
        ATTR_ID_PROTOCOL_DESC_LIST,
        bluez::BluetoothServiceAttributeValueBlueZ(
            std::move(MakeDescListForBlueZRecord(
                UUID_PROTOCOL_L2CAP, record.rfcomm_channel_number))));
  }

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpMasRecord>::Convert(const floss::BtSdpMasRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);

  bluez_record.AddRecordEntry(
      ATTR_ID_BT_PROFILE_DESC_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(
          std::move(MakeDescListForBlueZRecord(UUID_SERVCLASS_MAP_PROFILE,
                                               record.hdr.profile_version))));

  bluez_record.AddRecordEntry(
      ATTR_ID_MAS_INSTANCE_ID,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.mas_instance_id),
          std::optional<base::Value>(
              static_cast<int>(record.mas_instance_id))));

  bluez_record.AddRecordEntry(
      ATTR_ID_SUPPORTED_MSG_TYPE,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.supported_message_types),
          std::optional<base::Value>(
              static_cast<int>(record.supported_message_types))));

  bluez_record.AddRecordEntry(
      ATTR_ID_SUPPORTED_FEATURES,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.supported_features),
          std::optional<base::Value>(
              static_cast<int>(record.supported_features))));

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpMnsRecord>::Convert(const floss::BtSdpMnsRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);

  bluez_record.AddRecordEntry(
      ATTR_ID_BT_PROFILE_DESC_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(
          std::move(MakeDescListForBlueZRecord(UUID_SERVCLASS_MAP_PROFILE,
                                               record.hdr.profile_version))));

  bluez_record.AddRecordEntry(
      ATTR_ID_SUPPORTED_FEATURES,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.supported_features),
          std::optional<base::Value>(
              static_cast<int>(record.supported_features))));

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpPseRecord>::Convert(const floss::BtSdpPseRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);

  bluez_record.AddRecordEntry(
      ATTR_ID_BT_PROFILE_DESC_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(
          std::move(MakeDescListForBlueZRecord(UUID_SERVCLASS_PHONE_ACCESS,
                                               record.hdr.profile_version))));

  bluez_record.AddRecordEntry(
      ATTR_ID_SUPPORTED_REPOSITORIES,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.supported_repositories),
          std::optional<base::Value>(
              static_cast<int>(record.supported_repositories))));

  bluez_record.AddRecordEntry(
      ATTR_ID_SUPPORTED_FEATURES,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.supported_features),
          std::optional<base::Value>(
              static_cast<int>(record.supported_features))));

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpPceRecord>::Convert(const floss::BtSdpPceRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);

  bluez_record.AddRecordEntry(
      ATTR_ID_BT_PROFILE_DESC_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(
          std::move(MakeDescListForBlueZRecord(UUID_SERVCLASS_PHONE_ACCESS,
                                               record.hdr.profile_version))));

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpOpsRecord>::Convert(const floss::BtSdpOpsRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);

  bluez_record.AddRecordEntry(
      ATTR_ID_BT_PROFILE_DESC_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(
          std::move(MakeDescListForBlueZRecord(UUID_SERVCLASS_OBEX_OBJECT_PUSH,
                                               record.hdr.profile_version))));

  // TODO: supported_formats

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpSapRecord>::Convert(const floss::BtSdpSapRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);

  bluez_record.AddRecordEntry(
      ATTR_ID_BT_PROFILE_DESC_LIST,
      bluez::BluetoothServiceAttributeValueBlueZ(
          std::move(MakeDescListForBlueZRecord(UUID_SERVCLASS_SAP,
                                               record.hdr.profile_version))));

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ TypeConverter<
    bluez::BluetoothServiceRecordBlueZ,
    floss::BtSdpDipRecord>::Convert(const floss::BtSdpDipRecord& record) {
  bluez::BluetoothServiceRecordBlueZ bluez_record =
      TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                    floss::BtSdpHeaderOverlay>::Convert(record.hdr);
  // The following static_cast<int>() calls are being invoked on uint16_t fields
  // which can safely convert to int.
  bluez_record.AddRecordEntry(
      ATTR_ID_SPECIFICATION_ID,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.spec_id),
          std::optional<base::Value>(static_cast<int>(record.spec_id))));

  bluez_record.AddRecordEntry(
      ATTR_ID_VENDOR_ID,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.vendor),
          std::optional<base::Value>(static_cast<int>(record.vendor))));

  bluez_record.AddRecordEntry(
      ATTR_ID_VENDOR_ID_SOURCE,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.vendor_id_source),
          std::optional<base::Value>(
              static_cast<int>(record.vendor_id_source))));

  bluez_record.AddRecordEntry(
      ATTR_ID_PRODUCT_ID,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.product),
          std::optional<base::Value>(static_cast<int>(record.product))));

  bluez_record.AddRecordEntry(
      ATTR_ID_PRODUCT_VERSION,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::UINT,
          sizeof(record.version),
          std::optional<base::Value>(static_cast<int>(record.version))));

  bluez_record.AddRecordEntry(
      ATTR_ID_PRIMARY_RECORD,
      bluez::BluetoothServiceAttributeValueBlueZ(
          bluez::BluetoothServiceAttributeValueBlueZ::BOOL,
          sizeof(record.primary_record),
          std::optional<base::Value>(record.primary_record)));

  return bluez_record;
}

// static
bluez::BluetoothServiceRecordBlueZ
TypeConverter<bluez::BluetoothServiceRecordBlueZ, floss::BtSdpRecord>::Convert(
    const floss::BtSdpRecord& record) {
  if (absl::holds_alternative<floss::BtSdpHeaderOverlay>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpHeaderOverlay>::
        Convert(absl::get<floss::BtSdpHeaderOverlay>(record));
  } else if (absl::holds_alternative<floss::BtSdpMasRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpMasRecord>::
        Convert(absl::get<floss::BtSdpMasRecord>(record));
  } else if (absl::holds_alternative<floss::BtSdpMnsRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpMnsRecord>::
        Convert(absl::get<floss::BtSdpMnsRecord>(record));
  } else if (absl::holds_alternative<floss::BtSdpPseRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpPseRecord>::
        Convert(absl::get<floss::BtSdpPseRecord>(record));
  } else if (absl::holds_alternative<floss::BtSdpPceRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpPceRecord>::
        Convert(absl::get<floss::BtSdpPceRecord>(record));
  } else if (absl::holds_alternative<floss::BtSdpOpsRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpOpsRecord>::
        Convert(absl::get<floss::BtSdpOpsRecord>(record));
  } else if (absl::holds_alternative<floss::BtSdpSapRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpSapRecord>::
        Convert(absl::get<floss::BtSdpSapRecord>(record));
  } else if (absl::holds_alternative<floss::BtSdpDipRecord>(record)) {
    return TypeConverter<bluez::BluetoothServiceRecordBlueZ,
                         floss::BtSdpDipRecord>::
        Convert(absl::get<floss::BtSdpDipRecord>(record));
  } else {
    return bluez::BluetoothServiceRecordBlueZ();
  }
}

}  // namespace mojo