blob: 3fa1467dc974a11888a3015a2df00fa30f31cb69 [file] [log] [blame]
// Copyright 2014 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 "device/bluetooth/bluetooth_low_energy_win.h"
#include <memory>
#include <utility>
#include "base/files/file.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/strings/sys_string_conversions.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_version.h"
#include "third_party/re2/src/re2/re2.h"
namespace {
static device::win::BluetoothLowEnergyWrapper* g_instance_ = nullptr;
using device::win::DeviceRegistryPropertyValue;
using device::win::DevicePropertyValue;
using device::win::BluetoothLowEnergyDeviceInfo;
using device::win::BluetoothLowEnergyServiceInfo;
const char kPlatformNotSupported[] =
"Bluetooth Low energy is only supported on Windows 8 and later.";
const char kDeviceEnumError[] = "Error enumerating Bluetooth LE devices.";
const char kDeviceInfoError[] =
"Error retrieving Bluetooth LE device information.";
const char kDeviceAddressError[] =
"Device instance ID value does not seem to contain a Bluetooth Adapter "
"address.";
const char kDeviceFriendlyNameError[] = "Device name is not valid.";
const char kInvalidBluetoothAddress[] = "Bluetooth address format is invalid.";
struct Patterns {
Patterns();
// Patterns is only instantiated as a leaky LazyInstance, so the destructor
// is never called.
~Patterns() = delete;
const RE2 address_regex;
};
Patterns::Patterns()
// Match an embedded MAC address in a device path.
// e.g.
// BTHLEDEVICE\{0000180F-0000-1000-8000-00805F9B34FB}_DEV_VID&01000A_PID&
// 014C_REV&0100_818B4B0BACE6\8&4C387F7&0&0020
// matches _818B4B0BACE6\
// and the 12 hex digits are selected in a capture group.
: address_regex(R"(_([0-9A-F]{12})\\)") {}
base::LazyInstance<Patterns>::Leaky g_patterns = LAZY_INSTANCE_INITIALIZER;
// Like ScopedHandle but for HDEVINFO. Only use this on HDEVINFO returned from
// SetupDiGetClassDevs.
class DeviceInfoSetTraits {
public:
typedef HDEVINFO Handle;
static bool CloseHandle(HDEVINFO handle) {
return ::SetupDiDestroyDeviceInfoList(handle) != FALSE;
}
static bool IsHandleValid(HDEVINFO handle) {
return handle != INVALID_HANDLE_VALUE;
}
static HDEVINFO NullHandle() { return INVALID_HANDLE_VALUE; }
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(DeviceInfoSetTraits);
};
typedef base::win::GenericScopedHandle<DeviceInfoSetTraits,
base::win::DummyVerifierTraits>
ScopedDeviceInfoSetHandle;
std::string FormatBluetoothError(const char* message, HRESULT hr) {
std::ostringstream string_stream;
string_stream << message;
if (FAILED(hr))
string_stream << logging::SystemErrorCodeToString(hr);
return string_stream.str();
}
bool CheckInsufficientBuffer(bool success,
const char* message,
std::string* error) {
if (success) {
*error = FormatBluetoothError(message, S_OK);
return false;
}
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
if (hr != HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)) {
*error = FormatBluetoothError(message, hr);
return false;
}
return true;
}
bool CheckHResult(HRESULT hr, const char* message, std::string* error) {
if (FAILED(hr)) {
*error = FormatBluetoothError(message, hr);
return false;
}
return true;
}
bool CheckSuccess(bool success, const char* message, std::string* error) {
if (!success) {
CheckHResult(HRESULT_FROM_WIN32(GetLastError()), message, error);
return false;
}
return true;
}
bool CheckNoData(HRESULT hr, size_t length) {
if (hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND))
return true;
if (SUCCEEDED(hr) && length == 0)
return true;
return false;
}
bool CheckMoreData(HRESULT hr, const char* message, std::string* error) {
if (SUCCEEDED(hr)) {
*error = FormatBluetoothError(message, hr);
return false;
}
if (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA)) {
*error = FormatBluetoothError(message, hr);
return false;
}
return true;
}
bool CheckExpectedLength(size_t actual_length,
size_t expected_length,
const char* message,
std::string* error) {
if (actual_length != expected_length) {
*error = FormatBluetoothError(message, E_FAIL);
return false;
}
return true;
}
bool CollectBluetoothLowEnergyDeviceProperty(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVINFO_DATA device_info_data,
const DEVPROPKEY& key,
std::unique_ptr<DevicePropertyValue>* value,
std::string* error) {
DWORD required_length;
DEVPROPTYPE prop_type;
BOOL success = SetupDiGetDeviceProperty(device_info_handle.Get(),
device_info_data,
&key,
&prop_type,
NULL,
0,
&required_length,
0);
if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
return false;
std::unique_ptr<uint8_t[]> prop_value(new uint8_t[required_length]);
DWORD actual_length = required_length;
success = SetupDiGetDeviceProperty(device_info_handle.Get(),
device_info_data,
&key,
&prop_type,
prop_value.get(),
actual_length,
&required_length,
0);
if (!CheckSuccess(!!success, kDeviceInfoError, error))
return false;
if (!CheckExpectedLength(
actual_length, required_length, kDeviceInfoError, error)) {
return false;
}
(*value) = std::unique_ptr<DevicePropertyValue>(
new DevicePropertyValue(prop_type, std::move(prop_value), actual_length));
return true;
}
bool CollectBluetoothLowEnergyDeviceRegistryProperty(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVINFO_DATA device_info_data,
DWORD property_id,
std::unique_ptr<DeviceRegistryPropertyValue>* value,
std::string* error) {
ULONG required_length = 0;
BOOL success = SetupDiGetDeviceRegistryProperty(device_info_handle.Get(),
device_info_data,
property_id,
NULL,
NULL,
0,
&required_length);
if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
return false;
std::unique_ptr<uint8_t[]> property_value(new uint8_t[required_length]);
ULONG actual_length = required_length;
DWORD property_type;
success = SetupDiGetDeviceRegistryProperty(device_info_handle.Get(),
device_info_data,
property_id,
&property_type,
property_value.get(),
actual_length,
&required_length);
if (!CheckSuccess(!!success, kDeviceInfoError, error))
return false;
if (!CheckExpectedLength(
actual_length, required_length, kDeviceInfoError, error)) {
return false;
}
(*value) = DeviceRegistryPropertyValue::Create(
property_type, std::move(property_value), actual_length);
return true;
}
bool CollectBluetoothLowEnergyDeviceInstanceId(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVINFO_DATA device_info_data,
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
std::string* error) {
ULONG required_length = 0;
BOOL success = SetupDiGetDeviceInstanceId(
device_info_handle.Get(), device_info_data, NULL, 0, &required_length);
if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
return false;
std::unique_ptr<WCHAR[]> instance_id(new WCHAR[required_length]);
ULONG actual_length = required_length;
success = SetupDiGetDeviceInstanceId(device_info_handle.Get(),
device_info_data,
instance_id.get(),
actual_length,
&required_length);
if (!CheckSuccess(!!success, kDeviceInfoError, error))
return false;
if (!CheckExpectedLength(
actual_length, required_length, kDeviceInfoError, error)) {
return false;
}
if (actual_length >= 1) {
// Ensure string is zero terminated.
instance_id.get()[actual_length - 1] = 0;
device_info->id = base::SysWideToUTF8(instance_id.get());
}
return true;
}
bool CollectBluetoothLowEnergyDeviceFriendlyName(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVINFO_DATA device_info_data,
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
std::string* error) {
std::unique_ptr<DeviceRegistryPropertyValue> property_value;
if (!CollectBluetoothLowEnergyDeviceRegistryProperty(device_info_handle,
device_info_data,
SPDRP_FRIENDLYNAME,
&property_value,
error)) {
return false;
}
if (property_value->property_type() != REG_SZ) {
*error = kDeviceFriendlyNameError;
return false;
}
device_info->friendly_name = property_value->AsString();
return true;
}
bool ExtractBluetoothAddressFromDeviceInstanceId(const std::string& instance_id,
BLUETOOTH_ADDRESS* btha,
std::string* error) {
std::string address;
if (!RE2::PartialMatch(instance_id, g_patterns.Get().address_regex,
&address)) {
*error = kDeviceAddressError;
return false;
}
int buffer[6];
int result =
sscanf_s(address.c_str(), "%02X%02X%02X%02X%02X%02X", &buffer[5],
&buffer[4], &buffer[3], &buffer[2], &buffer[1], &buffer[0]);
if (result != 6) {
*error = kInvalidBluetoothAddress;
return false;
}
ZeroMemory(btha, sizeof(*btha));
btha->rgBytes[0] = buffer[0];
btha->rgBytes[1] = buffer[1];
btha->rgBytes[2] = buffer[2];
btha->rgBytes[3] = buffer[3];
btha->rgBytes[4] = buffer[4];
btha->rgBytes[5] = buffer[5];
return true;
}
bool CollectBluetoothLowEnergyDeviceAddress(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVINFO_DATA device_info_data,
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
std::string* error) {
// TODO(rpaquay): We exctract the bluetooth device address from the device
// instance ID string, as we did not find a more formal API for retrieving the
// bluetooth address of a Bluetooth Low Energy device.
// A Bluetooth device instance ID often has the following format (under
// Win8+):
// BTHLE\DEV_BC6A29AB5FB0\8&31038925&0&BC6A29AB5FB0
// However, they have also been seen with the following, more expanded,
// format:
// BTHLEDEVICE\{0000180F-0000-1000-8000-00805F9B34FB}_DEV_VID&01000A_PID&
// 014C_REV&0100_818B4B0BACE6\8&4C387F7&0&0020
return ExtractBluetoothAddressFromDeviceInstanceId(
device_info->id, &device_info->address, error);
}
bool CollectBluetoothLowEnergyDeviceStatus(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVINFO_DATA device_info_data,
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo>& device_info,
std::string* error) {
std::unique_ptr<DevicePropertyValue> value;
if (!CollectBluetoothLowEnergyDeviceProperty(device_info_handle,
device_info_data,
DEVPKEY_Device_DevNodeStatus,
&value,
error)) {
return false;
}
if (value->property_type() != DEVPROP_TYPE_UINT32) {
*error = kDeviceInfoError;
return false;
}
device_info->connected = !(value->AsUint32() & DN_DEVICE_DISCONNECTED);
// Windows 8 exposes BLE devices only if they are visible and paired. This
// might change in the future if Windows offers a public API for discovering
// and pairing BLE devices.
device_info->visible = true;
device_info->authenticated = true;
return true;
}
bool CollectBluetoothLowEnergyDeviceServices(
const base::FilePath& device_path,
std::vector<std::unique_ptr<BluetoothLowEnergyServiceInfo>>* services,
std::string* error) {
base::File file(device_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid()) {
*error = file.ErrorToString(file.error_details());
return false;
}
USHORT required_length;
HRESULT hr = BluetoothGATTGetServices(file.GetPlatformFile(),
0,
NULL,
&required_length,
BLUETOOTH_GATT_FLAG_NONE);
if (CheckNoData(hr, required_length))
return true;
if (!CheckMoreData(hr, kDeviceInfoError, error))
return false;
std::unique_ptr<BTH_LE_GATT_SERVICE[]> gatt_services(
new BTH_LE_GATT_SERVICE[required_length]);
USHORT actual_length = required_length;
hr = BluetoothGATTGetServices(file.GetPlatformFile(),
actual_length,
gatt_services.get(),
&required_length,
BLUETOOTH_GATT_FLAG_NONE);
if (!CheckHResult(hr, kDeviceInfoError, error))
return false;
if (!CheckExpectedLength(
actual_length, required_length, kDeviceInfoError, error)) {
return false;
}
for (USHORT i = 0; i < actual_length; ++i) {
BTH_LE_GATT_SERVICE& gatt_service(gatt_services.get()[i]);
auto service_info = std::make_unique<BluetoothLowEnergyServiceInfo>();
service_info->uuid = gatt_service.ServiceUuid;
service_info->attribute_handle = gatt_service.AttributeHandle;
services->push_back(std::move(service_info));
}
return true;
}
bool CollectBluetoothLowEnergyDeviceInfo(
const ScopedDeviceInfoSetHandle& device_info_handle,
PSP_DEVICE_INTERFACE_DATA device_interface_data,
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info,
std::string* error) {
// Retrieve required # of bytes for interface details
ULONG required_length = 0;
BOOL success = SetupDiGetDeviceInterfaceDetail(device_info_handle.Get(),
device_interface_data,
NULL,
0,
&required_length,
NULL);
if (!CheckInsufficientBuffer(!!success, kDeviceInfoError, error))
return false;
std::unique_ptr<uint8_t[]> interface_data(new uint8_t[required_length]);
ZeroMemory(interface_data.get(), required_length);
PSP_DEVICE_INTERFACE_DETAIL_DATA device_interface_detail_data =
reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA>(interface_data.get());
device_interface_detail_data->cbSize =
sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
SP_DEVINFO_DATA device_info_data = {0};
device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
ULONG actual_length = required_length;
success = SetupDiGetDeviceInterfaceDetail(device_info_handle.Get(),
device_interface_data,
device_interface_detail_data,
actual_length,
&required_length,
&device_info_data);
if (!CheckSuccess(!!success, kDeviceInfoError, error))
return false;
if (!CheckExpectedLength(
actual_length, required_length, kDeviceInfoError, error)) {
return false;
}
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo> result(
new device::win::BluetoothLowEnergyDeviceInfo());
result->path =
base::FilePath(std::wstring(device_interface_detail_data->DevicePath));
if (!CollectBluetoothLowEnergyDeviceInstanceId(
device_info_handle, &device_info_data, result, error)) {
return false;
}
// Get the friendly name. If it fails it is OK to leave the
// device_info_data.friendly_name as nullopt indicating the name not read.
CollectBluetoothLowEnergyDeviceFriendlyName(device_info_handle,
&device_info_data, result, error);
if (!CollectBluetoothLowEnergyDeviceAddress(
device_info_handle, &device_info_data, result, error)) {
return false;
}
if (!CollectBluetoothLowEnergyDeviceStatus(
device_info_handle, &device_info_data, result, error)) {
return false;
}
(*device_info) = std::move(result);
return true;
}
enum DeviceInfoResult { kOk, kError, kNoMoreDevices };
// For |device_interface_guid| see the Note of below
// EnumerateKnownBLEOrBLEGattServiceDevices interface.
DeviceInfoResult EnumerateSingleBluetoothLowEnergyDevice(
GUID device_interface_guid,
const ScopedDeviceInfoSetHandle& device_info_handle,
DWORD device_index,
std::unique_ptr<device::win::BluetoothLowEnergyDeviceInfo>* device_info,
std::string* error) {
GUID BluetoothInterfaceGUID = device_interface_guid;
SP_DEVICE_INTERFACE_DATA device_interface_data = {0};
device_interface_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
BOOL success = ::SetupDiEnumDeviceInterfaces(device_info_handle.Get(),
NULL,
&BluetoothInterfaceGUID,
device_index,
&device_interface_data);
if (!success) {
HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
if (hr == HRESULT_FROM_WIN32(ERROR_NO_MORE_ITEMS)) {
return kNoMoreDevices;
}
*error = FormatBluetoothError(kDeviceInfoError, hr);
return kError;
}
if (!CollectBluetoothLowEnergyDeviceInfo(
device_info_handle, &device_interface_data, device_info, error)) {
return kError;
}
return kOk;
}
// Opens a Device Info Set that can be used to enumerate Bluetooth LE devices
// present on the machine. For |device_interface_guid| see the Note of below
// EnumerateKnownBLEOrBLEGattServiceDevices interface.
HRESULT OpenBluetoothLowEnergyDevices(GUID device_interface_guid,
ScopedDeviceInfoSetHandle* handle) {
GUID BluetoothClassGUID = device_interface_guid;
ScopedDeviceInfoSetHandle result(SetupDiGetClassDevs(
&BluetoothClassGUID, NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE));
if (!result.IsValid()) {
return HRESULT_FROM_WIN32(::GetLastError());
}
(*handle) = std::move(result);
return S_OK;
}
// Enumerate known Bluetooth low energy devices or Bluetooth low energy GATT
// service devices according to |device_interface_guid|.
// Note: |device_interface_guid| = GUID_BLUETOOTHLE_DEVICE_INTERFACE corresponds
// Bluetooth low energy devices. |device_interface_guid| =
// GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE corresponds Bluetooth low energy
// Gatt service devices.
bool EnumerateKnownBLEOrBLEGattServiceDevices(
GUID guid,
std::vector<std::unique_ptr<BluetoothLowEnergyDeviceInfo>>* devices,
std::string* error) {
ScopedDeviceInfoSetHandle info_set_handle;
HRESULT hr = OpenBluetoothLowEnergyDevices(guid, &info_set_handle);
if (FAILED(hr)) {
*error = FormatBluetoothError(kDeviceEnumError, hr);
return false;
}
for (DWORD i = 0;; ++i) {
std::unique_ptr<BluetoothLowEnergyDeviceInfo> device_info;
DeviceInfoResult result = EnumerateSingleBluetoothLowEnergyDevice(
guid, info_set_handle, i, &device_info, error);
switch (result) {
case kNoMoreDevices:
return true;
case kError:
return false;
case kOk:
devices->push_back(std::move(device_info));
}
}
}
} // namespace
namespace device {
namespace win {
// static
std::unique_ptr<DeviceRegistryPropertyValue>
DeviceRegistryPropertyValue::Create(DWORD property_type,
std::unique_ptr<uint8_t[]> value,
size_t value_size) {
switch (property_type) {
case REG_SZ: {
// Ensure string is zero terminated.
size_t character_size = value_size / sizeof(WCHAR);
CHECK_EQ(character_size * sizeof(WCHAR), value_size);
CHECK_GE(character_size, 1u);
WCHAR* value_string = reinterpret_cast<WCHAR*>(value.get());
value_string[character_size - 1] = 0;
break;
}
case REG_DWORD: {
CHECK_EQ(value_size, sizeof(DWORD));
break;
}
}
return base::WrapUnique(
new DeviceRegistryPropertyValue(property_type, std::move(value)));
}
DeviceRegistryPropertyValue::DeviceRegistryPropertyValue(
DWORD property_type,
std::unique_ptr<uint8_t[]> value)
: property_type_(property_type), value_(std::move(value)) {}
DeviceRegistryPropertyValue::~DeviceRegistryPropertyValue() {
}
std::string DeviceRegistryPropertyValue::AsString() const {
CHECK_EQ(property_type_, static_cast<DWORD>(REG_SZ));
WCHAR* value_string = reinterpret_cast<WCHAR*>(value_.get());
return base::SysWideToUTF8(value_string);
}
DWORD DeviceRegistryPropertyValue::AsDWORD() const {
CHECK_EQ(property_type_, static_cast<DWORD>(REG_DWORD));
DWORD* value = reinterpret_cast<DWORD*>(value_.get());
return *value;
}
DevicePropertyValue::DevicePropertyValue(DEVPROPTYPE property_type,
std::unique_ptr<uint8_t[]> value,
size_t value_size)
: property_type_(property_type),
value_(std::move(value)),
value_size_(value_size) {}
DevicePropertyValue::~DevicePropertyValue() {
}
uint32_t DevicePropertyValue::AsUint32() const {
CHECK_EQ(property_type_, static_cast<DEVPROPTYPE>(DEVPROP_TYPE_UINT32));
CHECK_EQ(value_size_, sizeof(uint32_t));
return *reinterpret_cast<uint32_t*>(value_.get());
}
BluetoothLowEnergyServiceInfo::BluetoothLowEnergyServiceInfo() {
}
BluetoothLowEnergyServiceInfo::~BluetoothLowEnergyServiceInfo() {
}
BluetoothLowEnergyDeviceInfo::BluetoothLowEnergyDeviceInfo()
: visible(false), authenticated(false), connected(false) {
address.ullLong = BLUETOOTH_NULL_ADDRESS;
}
BluetoothLowEnergyDeviceInfo::~BluetoothLowEnergyDeviceInfo() {
}
bool ExtractBluetoothAddressFromDeviceInstanceIdForTesting(
const std::string& instance_id,
BLUETOOTH_ADDRESS* btha,
std::string* error) {
return ExtractBluetoothAddressFromDeviceInstanceId(instance_id, btha, error);
}
BluetoothLowEnergyWrapper* BluetoothLowEnergyWrapper::GetInstance() {
if (g_instance_ == nullptr) {
g_instance_ = new BluetoothLowEnergyWrapper();
}
return g_instance_;
}
void BluetoothLowEnergyWrapper::DeleteInstance() {
delete g_instance_;
g_instance_ = nullptr;
}
void BluetoothLowEnergyWrapper::SetInstanceForTest(
BluetoothLowEnergyWrapper* instance) {
delete g_instance_;
g_instance_ = instance;
}
BluetoothLowEnergyWrapper::BluetoothLowEnergyWrapper() {}
BluetoothLowEnergyWrapper::~BluetoothLowEnergyWrapper() {}
bool BluetoothLowEnergyWrapper::IsBluetoothLowEnergySupported() {
return base::win::GetVersion() >= base::win::VERSION_WIN8;
}
bool BluetoothLowEnergyWrapper::EnumerateKnownBluetoothLowEnergyDevices(
std::vector<std::unique_ptr<BluetoothLowEnergyDeviceInfo>>* devices,
std::string* error) {
if (!IsBluetoothLowEnergySupported()) {
*error = kPlatformNotSupported;
return false;
}
return EnumerateKnownBLEOrBLEGattServiceDevices(
GUID_BLUETOOTHLE_DEVICE_INTERFACE, devices, error);
}
bool BluetoothLowEnergyWrapper::
EnumerateKnownBluetoothLowEnergyGattServiceDevices(
std::vector<std::unique_ptr<BluetoothLowEnergyDeviceInfo>>* devices,
std::string* error) {
if (!IsBluetoothLowEnergySupported()) {
*error = kPlatformNotSupported;
return false;
}
return EnumerateKnownBLEOrBLEGattServiceDevices(
GUID_BLUETOOTH_GATT_SERVICE_DEVICE_INTERFACE, devices, error);
}
bool BluetoothLowEnergyWrapper::EnumerateKnownBluetoothLowEnergyServices(
const base::FilePath& device_path,
std::vector<std::unique_ptr<BluetoothLowEnergyServiceInfo>>* services,
std::string* error) {
if (!IsBluetoothLowEnergySupported()) {
*error = kPlatformNotSupported;
return false;
}
return CollectBluetoothLowEnergyDeviceServices(device_path, services, error);
}
HRESULT BluetoothLowEnergyWrapper::ReadCharacteristicsOfAService(
base::FilePath& service_path,
const PBTH_LE_GATT_SERVICE service,
std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC>* out_included_characteristics,
USHORT* out_counts) {
base::File file(service_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
USHORT allocated_length = 0;
HRESULT hr = BluetoothGATTGetCharacteristics(file.GetPlatformFile(), service,
0, NULL, &allocated_length,
BLUETOOTH_GATT_FLAG_NONE);
if (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA))
return hr;
out_included_characteristics->reset(
new BTH_LE_GATT_CHARACTERISTIC[allocated_length]);
hr = BluetoothGATTGetCharacteristics(file.GetPlatformFile(), service,
allocated_length,
out_included_characteristics->get(),
out_counts, BLUETOOTH_GATT_FLAG_NONE);
if (SUCCEEDED(hr) && allocated_length != *out_counts) {
LOG(ERROR) << "Retrieved charactersitics is not equal to expected"
<< " allocated_length " << allocated_length << " got "
<< *out_counts;
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
}
if (FAILED(hr)) {
out_included_characteristics->reset(nullptr);
*out_counts = 0;
}
return hr;
}
HRESULT BluetoothLowEnergyWrapper::ReadDescriptorsOfACharacteristic(
base::FilePath& service_path,
const PBTH_LE_GATT_CHARACTERISTIC characteristic,
std::unique_ptr<BTH_LE_GATT_DESCRIPTOR>* out_included_descriptors,
USHORT* out_counts) {
base::File file(service_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
USHORT allocated_length = 0;
HRESULT hr = BluetoothGATTGetDescriptors(
file.GetPlatformFile(), characteristic, 0, NULL, &allocated_length,
BLUETOOTH_GATT_FLAG_NONE);
if (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA))
return hr;
out_included_descriptors->reset(new BTH_LE_GATT_DESCRIPTOR[allocated_length]);
hr = BluetoothGATTGetDescriptors(
file.GetPlatformFile(), characteristic, allocated_length,
out_included_descriptors->get(), out_counts, BLUETOOTH_GATT_FLAG_NONE);
if (SUCCEEDED(hr) && allocated_length != *out_counts) {
LOG(ERROR) << "Retrieved descriptors is not equal to expected"
<< " allocated_length " << allocated_length << " got "
<< *out_counts;
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
}
if (FAILED(hr)) {
out_included_descriptors->reset(nullptr);
*out_counts = 0;
}
return hr;
}
HRESULT BluetoothLowEnergyWrapper::ReadCharacteristicValue(
base::FilePath& service_path,
const PBTH_LE_GATT_CHARACTERISTIC characteristic,
std::unique_ptr<BTH_LE_GATT_CHARACTERISTIC_VALUE>* out_value) {
base::File file(service_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
USHORT allocated_length = 0;
HRESULT hr = BluetoothGATTGetCharacteristicValue(
file.GetPlatformFile(), characteristic, 0, NULL, &allocated_length,
BLUETOOTH_GATT_FLAG_NONE);
if (hr != HRESULT_FROM_WIN32(ERROR_MORE_DATA))
return hr;
out_value->reset(
(PBTH_LE_GATT_CHARACTERISTIC_VALUE)(new UCHAR[allocated_length]));
USHORT out_length = 0;
hr = BluetoothGATTGetCharacteristicValue(
file.GetPlatformFile(), characteristic, (ULONG)allocated_length,
out_value->get(), &out_length, BLUETOOTH_GATT_FLAG_NONE);
if (SUCCEEDED(hr) && allocated_length != out_length) {
LOG(ERROR) << "Retrieved characteristic value size is not equal to expected"
<< " allocated_length " << allocated_length << " got "
<< out_length;
hr = HRESULT_FROM_WIN32(ERROR_INVALID_USER_BUFFER);
}
if (FAILED(hr)) {
out_value->reset(nullptr);
}
return hr;
}
HRESULT BluetoothLowEnergyWrapper::WriteCharacteristicValue(
base::FilePath& service_path,
const PBTH_LE_GATT_CHARACTERISTIC characteristic,
PBTH_LE_GATT_CHARACTERISTIC_VALUE new_value) {
base::File file(service_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WRITE);
if (!file.IsValid())
return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
ULONG flag = BLUETOOTH_GATT_FLAG_NONE;
if (!characteristic->IsWritable) {
DCHECK(characteristic->IsWritableWithoutResponse);
flag |= BLUETOOTH_GATT_FLAG_WRITE_WITHOUT_RESPONSE;
}
return BluetoothGATTSetCharacteristicValue(
file.GetPlatformFile(), characteristic, new_value, NULL, flag);
}
HRESULT BluetoothLowEnergyWrapper::RegisterGattEvents(
base::FilePath& service_path,
BTH_LE_GATT_EVENT_TYPE event_type,
PVOID event_parameter,
PFNBLUETOOTH_GATT_EVENT_CALLBACK_CORRECTED callback,
PVOID context,
BLUETOOTH_GATT_EVENT_HANDLE* out_handle) {
base::File file(service_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!file.IsValid())
return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
// Cast to the official callback type for compatibility with the Windows
// 10.0.10586 definition, even though it is incorrect. This cast can be
// removed when we mandate building Chromium with the 10.0.14393 SDK or
// higher.
return BluetoothGATTRegisterEvent(
file.GetPlatformFile(), event_type, event_parameter,
reinterpret_cast<PFNBLUETOOTH_GATT_EVENT_CALLBACK>(callback), context,
out_handle, BLUETOOTH_GATT_FLAG_NONE);
}
HRESULT BluetoothLowEnergyWrapper::UnregisterGattEvent(
BLUETOOTH_GATT_EVENT_HANDLE event_handle) {
return BluetoothGATTUnregisterEvent(event_handle, BLUETOOTH_GATT_FLAG_NONE);
}
HRESULT BluetoothLowEnergyWrapper::WriteDescriptorValue(
base::FilePath& service_path,
const PBTH_LE_GATT_DESCRIPTOR descriptor,
PBTH_LE_GATT_DESCRIPTOR_VALUE new_value) {
base::File file(service_path, base::File::FLAG_OPEN | base::File::FLAG_READ |
base::File::FLAG_WRITE);
if (!file.IsValid())
return HRESULT_FROM_WIN32(ERROR_OPEN_FAILED);
return BluetoothGATTSetDescriptorValue(file.GetPlatformFile(), descriptor,
new_value, BLUETOOTH_GATT_FLAG_NONE);
}
} // namespace win
} // namespace device