| // Copyright 2017 The Fuchsia 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 "helpers.h" |
| |
| #include <unordered_set> |
| |
| #include <endian.h> |
| |
| #include "garnet/drivers/bluetooth/lib/common/uuid.h" |
| #include "garnet/drivers/bluetooth/lib/gap/advertising_data.h" |
| #include "garnet/drivers/bluetooth/lib/gap/discovery_filter.h" |
| |
| using btlib::sm::SecurityLevel; |
| using fuchsia::bluetooth::Bool; |
| using fuchsia::bluetooth::Error; |
| using fuchsia::bluetooth::ErrorCode; |
| using fuchsia::bluetooth::Int8; |
| using fuchsia::bluetooth::Status; |
| |
| namespace ctrl = fuchsia::bluetooth::control; |
| namespace ble = fuchsia::bluetooth::le; |
| |
| namespace bthost { |
| namespace fidl_helpers { |
| namespace { |
| |
| ctrl::TechnologyType TechnologyTypeToFidl(::btlib::gap::TechnologyType type) { |
| switch (type) { |
| case ::btlib::gap::TechnologyType::kLowEnergy: |
| return ctrl::TechnologyType::LOW_ENERGY; |
| case ::btlib::gap::TechnologyType::kClassic: |
| return ctrl::TechnologyType::CLASSIC; |
| case ::btlib::gap::TechnologyType::kDualMode: |
| return ctrl::TechnologyType::DUAL_MODE; |
| default: |
| FXL_NOTREACHED(); |
| break; |
| } |
| |
| // This should never execute. |
| return ctrl::TechnologyType::DUAL_MODE; |
| } |
| |
| } // namespace |
| |
| ErrorCode HostErrorToFidl(::btlib::common::HostError host_error) { |
| switch (host_error) { |
| case ::btlib::common::HostError::kFailed: |
| return ErrorCode::FAILED; |
| case ::btlib::common::HostError::kTimedOut: |
| return ErrorCode::TIMED_OUT; |
| case ::btlib::common::HostError::kInvalidParameters: |
| return ErrorCode::INVALID_ARGUMENTS; |
| case ::btlib::common::HostError::kCanceled: |
| return ErrorCode::CANCELED; |
| case ::btlib::common::HostError::kInProgress: |
| return ErrorCode::IN_PROGRESS; |
| case ::btlib::common::HostError::kNotSupported: |
| return ErrorCode::NOT_SUPPORTED; |
| case ::btlib::common::HostError::kNotFound: |
| return ErrorCode::NOT_FOUND; |
| case ::btlib::common::HostError::kProtocolError: |
| return ErrorCode::PROTOCOL_ERROR; |
| default: |
| break; |
| } |
| |
| return ErrorCode::FAILED; |
| } |
| |
| Status NewFidlError(ErrorCode error_code, std::string description) { |
| Status status; |
| status.error = Error::New(); |
| status.error->error_code = error_code; |
| status.error->description = description; |
| return status; |
| } |
| |
| btlib::common::DeviceAddress::Type NewAddrType( |
| const fuchsia::bluetooth::control::AddressType& type) { |
| switch (type) { |
| case ctrl::AddressType::LE_RANDOM: |
| return btlib::common::DeviceAddress::Type::kLERandom; |
| case ctrl::AddressType::LE_PUBLIC: |
| return btlib::common::DeviceAddress::Type::kLEPublic; |
| case ctrl::AddressType::BREDR: |
| return btlib::common::DeviceAddress::Type::kBREDR; |
| default: |
| FXL_NOTREACHED(); |
| break; |
| } |
| return btlib::common::DeviceAddress::Type::kBREDR; |
| } |
| |
| btlib::sm::SecurityProperties NewSecurityLevel( |
| const fuchsia::bluetooth::control::SecurityProperties& sec_prop) { |
| auto level = btlib::sm::SecurityLevel::kEncrypted; |
| if (sec_prop.authenticated) { |
| level = btlib::sm::SecurityLevel::kAuthenticated; |
| } |
| |
| return btlib::sm::SecurityProperties(level, sec_prop.encryption_key_size, |
| sec_prop.secure_connections); |
| } |
| |
| btlib::sm::IOCapability NewIoCapability(ctrl::InputCapabilityType input, |
| ctrl::OutputCapabilityType output) { |
| if (input == ctrl::InputCapabilityType::NONE && |
| output == ctrl::OutputCapabilityType::NONE) { |
| return btlib::sm::IOCapability::kNoInputNoOutput; |
| } else if (input == ctrl::InputCapabilityType::KEYBOARD && |
| output == ctrl::OutputCapabilityType::DISPLAY) { |
| return btlib::sm::IOCapability::kKeyboardDisplay; |
| } else if (input == ctrl::InputCapabilityType::KEYBOARD && |
| output == ctrl::OutputCapabilityType::NONE) { |
| return btlib::sm::IOCapability::kKeyboardOnly; |
| } else if (input == ctrl::InputCapabilityType::NONE && |
| output == ctrl::OutputCapabilityType::DISPLAY) { |
| return btlib::sm::IOCapability::kDisplayOnly; |
| } else if (input == ctrl::InputCapabilityType::CONFIRMATION && |
| output == ctrl::OutputCapabilityType::DISPLAY) { |
| return btlib::sm::IOCapability::kDisplayYesNo; |
| } |
| return btlib::sm::IOCapability::kNoInputNoOutput; |
| } |
| |
| ctrl::AdapterInfo NewAdapterInfo(const ::btlib::gap::Adapter& adapter) { |
| ctrl::AdapterInfo adapter_info; |
| adapter_info.state = ctrl::AdapterState::New(); |
| |
| adapter_info.state->local_name = adapter.state().local_name(); |
| |
| adapter_info.state->discoverable = Bool::New(); |
| adapter_info.state->discoverable->value = false; |
| adapter_info.state->discovering = Bool::New(); |
| adapter_info.state->discovering->value = adapter.IsDiscovering(); |
| |
| adapter_info.identifier = adapter.identifier(); |
| adapter_info.address = adapter.state().controller_address().ToString(); |
| |
| adapter_info.technology = TechnologyTypeToFidl(adapter.state().type()); |
| |
| return adapter_info; |
| } |
| |
| ctrl::RemoteDevicePtr NewRemoteDevice( |
| const ::btlib::gap::RemoteDevice& device) { |
| auto fidl_device = ctrl::RemoteDevice::New(); |
| fidl_device->identifier = device.identifier(); |
| fidl_device->address = device.address().value().ToString(); |
| fidl_device->technology = TechnologyTypeToFidl(device.technology()); |
| |
| // TODO(armansito): Report correct values once we support these. |
| fidl_device->connected = false; |
| fidl_device->bonded = false; |
| |
| // Set default value for device appearance. |
| fidl_device->appearance = ctrl::Appearance::UNKNOWN; |
| |
| if (device.rssi() != ::btlib::hci::kRSSIInvalid) { |
| fidl_device->rssi = Int8::New(); |
| fidl_device->rssi->value = device.rssi(); |
| } |
| |
| ::btlib::gap::AdvertisingData adv_data; |
| if (!::btlib::gap::AdvertisingData::FromBytes(device.advertising_data(), |
| &adv_data)) { |
| fidl_device->service_uuids.resize(0); |
| return fidl_device; |
| } |
| |
| const auto& uuids = adv_data.service_uuids(); |
| |
| // |service_uuids| is not a nullable field, so we need to assign something to |
| // it. |
| if (uuids.empty()) { |
| fidl_device->service_uuids.resize(0); |
| } else { |
| for (const auto& uuid : uuids) { |
| fidl_device->service_uuids.push_back(uuid.ToString()); |
| } |
| } |
| |
| if (adv_data.local_name()) |
| fidl_device->name = *adv_data.local_name(); |
| if (adv_data.appearance()) { |
| fidl_device->appearance = |
| static_cast<ctrl::Appearance>(le16toh(*adv_data.appearance())); |
| } |
| if (adv_data.tx_power()) { |
| auto fidl_tx_power = Int8::New(); |
| fidl_tx_power->value = *adv_data.tx_power(); |
| fidl_device->tx_power = std::move(fidl_tx_power); |
| } |
| |
| return fidl_device; |
| } |
| |
| ble::RemoteDevicePtr NewLERemoteDevice( |
| const ::btlib::gap::RemoteDevice& device) { |
| ::btlib::gap::AdvertisingData ad; |
| auto fidl_device = ble::RemoteDevice::New(); |
| fidl_device->identifier = device.identifier(); |
| fidl_device->connectable = device.connectable(); |
| |
| // Initialize advertising data only if its non-empty. |
| if (device.advertising_data().size() != 0u) { |
| ::btlib::gap::AdvertisingData ad; |
| if (!::btlib::gap::AdvertisingData::FromBytes(device.advertising_data(), |
| &ad)) |
| return nullptr; |
| |
| fidl_device->advertising_data = ad.AsLEAdvertisingData(); |
| } |
| |
| if (device.rssi() != ::btlib::hci::kRSSIInvalid) { |
| fidl_device->rssi = Int8::New(); |
| fidl_device->rssi->value = device.rssi(); |
| } |
| |
| return fidl_device; |
| } |
| |
| bool IsScanFilterValid(const ble::ScanFilter& fidl_filter) { |
| // |service_uuids| is the only field that can potentially contain invalid |
| // data, since they are represented as strings. |
| if (!fidl_filter.service_uuids) |
| return true; |
| |
| for (const auto& uuid_str : *fidl_filter.service_uuids) { |
| if (!::btlib::common::IsStringValidUuid(uuid_str)) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool PopulateDiscoveryFilter(const ble::ScanFilter& fidl_filter, |
| ::btlib::gap::DiscoveryFilter* out_filter) { |
| FXL_DCHECK(out_filter); |
| |
| if (fidl_filter.service_uuids) { |
| std::vector<::btlib::common::UUID> uuids; |
| for (const auto& uuid_str : *fidl_filter.service_uuids) { |
| ::btlib::common::UUID uuid; |
| if (!::btlib::common::StringToUuid(uuid_str, &uuid)) { |
| FXL_VLOG(1) << "Invalid parameters given to scan filter"; |
| return false; |
| } |
| uuids.push_back(uuid); |
| } |
| |
| if (!uuids.empty()) |
| out_filter->set_service_uuids(uuids); |
| } |
| |
| if (fidl_filter.connectable) { |
| out_filter->set_connectable(fidl_filter.connectable->value); |
| } |
| |
| if (fidl_filter.manufacturer_identifier) { |
| out_filter->set_manufacturer_code( |
| fidl_filter.manufacturer_identifier->value); |
| } |
| |
| if (fidl_filter.name_substring && !fidl_filter.name_substring.get().empty()) { |
| out_filter->set_name_substring(fidl_filter.name_substring.get()); |
| } |
| |
| if (fidl_filter.max_path_loss) { |
| out_filter->set_pathloss(fidl_filter.max_path_loss->value); |
| } |
| |
| return true; |
| } |
| |
| } // namespace fidl_helpers |
| } // namespace bthost |
| |
| // static |
| fidl::VectorPtr<uint8_t> |
| fxl::TypeConverter<fidl::VectorPtr<uint8_t>, ::btlib::common::ByteBuffer>:: |
| Convert(const ::btlib::common::ByteBuffer& from) { |
| auto to = fidl::VectorPtr<uint8_t>::New(from.size()); |
| ::btlib::common::MutableBufferView view(to->data(), to->size()); |
| view.Write(from); |
| return to; |
| } |