blob: a53422c2d600cc332121b03f682a8c5442f899c0 [file] [log] [blame]
// Copyright 2018 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_gatt_discoverer_winrt.h"
#include <windows.foundation.collections.h>
#include <utility>
#include "base/bind.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/win/post_async_results.h"
#include "device/bluetooth/bluetooth_remote_gatt_service_winrt.h"
namespace device {
namespace {
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice;
using ABI::Windows::Devices::Bluetooth::IBluetoothLEDevice3;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristicsResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus_AccessDenied;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus_Success;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::GattDescriptor;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDescriptorsResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceService;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceServicesResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattCharacteristic3;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattCharacteristicsResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDescriptorsResult;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceService3;
using ABI::Windows::Devices::Bluetooth::GenericAttributeProfile::
IGattDeviceServicesResult;
using ABI::Windows::Foundation::IAsyncOperation;
using ABI::Windows::Foundation::IReference;
using ABI::Windows::Foundation::Collections::IVectorView;
using Microsoft::WRL::ComPtr;
template <typename IGattResult>
bool CheckCommunicationStatus(IGattResult* gatt_result,
bool allow_access_denied = false) {
if (!gatt_result) {
VLOG(2) << "Getting GATT Results failed.";
return false;
}
GattCommunicationStatus status;
HRESULT hr = gatt_result->get_Status(&status);
if (FAILED(hr)) {
VLOG(2) << "Getting GATT Communication Status failed: "
<< logging::SystemErrorCodeToString(hr);
return false;
}
if (status != GattCommunicationStatus_Success) {
if (status == GattCommunicationStatus_AccessDenied) {
VLOG(2) << "GATT access denied error";
} else {
VLOG(2) << "Unexpected GattCommunicationStatus: " << status;
}
VLOG(2) << "GATT Error Code: "
<< static_cast<int>(
BluetoothRemoteGattServiceWinrt::GetGattErrorCode(
gatt_result));
}
return status == GattCommunicationStatus_Success ||
(allow_access_denied &&
status == GattCommunicationStatus_AccessDenied);
}
template <typename T, typename I>
bool GetAsVector(IVectorView<T*>* view, std::vector<ComPtr<I>>* vector) {
unsigned size;
HRESULT hr = view->get_Size(&size);
if (FAILED(hr)) {
VLOG(2) << "Getting Size failed: " << logging::SystemErrorCodeToString(hr);
return false;
}
vector->reserve(size);
for (unsigned i = 0; i < size; ++i) {
ComPtr<I> entry;
hr = view->GetAt(i, &entry);
if (FAILED(hr)) {
VLOG(2) << "GetAt(" << i
<< ") failed: " << logging::SystemErrorCodeToString(hr);
return false;
}
vector->push_back(std::move(entry));
}
return true;
}
} // namespace
BluetoothGattDiscovererWinrt::BluetoothGattDiscovererWinrt(
ComPtr<IBluetoothLEDevice> ble_device)
: ble_device_(std::move(ble_device)), weak_ptr_factory_(this) {}
BluetoothGattDiscovererWinrt::~BluetoothGattDiscovererWinrt() = default;
void BluetoothGattDiscovererWinrt::StartGattDiscovery(
GattDiscoveryCallback callback) {
callback_ = std::move(callback);
ComPtr<IBluetoothLEDevice3> ble_device_3;
HRESULT hr = ble_device_.As(&ble_device_3);
if (FAILED(hr)) {
VLOG(2) << "Obtaining IBluetoothLEDevice3 failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
ComPtr<IAsyncOperation<GattDeviceServicesResult*>> get_gatt_services_op;
hr = ble_device_3->GetGattServicesAsync(&get_gatt_services_op);
if (FAILED(hr)) {
VLOG(2) << "BluetoothLEDevice::GetGattServicesAsync failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
hr = base::win::PostAsyncResults(
std::move(get_gatt_services_op),
base::BindOnce(&BluetoothGattDiscovererWinrt::OnGetGattServices,
weak_ptr_factory_.GetWeakPtr()));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
}
}
const BluetoothGattDiscovererWinrt::GattServiceList&
BluetoothGattDiscovererWinrt::GetGattServices() const {
return gatt_services_;
}
const BluetoothGattDiscovererWinrt::GattCharacteristicList*
BluetoothGattDiscovererWinrt::GetCharacteristics(
uint16_t service_attribute_handle) const {
auto iter = service_to_characteristics_map_.find(service_attribute_handle);
return iter != service_to_characteristics_map_.end() ? &iter->second
: nullptr;
}
const BluetoothGattDiscovererWinrt::GattDescriptorList*
BluetoothGattDiscovererWinrt::GetDescriptors(
uint16_t characteristic_attribute_handle) const {
auto iter =
characteristic_to_descriptors_map_.find(characteristic_attribute_handle);
return iter != characteristic_to_descriptors_map_.end() ? &iter->second
: nullptr;
}
void BluetoothGattDiscovererWinrt::OnGetGattServices(
ComPtr<IGattDeviceServicesResult> services_result) {
if (!CheckCommunicationStatus(services_result.Get())) {
std::move(callback_).Run(false);
return;
}
ComPtr<IVectorView<GattDeviceService*>> services;
HRESULT hr = services_result->get_Services(&services);
if (FAILED(hr)) {
VLOG(2) << "Getting GATT Services failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
if (!GetAsVector(services.Get(), &gatt_services_)) {
std::move(callback_).Run(false);
return;
}
num_services_ = gatt_services_.size();
for (const auto& gatt_service : gatt_services_) {
uint16_t service_attribute_handle;
hr = gatt_service->get_AttributeHandle(&service_attribute_handle);
if (FAILED(hr)) {
VLOG(2) << "Getting AttributeHandle failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
ComPtr<IGattDeviceService3> gatt_service_3;
hr = gatt_service.As(&gatt_service_3);
if (FAILED(hr)) {
VLOG(2) << "Obtaining IGattDeviceService3 failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
ComPtr<IAsyncOperation<GattCharacteristicsResult*>> get_characteristics_op;
hr = gatt_service_3->GetCharacteristicsAsync(&get_characteristics_op);
if (FAILED(hr)) {
VLOG(2) << "GattDeviceService::GetCharacteristicsAsync() failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
hr = base::win::PostAsyncResults(
std::move(get_characteristics_op),
base::BindOnce(&BluetoothGattDiscovererWinrt::OnGetCharacteristics,
weak_ptr_factory_.GetWeakPtr(),
service_attribute_handle));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
}
}
RunCallbackIfDone();
}
void BluetoothGattDiscovererWinrt::OnGetCharacteristics(
uint16_t service_attribute_handle,
ComPtr<IGattCharacteristicsResult> characteristics_result) {
// A few GATT services like HID over GATT (short UUID 0x1812) are protected
// by the OS, leading to an access denied error.
if (!CheckCommunicationStatus(characteristics_result.Get(), true)) {
std::move(callback_).Run(false);
return;
}
ComPtr<IVectorView<GattCharacteristic*>> characteristics;
HRESULT hr = characteristics_result->get_Characteristics(&characteristics);
if (FAILED(hr)) {
VLOG(2) << "Getting Characteristics failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
DCHECK(!base::ContainsKey(service_to_characteristics_map_,
service_attribute_handle));
auto& characteristics_list =
service_to_characteristics_map_[service_attribute_handle];
if (!GetAsVector(characteristics.Get(), &characteristics_list)) {
std::move(callback_).Run(false);
return;
}
num_characteristics_ += characteristics_list.size();
for (const auto& gatt_characteristic : characteristics_list) {
uint16_t characteristic_attribute_handle;
hr = gatt_characteristic->get_AttributeHandle(
&characteristic_attribute_handle);
if (FAILED(hr)) {
VLOG(2) << "Getting AttributeHandle failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
ComPtr<IGattCharacteristic3> gatt_characteristic_3;
hr = gatt_characteristic.As(&gatt_characteristic_3);
if (FAILED(hr)) {
VLOG(2) << "Obtaining IGattCharacteristic3 failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
ComPtr<IAsyncOperation<GattDescriptorsResult*>> get_descriptors_op;
hr = gatt_characteristic_3->GetDescriptorsAsync(&get_descriptors_op);
if (FAILED(hr)) {
VLOG(2) << "GattCharacteristic::GetDescriptorsAsync() failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
hr = base::win::PostAsyncResults(
std::move(get_descriptors_op),
base::BindOnce(&BluetoothGattDiscovererWinrt::OnGetDescriptors,
weak_ptr_factory_.GetWeakPtr(),
characteristic_attribute_handle));
if (FAILED(hr)) {
VLOG(2) << "PostAsyncResults failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
}
}
RunCallbackIfDone();
}
void BluetoothGattDiscovererWinrt::OnGetDescriptors(
uint16_t characteristic_attribute_handle,
ComPtr<IGattDescriptorsResult> descriptors_result) {
if (!CheckCommunicationStatus(descriptors_result.Get())) {
std::move(callback_).Run(false);
return;
}
ComPtr<IVectorView<GattDescriptor*>> descriptors;
HRESULT hr = descriptors_result->get_Descriptors(&descriptors);
if (FAILED(hr)) {
VLOG(2) << "Getting Descriptors failed: "
<< logging::SystemErrorCodeToString(hr);
std::move(callback_).Run(false);
return;
}
DCHECK(!base::ContainsKey(characteristic_to_descriptors_map_,
characteristic_attribute_handle));
if (!GetAsVector(descriptors.Get(), &characteristic_to_descriptors_map_
[characteristic_attribute_handle])) {
std::move(callback_).Run(false);
return;
}
RunCallbackIfDone();
}
void BluetoothGattDiscovererWinrt::RunCallbackIfDone() {
DCHECK(callback_);
if (service_to_characteristics_map_.size() == num_services_ &&
characteristic_to_descriptors_map_.size() == num_characteristics_) {
std::move(callback_).Run(true);
}
}
} // namespace device