Implement Ble GATT Client for Windows

PiperOrigin-RevId: 494033195
This commit is contained in:
Qin Wang
2022-12-08 17:06:52 -08:00
committed by Copybara-Service
parent 82b2c63a7c
commit cdd65616fc
4 changed files with 539 additions and 0 deletions
+25
View File
@@ -22,3 +22,28 @@ cc_library(
],
visibility = ["//:__subpackages__"],
)
cc_library(
name = "platform_windows",
srcs = [
"ble_gatt_client.cc",
],
hdrs = [
"ble_gatt_client.h",
],
copts = [
"-Ithird_party",
"-Ithird_party/nearby/internal/platform/implementation/windows/generated",
],
defines = ["_SILENCE_CLANG_COROUTINE_MESSAGE"],
visibility = ["//fastpair:__subpackages__"],
deps = [
"//internal/platform:base",
"//internal/platform:logging",
"//internal/platform/implementation/windows/generated:types",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/container:flat_hash_map",
"@com_google_absl//absl/status",
"@com_google_absl//absl/strings",
],
)
@@ -0,0 +1,375 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "fastpair/internal/impl/windows/ble_gatt_client.h"
// Standard C/C++ headers
#include <cstdint>
#include <exception>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
// Nearby headers
#include "absl/types/optional.h"
#include "internal/platform/implementation/windows/generated/winrt/impl/Windows.Devices.Bluetooth.0.h"
#include "internal/platform/implementation/windows/generated/winrt/impl/Windows.Devices.Bluetooth.GenericAttributeProfile.0.h"
#include "internal/platform/logging.h"
// WinRT headers
#include "internal/platform/uuid.h"
#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h"
#include "winrt/Windows.Devices.Bluetooth.h"
#include "winrt/Windows.Foundation.Collections.h"
#include "winrt/Windows.Foundation.h"
#include "winrt/Windows.Storage.Streams.h"
#include "winrt/base.h"
namespace location {
namespace nearby {
namespace windows {
namespace {
using ::winrt::Windows::Devices::Bluetooth::BluetoothLEDevice;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristicProperties;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristicsResult;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattClientCharacteristicConfigurationDescriptorValue;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCommunicationStatus;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDescriptor;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDescriptorsResult;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceService;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceServicesResult;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattOpenStatus;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattSharingMode;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattValueChangedEventArgs;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattWriteOption;
using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattWriteResult;
using ::winrt::Windows::Foundation::IAsyncOperation;
using ::winrt::Windows::Foundation::IReference;
using ::winrt::Windows::Foundation::Collections::IVectorView;
using ::winrt::Windows::Storage::Streams::DataReader;
using ::winrt::Windows::Storage::Streams::DataWriter;
using ::winrt::Windows::Storage::Streams::IBuffer;
std::string GattCommunicationStatusToString(GattCommunicationStatus status) {
switch (status) {
case GattCommunicationStatus::Success:
return "Success";
case GattCommunicationStatus::Unreachable:
return "Unreachable";
case GattCommunicationStatus::ProtocolError:
return "ProtocolError";
case GattCommunicationStatus::AccessDenied:
return "AccessDenied";
default:
return "Unknown";
}
}
std::string GattOpenStatusToString(GattOpenStatus status) {
switch (status) {
case GattOpenStatus::Unspecified:
return "Unspecified";
case GattOpenStatus::Success:
return "Success";
case GattOpenStatus::AlreadyOpened:
return "AlreadyOpened";
case GattOpenStatus::NotFound:
return "NotFound";
case GattOpenStatus::SharingViolation:
return "SharingViolation";
case GattOpenStatus::AccessDenied:
return "AccessDenied";
default:
return "Unknown";
}
}
template <typename IGattResult>
bool CheckCommunicationStatus(IGattResult& gatt_result,
bool allow_access_denied = false) {
if (!gatt_result) {
NEARBY_LOGS(VERBOSE) << "Getting GATT Results failed.";
return false;
}
GattCommunicationStatus status = gatt_result.Status();
NEARBY_LOGS(VERBOSE) << "GattCommunicationStatus: "
<< GattCommunicationStatusToString(status);
return status == GattCommunicationStatus::Success ||
(allow_access_denied &&
status == GattCommunicationStatus::AccessDenied);
}
template <typename T, typename I>
void GetAsVector(IVectorView<T>& view, std::vector<I>& vector) {
unsigned size = view.Size();
vector.reserve(size);
for (unsigned i = 0; i < size; ++i) {
I entry = view.GetAt(i);
vector.push_back(std::move(entry));
}
}
} // namespace
BleGattClient::BleGattClient(BluetoothLEDevice& ble_device)
: ble_device_(ble_device) {}
void BleGattClient::StartGattDiscovery() {
// Async Operation to get GattDeviceServices For Uuid
IAsyncOperation<GattDeviceServicesResult> get_gatt_services_op =
ble_device_.GetGattServicesAsync();
// Result for getting GattDeviceServices
GattDeviceServicesResult get_services_result = get_gatt_services_op.get();
OnGetGattServices(get_services_result);
service_discovered_ = true;
}
void BleGattClient::OnGetGattServices(
GattDeviceServicesResult& services_result) {
if (!CheckCommunicationStatus(services_result)) {
NEARBY_LOGS(VERBOSE) << "Failed to get GATT services.";
return;
}
NEARBY_LOGS(VERBOSE) << "Successfully get GATT services.";
IVectorView<GattDeviceService> services = services_result.Services();
GetAsVector(services, gatt_services_);
for (auto& gatt_service : gatt_services_) {
// Async Operation to open GattDeviceServices
IAsyncOperation<GattOpenStatus> open_service_op =
gatt_service.OpenAsync(GattSharingMode::SharedReadAndWrite);
GattOpenStatus open_status = open_service_op.get();
OnServiceOpen(gatt_service, gatt_service.AttributeHandle(), open_status);
}
}
void BleGattClient::OnServiceOpen(GattDeviceService& gatt_service,
uint16_t service_attribute_handle,
GattOpenStatus open_status) {
if (open_status != GattOpenStatus::Success &&
open_status != GattOpenStatus::AlreadyOpened) {
NEARBY_LOGS(VERBOSE) << "Ignoring failure to open service: "
<< service_attribute_handle << ": "
<< GattOpenStatusToString(open_status);
service_to_characteristics_map_.insert({service_attribute_handle, {}});
return;
}
NEARBY_LOGS(VERBOSE) << "Successfully open service: "
<< service_attribute_handle;
// Async Operation to get GattCharacteristics
IAsyncOperation<GattCharacteristicsResult> get_characteristics_op =
gatt_service.GetCharacteristicsAsync();
// Result for getting GattCharacteristics
GattCharacteristicsResult characteristics_result =
get_characteristics_op.get();
OnGetCharacteristics(service_attribute_handle, characteristics_result);
}
void BleGattClient::OnGetCharacteristics(
uint16_t service_attribute_handle,
GattCharacteristicsResult& characteristics_result) {
if (!CheckCommunicationStatus(characteristics_result,
/*allow_access_denied=*/true)) {
NEARBY_LOGS(VERBOSE) << "Failed to get characteristics for service:"
<< service_attribute_handle;
return;
}
NEARBY_LOGS(VERBOSE) << "Successfully get characteristics for service:"
<< service_attribute_handle;
IVectorView<GattCharacteristic> characteristics =
characteristics_result.Characteristics();
auto& characteristics_list =
service_to_characteristics_map_[service_attribute_handle];
GetAsVector(characteristics, characteristics_list);
}
BleGattClient::GattCharacteristicList& BleGattClient::GetCharacteristics(
std::string_view service_uuid) {
if (!service_discovered_) {
StartGattDiscovery();
}
uint16_t service_attribute_handle;
winrt::guid service_guid(service_uuid);
for (auto& gatt_service : gatt_services_) {
if (gatt_service.Uuid() == service_guid) {
service_attribute_handle = gatt_service.AttributeHandle();
break;
}
}
return service_to_characteristics_map_[service_attribute_handle];
}
absl::optional<GattCharacteristic> BleGattClient::GetCharacteristic(
std::string_view service_uuid, std::string_view characteristic_uuid) {
NEARBY_LOGS(VERBOSE) << "Getting characteristic: " << characteristic_uuid
<< " on service: " << service_uuid;
winrt::guid characteristic_guid(characteristic_uuid);
GattCharacteristicList characteristics = GetCharacteristics(service_uuid);
for (auto& characteristic : characteristics) {
if (characteristic.Uuid() == characteristic_guid) {
NEARBY_LOGS(VERBOSE) << "Characteristic " << characteristic_uuid
<< " found.";
return characteristic;
}
}
return absl::nullopt;
}
bool BleGattClient::WriteCharacteristic(std::string_view service_uuid,
std::string_view characteristic_uuid,
const ByteArray& value) {
absl::optional<GattCharacteristic> characteristic =
GetCharacteristic(service_uuid, characteristic_uuid);
if (characteristic.has_value()) {
return WriteCharacteristic(characteristic.value(), value);
}
return false;
}
bool BleGattClient::WriteCharacteristic(GattCharacteristic& characteristics,
const ByteArray& value) {
NEARBY_LOGS(VERBOSE) << "Writting " << value.size() << " bytes "
<< "on characteristics: "
<< characteristics.AttributeHandle();
DataWriter data_writer;
for (int i = 0; i < value.size(); ++i) {
data_writer.WriteByte(static_cast<uint8_t>(*(value.data() + i)));
}
IBuffer buffer = data_writer.DetachBuffer();
SubscribeToNotifications(characteristics);
IAsyncOperation<GattWriteResult> write_value_op =
characteristics.WriteValueWithResultAsync(
buffer, GattWriteOption::WriteWithResponse);
GattWriteResult result = write_value_op.get();
return OnWriteValueWithResultAndOption(result);
}
bool BleGattClient::OnWriteValueWithResultAndOption(
GattWriteResult& write_result) {
GattCommunicationStatus status = write_result.Status();
if (status == GattCommunicationStatus::Success) {
NEARBY_LOGS(VERBOSE) << "Successfully write characteristic.";
return true;
}
NEARBY_LOGS(VERBOSE) << "Error write characteristic with unexpected status: "
<< GattCommunicationStatusToString(status);
return false;
}
void BleGattClient::SubscribeToNotifications(
GattCharacteristic& characteristics) {
if (WriteCccDescriptor(
characteristics,
GetCccdValue(characteristics.CharacteristicProperties()))) {
AddValueChangedHandler(characteristics);
NEARBY_LOGS(VERBOSE) << "Successfully subscribe for value changes.";
} else {
NEARBY_LOGS(VERBOSE) << "Error registering for value changes.";
}
}
void BleGattClient::UnsubscribeFromNotifications(
GattCharacteristic& characteristics) {
if (WriteCccDescriptor(
characteristics,
GattClientCharacteristicConfigurationDescriptorValue::None)) {
RemoveValueChangedHandler(characteristics);
NEARBY_LOGS(VERBOSE) << "Successfully un-registered for notifications.";
} else {
NEARBY_LOGS(VERBOSE) << "Error un-registered for notifications.";
}
}
bool BleGattClient::WriteCccDescriptor(
GattCharacteristic& characteristics,
GattClientCharacteristicConfigurationDescriptorValue value) {
try {
IAsyncOperation<GattCommunicationStatus> write_cccdValue_op =
characteristics.WriteClientCharacteristicConfigurationDescriptorAsync(
value);
GattCommunicationStatus status = write_cccdValue_op.get();
if (status == GattCommunicationStatus::Success) {
NEARBY_LOGS(VERBOSE) << "Successfully write client characteristic "
"configuration descriptor";
return true;
} else {
NEARBY_LOGS(VERBOSE) << "Error write client characteristic configuration "
"descriptor: Status = "
<< GattCommunicationStatusToString(status);
return false;
}
} catch (std::exception exception) {
// This usually happens when a device reports that it support notify, but
// it actually doesn't.
NEARBY_LOGS(ERROR) << __func__
<< ": Exception to write client characteristic "
"configuration descriptor";
}
return false;
}
GattClientCharacteristicConfigurationDescriptorValue
BleGattClient::GetCccdValue(GattCharacteristicProperties properties) {
GattClientCharacteristicConfigurationDescriptorValue cccdValue =
GattClientCharacteristicConfigurationDescriptorValue::None;
if ((properties & GattCharacteristicProperties::Indicate) !=
GattCharacteristicProperties::None) {
cccdValue = GattClientCharacteristicConfigurationDescriptorValue::Indicate;
} else if ((properties & GattCharacteristicProperties::Notify) !=
GattCharacteristicProperties::None) {
cccdValue = GattClientCharacteristicConfigurationDescriptorValue::Notify;
}
return cccdValue;
}
void BleGattClient::AddValueChangedHandler(
GattCharacteristic& characteristics) {
if (!notifications_token_) {
NEARBY_LOGS(VERBOSE)
<< " notifications_token_ = characteristics.ValueChanged";
notifications_token_ =
characteristics.ValueChanged({this, &BleGattClient::OnValueChanged});
}
}
void BleGattClient::RemoveValueChangedHandler(
GattCharacteristic& characteristics) {
if (notifications_token_) {
characteristics.ValueChanged(std::exchange(notifications_token_, {}));
}
}
void BleGattClient::OnValueChanged(GattCharacteristic const& characteristic,
GattValueChangedEventArgs args) {
IBuffer value = args.CharacteristicValue();
NEARBY_LOGS(VERBOSE) << "onValueChange:";
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -0,0 +1,138 @@
// Copyright 2022 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_BLE_GATT_CLIENT_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_BLE_GATT_CLIENT_H_
#include <guiddef.h>
#include <functional>
#include <string>
#include <string_view>
#include <vector>
#include "absl/container/flat_hash_map.h"
#include "internal/platform/byte_array.h"
#include "winrt/Windows.Devices.Bluetooth.GenericAttributeProfile.h"
#include "winrt/Windows.Devices.Bluetooth.h"
namespace location {
namespace nearby {
namespace windows {
class BleGattClient {
public:
using GattServiceList =
std::vector<::winrt::Windows::Devices::Bluetooth::
GenericAttributeProfile::GattDeviceService>;
using GattCharacteristicList =
std::vector<::winrt::Windows::Devices::Bluetooth::
GenericAttributeProfile::GattCharacteristic>;
using GattDescriptorList =
std::vector<::winrt::Windows::Devices::Bluetooth::
GenericAttributeProfile::GattDescriptor>;
explicit BleGattClient(
::winrt::Windows::Devices::Bluetooth::BluetoothLEDevice& ble_device);
BleGattClient(const BleGattClient&) = delete;
BleGattClient& operator=(const BleGattClient&) = delete;
~BleGattClient() = default;
void StartGattDiscovery();
absl::optional<::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic>
GetCharacteristic(std::string_view service_uuid,
std::string_view characteristic_uuid);
GattCharacteristicList& GetCharacteristics(std::string_view service_uuid);
bool WriteCharacteristic(std::string_view service_uuid,
std::string_view characteristic_uuid,
const ByteArray& value);
bool WriteCharacteristic(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic& characteristics,
const ByteArray& value);
private:
void OnGetGattServices(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceServicesResult& services_result);
void OnServiceOpen(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattDeviceService& gatt_service,
uint16_t service_attribute_handle,
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattOpenStatus open_status);
void OnGetCharacteristics(
uint16_t service_attribute_handle,
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristicsResult& characteristics_result);
bool OnWriteValueWithResultAndOption(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattWriteResult& write_result);
void SubscribeToNotifications(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic& characteristics);
void UnsubscribeFromNotifications(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic& characteristics);
bool WriteCccDescriptor(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic& characteristics,
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattClientCharacteristicConfigurationDescriptorValue value);
void AddValueChangedHandler(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic& characteristics);
void RemoveValueChangedHandler(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic& characteristics);
void OnValueChanged(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristic const& characteristic,
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattValueChangedEventArgs args);
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattClientCharacteristicConfigurationDescriptorValue
GetCccdValue(
::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile::
GattCharacteristicProperties properties);
::winrt::Windows::Devices::Bluetooth::BluetoothLEDevice ble_device_;
::winrt::event_token notifications_token_;
bool service_discovered_ = false;
GattServiceList gatt_services_;
absl::flat_hash_map<uint16_t, GattCharacteristicList>
service_to_characteristics_map_;
};
} // namespace windows
} // namespace nearby
} // namespace location
#endif // THIRD_PARTY_NEARBY_FASTPAIR_INTERNAL_IMPL_WINDOWS_BLE_GATT_CLIENT_H_
@@ -38,6 +38,7 @@ cc_library(
textual_hdrs = glob(["**/*.h"]),
visibility = [
"//connections/windows:__subpackages__",
"//fastpair/internal:__subpackages__",
"//internal:__subpackages__",
"//internal/platform/implementation/windows:__subpackages__",
],