| // Copyright 2016 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <algorithm> |
| #include <cctype> |
| #include <iomanip> |
| #include <ios> |
| #include <memory> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/arc/bluetooth/bluetooth_type_converters.h" |
| #include "device/bluetooth/bluetooth_gatt_service.h" |
| #include "device/bluetooth/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; |
| |
| bool IsNonHex(char c) { |
| return !isxdigit(c); |
| } |
| |
| std::string StripNonHex(const std::string& str) { |
| std::string result = str; |
| result.erase(std::remove_if(result.begin(), result.end(), IsNonHex), |
| result.end()); |
| |
| return result; |
| } |
| |
| } // 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(); |
| base::HexStringToBytes(StripNonHex(address), &mojo_addr->address); |
| |
| 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::BluetoothGattStatus |
| TypeConverter<arc::mojom::BluetoothGattStatus, |
| device::BluetoothGattService::GattErrorCode>:: |
| Convert(const device::BluetoothGattService::GattErrorCode& error_code) { |
| arc::mojom::BluetoothGattStatus ret; |
| |
| switch (error_code) { |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_INVALID_LENGTH: |
| ret = arc::mojom::BluetoothGattStatus::GATT_INVALID_ATTRIBUTE_LENGTH; |
| break; |
| |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PERMITTED: |
| ret = arc::mojom::BluetoothGattStatus::GATT_READ_NOT_PERMITTED; |
| break; |
| |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_AUTHORIZED: |
| ret = arc::mojom::BluetoothGattStatus::GATT_INSUFFICIENT_AUTHENTICATION; |
| break; |
| |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_SUPPORTED: |
| ret = arc::mojom::BluetoothGattStatus::GATT_REQUEST_NOT_SUPPORTED; |
| break; |
| |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_UNKNOWN: |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_FAILED: |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_IN_PROGRESS: |
| case device::BluetoothGattService::GattErrorCode::GATT_ERROR_NOT_PAIRED: |
| ret = arc::mojom::BluetoothGattStatus::GATT_FAILURE; |
| break; |
| |
| default: |
| ret = arc::mojom::BluetoothGattStatus::GATT_FAILURE; |
| break; |
| } |
| 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 = 0; |
| |
| switch (result->type) { |
| case bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE: |
| result->value.Append(base::MakeUnique<base::Value>()); |
| break; |
| case bluez::BluetoothServiceAttributeValueBlueZ::UINT: |
| case bluez::BluetoothServiceAttributeValueBlueZ::INT: |
| case bluez::BluetoothServiceAttributeValueBlueZ::UUID: |
| case bluez::BluetoothServiceAttributeValueBlueZ::STRING: |
| case bluez::BluetoothServiceAttributeValueBlueZ::URL: |
| case bluez::BluetoothServiceAttributeValueBlueZ::BOOL: |
| result->type_size = attr_bluez.size(); |
| result->value.Append(attr_bluez.value().CreateDeepCopy()); |
| break; |
| case bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE: |
| if (depth + 1 >= arc::kBluetoothSDPMaxDepth) { |
| result->type = bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE; |
| result->type_size = 0; |
| result->value.Append(base::MakeUnique<base::Value>()); |
| return result; |
| } |
| for (const auto& child : attr_bluez.sequence()) { |
| result->sequence.push_back(Convert(child, depth + 1)); |
| } |
| result->type_size = result->sequence.size(); |
| result->value.Clear(); |
| break; |
| default: |
| NOTREACHED(); |
| } |
| |
| return result; |
| } |
| |
| // static |
| bluez::BluetoothServiceAttributeValueBlueZ |
| TypeConverter<bluez::BluetoothServiceAttributeValueBlueZ, |
| arc::mojom::BluetoothSdpAttributePtr>:: |
| Convert(const arc::mojom::BluetoothSdpAttributePtr& attr, size_t depth) { |
| bluez::BluetoothServiceAttributeValueBlueZ::Type type = attr->type; |
| |
| switch (type) { |
| case bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE: |
| return bluez::BluetoothServiceAttributeValueBlueZ(); |
| case bluez::BluetoothServiceAttributeValueBlueZ::UINT: |
| case bluez::BluetoothServiceAttributeValueBlueZ::INT: |
| case bluez::BluetoothServiceAttributeValueBlueZ::UUID: |
| case bluez::BluetoothServiceAttributeValueBlueZ::STRING: |
| case bluez::BluetoothServiceAttributeValueBlueZ::URL: |
| case bluez::BluetoothServiceAttributeValueBlueZ::BOOL: { |
| if (attr->value.GetSize() != 1) { |
| return bluez::BluetoothServiceAttributeValueBlueZ( |
| bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE, 0, |
| base::MakeUnique<base::Value>()); |
| } |
| |
| std::unique_ptr<base::Value> value; |
| attr->value.Remove(0, &value); |
| |
| return bluez::BluetoothServiceAttributeValueBlueZ( |
| type, static_cast<size_t>(attr->type_size), std::move(value)); |
| } |
| case bluez::BluetoothServiceAttributeValueBlueZ::SEQUENCE: { |
| if (depth + 1 >= arc::kBluetoothSDPMaxDepth || attr->sequence.empty()) { |
| return bluez::BluetoothServiceAttributeValueBlueZ( |
| bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE, 0, |
| base::MakeUnique<base::Value>()); |
| } |
| |
| auto bluez_sequence = base::MakeUnique< |
| bluez::BluetoothServiceAttributeValueBlueZ::Sequence>(); |
| for (const auto& child : attr->sequence) { |
| bluez_sequence->push_back(Convert(child, depth + 1)); |
| } |
| return bluez::BluetoothServiceAttributeValueBlueZ( |
| std::move(bluez_sequence)); |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| return bluez::BluetoothServiceAttributeValueBlueZ( |
| bluez::BluetoothServiceAttributeValueBlueZ::NULLTYPE, 0, |
| base::MakeUnique<base::Value>()); |
| } |
| |
| // 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(); |
| break; |
| } |
| } |
| |
| return record_bluez; |
| } |
| |
| } // namespace mojo |