blob: aa1426ea3659d9daa49a3c1aa673f50e1531b9f1 [file] [log] [blame]
// 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/log.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 fuchsia::bluetooth::Bool;
using fuchsia::bluetooth::Error;
using fuchsia::bluetooth::ErrorCode;
using fuchsia::bluetooth::Int8;
using fuchsia::bluetooth::Status;
namespace fble = fuchsia::bluetooth::le;
namespace fctrl = fuchsia::bluetooth::control;
namespace fhost = fuchsia::bluetooth::host;
namespace bthost {
namespace fidl_helpers {
namespace {
fctrl::TechnologyType TechnologyTypeToFidl(::btlib::gap::TechnologyType type) {
switch (type) {
case ::btlib::gap::TechnologyType::kLowEnergy:
return fctrl::TechnologyType::LOW_ENERGY;
case ::btlib::gap::TechnologyType::kClassic:
return fctrl::TechnologyType::CLASSIC;
case ::btlib::gap::TechnologyType::kDualMode:
return fctrl::TechnologyType::DUAL_MODE;
default:
ZX_PANIC("invalid technology type: %u", static_cast<unsigned int>(type));
break;
}
// This should never execute.
return fctrl::TechnologyType::DUAL_MODE;
}
btlib::common::UInt128 KeyDataFromFidl(const fhost::Key& key) {
btlib::common::UInt128 result;
static_assert(sizeof(key.value) == result.size(),
"keys must have the same size");
std::copy(key.value.begin(), key.value.end(), result.begin());
return result;
}
::fidl::Array<uint8_t, 16> KeyDataToFidl(const btlib::common::UInt128& key) {
::fidl::Array<uint8_t, 16> result;
static_assert(sizeof(key) == result.size(), "keys must have the same size");
std::copy(key.begin(), key.end(), result.begin());
return result;
}
btlib::sm::SecurityProperties SecurityPropsFromFidl(
const fhost::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);
}
fhost::SecurityProperties SecurityPropsToFidl(
const btlib::sm::SecurityProperties& sec_prop) {
fhost::SecurityProperties result;
result.authenticated = sec_prop.authenticated();
result.secure_connections = sec_prop.secure_connections();
result.encryption_key_size = sec_prop.enc_key_size();
return result;
}
btlib::common::DeviceAddress::Type BondingAddrTypeFromFidl(
const fhost::AddressType& type) {
switch (type) {
case fhost::AddressType::LE_RANDOM:
return btlib::common::DeviceAddress::Type::kLERandom;
case fhost::AddressType::LE_PUBLIC:
return btlib::common::DeviceAddress::Type::kLEPublic;
case fhost::AddressType::BREDR:
return btlib::common::DeviceAddress::Type::kBREDR;
default:
ZX_PANIC("invalid address type: %u", static_cast<unsigned int>(type));
break;
}
return btlib::common::DeviceAddress::Type::kBREDR;
}
fhost::AddressType BondingAddrTypeToFidl(
btlib::common::DeviceAddress::Type type) {
switch (type) {
case btlib::common::DeviceAddress::Type::kLERandom:
return fhost::AddressType::LE_RANDOM;
case btlib::common::DeviceAddress::Type::kLEPublic:
return fhost::AddressType::LE_PUBLIC;
case btlib::common::DeviceAddress::Type::kBREDR:
return fhost::AddressType::BREDR;
default:
// Anonymous is not a valid address type to use for bonding, so we treat
// that as a programming error.
ZX_PANIC("invalid address type for bonding: %u",
static_cast<unsigned int>(type));
break;
}
return fhost::AddressType::BREDR;
}
btlib::sm::LTK LtkFromFidl(const fhost::LTK& ltk) {
return btlib::sm::LTK(
SecurityPropsFromFidl(ltk.key.security_properties),
btlib::hci::LinkKey(KeyDataFromFidl(ltk.key), ltk.rand, ltk.ediv));
}
fhost::LTK LtkToFidl(const btlib::sm::LTK& ltk) {
fhost::LTK result;
result.key.security_properties = SecurityPropsToFidl(ltk.security());
result.key.value = KeyDataToFidl(ltk.key().value());
// TODO(armansito): Remove this field since its already captured in security
// properties.
result.key_size = ltk.security().enc_key_size();
result.rand = ltk.key().rand();
result.ediv = ltk.key().ediv();
return result;
}
btlib::sm::Key KeyFromFidl(const fhost::Key& key) {
return btlib::sm::Key(SecurityPropsFromFidl(key.security_properties),
KeyDataFromFidl(key));
}
fhost::Key KeyToFidl(const btlib::sm::Key& key) {
fhost::Key result;
result.security_properties = SecurityPropsToFidl(key.security());
result.value = KeyDataToFidl(key.value());
return result;
}
} // 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::sm::IOCapability IoCapabilityFromFidl(
fctrl::InputCapabilityType input, fctrl::OutputCapabilityType output) {
if (input == fctrl::InputCapabilityType::NONE &&
output == fctrl::OutputCapabilityType::NONE) {
return btlib::sm::IOCapability::kNoInputNoOutput;
} else if (input == fctrl::InputCapabilityType::KEYBOARD &&
output == fctrl::OutputCapabilityType::DISPLAY) {
return btlib::sm::IOCapability::kKeyboardDisplay;
} else if (input == fctrl::InputCapabilityType::KEYBOARD &&
output == fctrl::OutputCapabilityType::NONE) {
return btlib::sm::IOCapability::kKeyboardOnly;
} else if (input == fctrl::InputCapabilityType::NONE &&
output == fctrl::OutputCapabilityType::DISPLAY) {
return btlib::sm::IOCapability::kDisplayOnly;
} else if (input == fctrl::InputCapabilityType::CONFIRMATION &&
output == fctrl::OutputCapabilityType::DISPLAY) {
return btlib::sm::IOCapability::kDisplayYesNo;
}
return btlib::sm::IOCapability::kNoInputNoOutput;
}
btlib::sm::PairingData PairingDataFromFidl(const fhost::LEData& data) {
btlib::sm::PairingData result;
result.identity_address = btlib::common::DeviceAddress(
BondingAddrTypeFromFidl(data.address_type), data.address);
if (data.ltk) {
result.ltk = LtkFromFidl(*data.ltk);
}
if (data.irk) {
result.irk = KeyFromFidl(*data.irk);
}
if (data.csrk) {
result.csrk = KeyFromFidl(*data.csrk);
}
return result;
}
fctrl::AdapterInfo NewAdapterInfo(const ::btlib::gap::Adapter& adapter) {
fctrl::AdapterInfo adapter_info;
adapter_info.identifier = adapter.identifier();
adapter_info.technology = TechnologyTypeToFidl(adapter.state().type());
adapter_info.address = adapter.state().controller_address().ToString();
adapter_info.state = fctrl::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();
// TODO(armansito): Populate |local_service_uuids| as well.
return adapter_info;
}
fctrl::RemoteDevice NewRemoteDevice(const ::btlib::gap::RemoteDevice& device) {
fctrl::RemoteDevice fidl_device;
fidl_device.identifier = device.identifier();
fidl_device.address = device.address().value().ToString();
fidl_device.technology = TechnologyTypeToFidl(device.technology());
fidl_device.connected = device.connected();
fidl_device.bonded = device.bonded();
// Set default value for device appearance.
fidl_device.appearance = fctrl::Appearance::UNKNOWN;
// |service_uuids| is not a nullable field, so we need to assign something
// to it.
fidl_device.service_uuids.resize(0);
if (device.rssi() != ::btlib::hci::kRSSIInvalid) {
fidl_device.rssi = Int8::New();
fidl_device.rssi->value = device.rssi();
}
if (device.name()) {
fidl_device.name = *device.name();
}
if (device.le()) {
::btlib::gap::AdvertisingData adv_data;
if (!::btlib::gap::AdvertisingData::FromBytes(
device.le()->advertising_data(), &adv_data)) {
return fidl_device;
}
for (const auto& uuid : adv_data.service_uuids()) {
fidl_device.service_uuids.push_back(uuid.ToString());
}
if (adv_data.appearance()) {
fidl_device.appearance =
static_cast<fctrl::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;
}
fctrl::RemoteDevicePtr NewRemoteDevicePtr(
const ::btlib::gap::RemoteDevice& device) {
auto fidl_device = fctrl::RemoteDevice::New();
*fidl_device = NewRemoteDevice(device);
return fidl_device;
}
fhost::BondingData NewBondingData(const ::btlib::gap::Adapter& adapter,
const ::btlib::gap::RemoteDevice& device) {
fhost::BondingData out_data;
out_data.identifier = device.identifier();
out_data.local_address = adapter.state().controller_address().ToString();
if (device.name()) {
out_data.name = *device.name();
}
// Store LE data.
if (device.le() && device.le()->bond_data()) {
out_data.le = fhost::LEData::New();
const auto& le_data = *device.le()->bond_data();
const auto& identity =
le_data.identity_address ? *le_data.identity_address : device.address();
out_data.le->address = identity.value().ToString();
out_data.le->address_type = BondingAddrTypeToFidl(identity.type());
// TODO(armansito): Populate the preferred connection parameters here.
// TODO(armansito): Populate with discovered GATT services. We initialize
// this as empty as |services| is not nullable.
out_data.le->services.resize(0);
if (le_data.ltk) {
out_data.le->ltk = fhost::LTK::New();
*out_data.le->ltk = LtkToFidl(*le_data.ltk);
}
if (le_data.irk) {
out_data.le->irk = fhost::Key::New();
*out_data.le->irk = KeyToFidl(*le_data.irk);
}
if (le_data.csrk) {
out_data.le->csrk = fhost::Key::New();
*out_data.le->csrk = KeyToFidl(*le_data.csrk);
}
}
// TODO(armansito): Store BR/EDR data.
return out_data;
}
fble::RemoteDevicePtr NewLERemoteDevice(
const ::btlib::gap::RemoteDevice& device) {
::btlib::gap::AdvertisingData ad;
if (!device.le()) {
return nullptr;
}
const auto& le = *device.le();
auto fidl_device = fble::RemoteDevice::New();
fidl_device->identifier = device.identifier();
fidl_device->connectable = device.connectable();
// Initialize advertising data only if its non-empty.
if (le.advertising_data().size() != 0u) {
::btlib::gap::AdvertisingData ad;
if (!::btlib::gap::AdvertisingData::FromBytes(le.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 fble::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 fble::ScanFilter& fidl_filter,
::btlib::gap::DiscoveryFilter* out_filter) {
ZX_DEBUG_ASSERT(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)) {
bt_log(TRACE, "bt-host", "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;
}