blob: dfb74234812dfb81822c46b3d7f6f09cc62958d7 [file] [log] [blame]
// 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 "components/arc/bluetooth/bluetooth_struct_traits.h"
#include <initializer_list>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "base/stl_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "device/bluetooth/bluetooth_advertisement.h"
#include "device/bluetooth/bluetooth_uuid.h"
namespace {
// BluetoothUUID helpers.
constexpr size_t kUUIDSize = 16;
bool IsNonHex(char c) {
return !isxdigit(c);
}
std::string StripNonHex(const std::string& str) {
std::string result = str;
base::EraseIf(result, IsNonHex);
return result;
}
// BluetoothAdvertisement helpers.
struct AdvertisementEntry {
virtual ~AdvertisementEntry() {}
virtual void AddTo(device::BluetoothAdvertisement::Data* data) {}
};
struct ServiceUUID16Entry : public AdvertisementEntry {
std::vector<uint16_t> service_uuids_16;
~ServiceUUID16Entry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
auto string_uuids = data->service_uuids();
if (string_uuids == nullptr)
string_uuids = std::make_unique<std::vector<std::string>>();
for (const auto& uuid : service_uuids_16) {
string_uuids->emplace_back(base::StringPrintf("%04x", uuid));
}
data->set_service_uuids(std::move(string_uuids));
}
};
struct ServiceUUIDEntry : public AdvertisementEntry {
std::vector<device::BluetoothUUID> service_uuids;
~ServiceUUIDEntry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
auto string_uuids = data->service_uuids();
if (string_uuids == nullptr)
string_uuids = std::make_unique<std::vector<std::string>>();
for (const auto& uuid : service_uuids) {
string_uuids->emplace_back(uuid.value());
}
data->set_service_uuids(std::move(string_uuids));
}
};
struct ServiceDataEntry : public AdvertisementEntry {
uint16_t service_uuid;
std::vector<uint8_t> service_data;
~ServiceDataEntry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
std::string string_uuid = base::StringPrintf("%04x", service_uuid);
using MapType = std::map<std::string, std::vector<uint8_t>>;
data->set_service_data(
std::make_unique<MapType, std::initializer_list<MapType::value_type>>(
{{string_uuid, service_data}}));
}
};
struct ManufacturerDataEntry : public AdvertisementEntry {
uint16_t company_id_code;
std::vector<uint8_t> blob;
~ManufacturerDataEntry() override {}
void AddTo(device::BluetoothAdvertisement::Data* data) override {
using MapType = std::map<uint16_t, std::vector<uint8_t>>;
data->set_manufacturer_data(
std::make_unique<MapType, std::initializer_list<MapType::value_type>>(
{{company_id_code, blob}}));
}
};
uint16_t ExtractCompanyIdentifierCode(std::vector<uint8_t>* blob) {
// The company identifier code is in little-endian.
uint16_t company_id_code = (*blob)[1] << 8 | (*blob)[0];
blob->erase(blob->begin(), blob->begin() + sizeof(uint16_t));
return company_id_code;
}
} // namespace
namespace mojo {
// static
std::vector<uint8_t>
StructTraits<arc::mojom::BluetoothUUIDDataView, device::BluetoothUUID>::uuid(
const device::BluetoothUUID& input) {
// TODO(dcheng): Figure out what to do here, this is called twice on
// serialization. Building a vector is a little inefficient.
std::string uuid_str = StripNonHex(input.canonical_value());
std::vector<uint8_t> address_bytes;
base::HexStringToBytes(uuid_str, &address_bytes);
return address_bytes;
}
// static
bool StructTraits<arc::mojom::BluetoothUUIDDataView,
device::BluetoothUUID>::Read(
arc::mojom::BluetoothUUIDDataView data,
device::BluetoothUUID* output) {
std::vector<uint8_t> address_bytes;
if (!data.ReadUuid(&address_bytes))
return false;
if (address_bytes.size() != kUUIDSize)
return false;
// BluetoothUUID expects the format below with the dashes inserted.
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
std::string uuid_str =
base::HexEncode(address_bytes.data(), address_bytes.size());
constexpr size_t kUuidDashPos[] = {8, 13, 18, 23};
for (auto pos : kUuidDashPos)
uuid_str = uuid_str.insert(pos, "-");
device::BluetoothUUID result(uuid_str);
DCHECK(result.IsValid());
*output = result;
return true;
}
template <>
struct EnumTraits<arc::mojom::BluetoothAdvertisementType,
device::BluetoothAdvertisement::AdvertisementType> {
static bool FromMojom(
arc::mojom::BluetoothAdvertisementType mojom_type,
device::BluetoothAdvertisement::AdvertisementType* type) {
switch (mojom_type) {
case arc::mojom::BluetoothAdvertisementType::ADV_TYPE_CONNECTABLE:
case arc::mojom::BluetoothAdvertisementType::ADV_TYPE_SCANNABLE:
*type = device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_PERIPHERAL;
return true;
case arc::mojom::BluetoothAdvertisementType::ADV_TYPE_NON_CONNECTABLE:
*type = device::BluetoothAdvertisement::ADVERTISEMENT_TYPE_BROADCAST;
return true;
}
NOTREACHED() << "Invalid type: " << static_cast<uint32_t>(mojom_type);
return false;
}
};
template <>
struct StructTraits<arc::mojom::BluetoothServiceDataDataView,
ServiceDataEntry> {
static bool Read(arc::mojom::BluetoothServiceDataDataView data,
ServiceDataEntry* output) {
output->service_uuid = data.uuid_16bit();
return data.ReadData(&output->service_data);
}
};
template <>
struct UnionTraits<arc::mojom::BluetoothAdvertisingDataDataView,
std::unique_ptr<AdvertisementEntry>> {
static bool Read(arc::mojom::BluetoothAdvertisingDataDataView data,
std::unique_ptr<AdvertisementEntry>* output) {
switch (data.tag()) {
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::
SERVICE_UUIDS_16: {
std::unique_ptr<ServiceUUID16Entry> service_uuids_16 =
std::make_unique<ServiceUUID16Entry>();
if (!data.ReadServiceUuids16(&service_uuids_16->service_uuids_16))
return false;
*output = std::move(service_uuids_16);
break;
}
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::SERVICE_UUIDS: {
std::unique_ptr<ServiceUUIDEntry> service_uuids =
std::make_unique<ServiceUUIDEntry>();
if (!data.ReadServiceUuids(&service_uuids->service_uuids))
return false;
*output = std::move(service_uuids);
break;
}
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::SERVICE_DATA: {
std::unique_ptr<ServiceDataEntry> service_data =
std::make_unique<ServiceDataEntry>();
if (!data.ReadServiceData(service_data.get()))
return false;
*output = std::move(service_data);
break;
}
case arc::mojom::BluetoothAdvertisingDataDataView::Tag::
MANUFACTURER_DATA: {
std::unique_ptr<ManufacturerDataEntry> manufacturer_data =
std::make_unique<ManufacturerDataEntry>();
// We get manufacturer data as a big blob. The first two bytes
// should be a company identifier code and the rest is manufacturer-
// specific.
std::vector<uint8_t> blob;
if (!data.ReadManufacturerData(&blob))
return false;
if (blob.size() < sizeof(uint16_t)) {
LOG(WARNING) << "Advertisement had malformed manufacturer data";
return false;
}
manufacturer_data->company_id_code =
ExtractCompanyIdentifierCode(&blob);
manufacturer_data->blob = std::move(blob);
*output = std::move(manufacturer_data);
break;
}
default: {
LOG(WARNING) << "Bluetooth advertising data case not implemented";
// Default AdvertisementEntry does nothing when added to the
// device::BluetoothAdvertisement::AdvertisementData, so data we
// don't know how to handle yet will be dropped but won't cause a
// failure to deserialize.
*output = std::make_unique<AdvertisementEntry>();
break;
}
}
return true;
}
};
// static
bool StructTraits<arc::mojom::BluetoothAdvertisementDataView,
std::unique_ptr<device::BluetoothAdvertisement::Data>>::
Read(arc::mojom::BluetoothAdvertisementDataView advertisement,
std::unique_ptr<device::BluetoothAdvertisement::Data>* output) {
device::BluetoothAdvertisement::AdvertisementType adv_type;
if (!advertisement.ReadType(&adv_type))
return false;
auto data = std::make_unique<device::BluetoothAdvertisement::Data>(adv_type);
std::vector<std::unique_ptr<AdvertisementEntry>> adv_entries;
if (!advertisement.ReadData(&adv_entries))
return false;
for (const auto& adv_entry : adv_entries)
adv_entry->AddTo(data.get());
data->set_include_tx_power(advertisement.include_tx_power());
*output = std::move(data);
return true;
}
} // namespace mojo