diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 7a2029de..2d95ca45 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -57,6 +57,7 @@ cc_library( hdrs = [ "ble.h", "ble_gatt_client.h", + "ble_gatt_server.h", "ble_medium.h", "ble_peripheral.h", "ble_socket.h", @@ -133,6 +134,7 @@ cc_library( name = "windows", srcs = [ "ble_gatt_client.cc", + "ble_gatt_server.cc", "ble_medium.cc", "ble_socket.cc", "ble_v2.cc", diff --git a/internal/platform/implementation/windows/ble_gatt_server.cc b/internal/platform/implementation/windows/ble_gatt_server.cc new file mode 100644 index 00000000..f1434900 --- /dev/null +++ b/internal/platform/implementation/windows/ble_gatt_server.cc @@ -0,0 +1,639 @@ +// Copyright 2023 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 "internal/platform/implementation/windows/ble_gatt_server.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/log/check.h" +#include "absl/strings/escaping.h" +#include "absl/strings/str_format.h" +#include "absl/types/optional.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/windows/utils.h" +#include "internal/platform/logging.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Storage.Streams.h" +#include "winrt/base.h" + +namespace nearby { +namespace windows { +namespace { + +using ::winrt::Windows::Devices::Bluetooth::BluetoothError; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattCharacteristicProperties; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattClientNotificationResult; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattCommunicationStatus; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristicParameters; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristicResult; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattProtectionLevel; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattReadRequest; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattReadRequestedEventArgs; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProvider; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProviderAdvertisementStatus; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProviderAdvertisementStatusChangedEventArgs; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProviderAdvertisingParameters; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProviderResult; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattSubscribedClient; +using ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattWriteRequestedEventArgs; +using ::winrt::Windows::Foundation::Collections::IVectorView; +using ::winrt::Windows::Storage::Streams::Buffer; +using ::winrt::Windows::Storage::Streams::DataWriter; + +std::string ConvertGattStatusToString( + GattServiceProviderAdvertisementStatus status) { + switch (status) { + case GattServiceProviderAdvertisementStatus::Aborted: + return "Aborted"; + case GattServiceProviderAdvertisementStatus:: + StartedWithoutAllAdvertisementData: + return "StartedWithoutAllAdvertisementData"; + case GattServiceProviderAdvertisementStatus::Started: + return "Started"; + case GattServiceProviderAdvertisementStatus::Created: + return "Created"; + case GattServiceProviderAdvertisementStatus::Stopped: + return "Stopped"; + default: + return "Unknown"; + } +} + +} // namespace + +BleGattServer::BleGattServer(api::BluetoothAdapter* adapter, + api::ble_v2::ServerGattConnectionCallback callback) + : adapter_(dynamic_cast(adapter)) { + DCHECK_NE(adapter_, nullptr); + gatt_connection_callback_ = std::move(callback); +} + +absl::optional +BleGattServer::CreateCharacteristic( + const Uuid& service_uuid, const Uuid& characteristic_uuid, + const std::vector& permissions, + const std::vector& properties) { + NEARBY_LOGS(VERBOSE) << __func__ << ": create characteristic, service_uuid: " + << std::string(service_uuid) << ", characteristic_uuid: " + << std::string(characteristic_uuid); + + if (!service_uuid_.IsEmpty() && service_uuid_ != service_uuid) { + NEARBY_LOGS(ERROR) << __func__ + << ": Only support one GATT service for now."; + return absl::nullopt; + } + + service_uuid_ = service_uuid; + + api::ble_v2::GattCharacteristic gatt_characteristic; + gatt_characteristic.uuid = characteristic_uuid; + gatt_characteristic.service_uuid = service_uuid; + gatt_characteristic.permissions = permissions; + gatt_characteristic.properties = properties; + + GattCharacteristicData gatt_characteristic_data; + gatt_characteristic_data.gatt_characteristic = gatt_characteristic; + + gatt_characteristic_datas_.push_back(gatt_characteristic_data); + + return gatt_characteristic; +} + +bool BleGattServer::UpdateCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic, + const nearby::ByteArray& value) { + NEARBY_LOGS(VERBOSE) << __func__ << ": update characteristic: " + << std::string(characteristic.uuid); + + if (characteristic.service_uuid != service_uuid_) { + NEARBY_LOGS(ERROR) << __func__ << ": Cannot found the GATT service."; + return false; + } + + for (auto& it : gatt_characteristic_datas_) { + if (it.gatt_characteristic.uuid == characteristic.uuid) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": Found the characteristic to update."; + it.data = value; + + // If it is in running, notify the value changed. + if (is_advertising_) { + // Make sure the character has indication property. + bool is_indicate_characteristic = false; + for (const auto property : it.gatt_characteristic.properties) { + if (property == + api::ble_v2::GattCharacteristic::Property::kIndicate) { + is_indicate_characteristic = true; + break; + } + } + + NEARBY_LOGS(INFO) << __func__ + << ": Notify characteristic value updated."; + if (is_indicate_characteristic) { + NotifyValueChanged(it.gatt_characteristic); + } + } + + return true; + } + } + + NEARBY_LOGS(ERROR) << __func__ << ": Failed to update the characteristic."; + + return false; +} + +void BleGattServer::Stop() { + NEARBY_LOGS(VERBOSE) << __func__ << ": Start to stop GATT server."; + try { + if (gatt_service_provider_ == nullptr) { + NEARBY_LOGS(WARNING) << __func__ << ": GATT server already stopped."; + return; + } + + gatt_service_provider_.StopAdvertising(); + gatt_characteristic_datas_.clear(); + service_uuid_ = Uuid(); + gatt_service_provider_ = nullptr; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } +} + +bool BleGattServer::InitializeGattServer() { + try { + // Create and advertise GATT service. + NEARBY_LOGS(VERBOSE) << __func__ << ": Create GATT service service_uuid=" + << std::string(service_uuid_); + + if (adapter_ == nullptr || !adapter_->IsEnabled()) { + NEARBY_LOGS(ERROR) << __func__ << ": Bluetooth adapter is disabled."; + return false; + } + + winrt::guid service_uuid = nearby_uuid_to_winrt_guid(service_uuid_); + GattServiceProviderResult service_provider_result = + GattServiceProvider::CreateAsync(service_uuid).get(); + + if (service_provider_result.Error() != BluetoothError::Success) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to create GATT service. Error: " + << static_cast(service_provider_result.Error()); + return false; + } + + gatt_service_provider_ = service_provider_result.ServiceProvider(); + + service_provider_advertisement_changed_token_ = + gatt_service_provider_.AdvertisementStatusChanged( + {this, &BleGattServer::ServiceProvider_AdvertisementStatusChanged}); + NEARBY_LOGS(INFO) << __func__ << ": GATT service created."; + + // Create GATT characteristics. + for (auto& characteristic_data : gatt_characteristic_datas_) { + bool is_read_supported = false; + bool is_write_supported = false; + bool is_indicate_supported = false; + GattLocalCharacteristicParameters gatt_characteristic_parameters; + + // Set GATT properties. + GattCharacteristicProperties properties = + GattCharacteristicProperties::None; + for (const auto& property : + characteristic_data.gatt_characteristic.properties) { + if (property == api::ble_v2::GattCharacteristic::Property::kRead) { + properties |= GattCharacteristicProperties::Read; + is_read_supported = true; + } else if (property == + api::ble_v2::GattCharacteristic::Property::kWrite) { + properties |= GattCharacteristicProperties::Write; + is_write_supported = true; + } else if (property == + api::ble_v2::GattCharacteristic::Property::kIndicate) { + properties |= GattCharacteristicProperties::Indicate; + is_indicate_supported = true; + } + } + + NEARBY_LOGS(VERBOSE) << __func__ + << ": GATT characteristic properties: read=" + << is_read_supported + << ",write=" << is_write_supported + << ",indicate=" << is_indicate_supported; + + gatt_characteristic_parameters.CharacteristicProperties(properties); + gatt_characteristic_parameters.WriteProtectionLevel( + GattProtectionLevel::Plain); + + winrt::guid characteristic_uuid = nearby_uuid_to_winrt_guid( + characteristic_data.gatt_characteristic.uuid); + + NEARBY_LOGS(VERBOSE) << __func__ + << ": Create characteristic characteristic_uuid=" + << winrt::to_string( + winrt::to_hstring(characteristic_uuid)); + + GattLocalCharacteristicResult result = + gatt_service_provider_.Service() + .CreateCharacteristicAsync(characteristic_uuid, + gatt_characteristic_parameters) + .get(); + + if (result.Error() != BluetoothError::Success) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to create GATT characteristic. Error: " + << static_cast(result.Error()); + return false; + } + + characteristic_data.local_characteristic = result.Characteristic(); + + ::winrt::guid local_characteristic_guid = + characteristic_data.local_characteristic.Uuid(); + NEARBY_LOGS(VERBOSE) << __func__ << ": Local GATT characteristic. uuid: " + << winrt::to_string( + winrt::to_hstring(local_characteristic_guid)); + + // Setup gatt local characteristic events. + if (is_read_supported) { + characteristic_data.read_token = + characteristic_data.local_characteristic.ReadRequested( + {this, &BleGattServer::Characteristic_ReadRequestedAsync}); + } + + if (is_write_supported) { + characteristic_data.write_token = + characteristic_data.local_characteristic.WriteRequested( + {this, &BleGattServer::Characteristic_WriteRequestedAsync}); + } + + if (is_indicate_supported) { + characteristic_data.subscribed_clients_changed_token = + characteristic_data.local_characteristic.SubscribedClientsChanged( + {this, + &BleGattServer::Characteristic_SubscribedClientsChanged}); + } + } + + is_gatt_server_inited_ = true; + + NEARBY_LOGS(INFO) << __func__ << ": GATT service is initalized."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } + + // Clean up. + if (gatt_service_provider_ != nullptr) { + gatt_service_provider_ = nullptr; + } + + is_gatt_server_inited_ = false; + return false; +} + +bool BleGattServer::StartAdvertisement(const ByteArray& service_data, + bool is_connectable) { + try { + NEARBY_LOGS(VERBOSE) << __func__ << ": service_data=" + << absl::BytesToHexString(service_data.AsStringView()) + << ", is_connectable=" << is_connectable; + + if (is_advertising_) { + NEARBY_LOGS(ERROR) << ": GATT server is already in advertising."; + return false; + } + + is_advertising_ = true; + + if (!is_gatt_server_inited_ && !InitializeGattServer()) { + NEARBY_LOGS(ERROR) << ":Failed to initalize GATT service."; + is_advertising_ = false; + return false; + } + + // Start the GATT server advertising + GattServiceProviderAdvertisingParameters advertisement_parameters; + advertisement_parameters.IsConnectable(is_connectable); + advertisement_parameters.IsDiscoverable(true); + + DataWriter data_writer; + for (int i = 0; i < service_data.size(); ++i) { + data_writer.WriteByte(static_cast(*(service_data.data() + i))); + } + + advertisement_parameters.ServiceData(data_writer.DetachBuffer()); + + gatt_service_provider_.StartAdvertising(advertisement_parameters); + NEARBY_LOGS(INFO) << __func__ << ": GATT server started."; + + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } + + is_advertising_ = false; + NEARBY_LOGS(ERROR) << __func__ << ": Failed to advertise GATT server."; + return false; +} + +bool BleGattServer::StopAdvertisement() { + try { + NEARBY_LOGS(INFO) << __func__ << ": stop advertisement."; + + if (!is_advertising_) { + NEARBY_LOGS(WARNING) << __func__ << ": no GATT advertisement."; + return true; + } + + if (gatt_service_provider_ == nullptr) { + NEARBY_LOGS(WARNING) << __func__ << ": no GATT server is running."; + return true; + } + + gatt_service_provider_.StopAdvertising(); + NEARBY_LOGS(INFO) << __func__ << ": GATT server stopped."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } + + return false; +} + +::winrt::fire_and_forget BleGattServer::Characteristic_ReadRequestedAsync( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic const& gatt_local_characteristic, + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattReadRequestedEventArgs args) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Read characteristic. uuid: " + << winrt::to_string(winrt::to_hstring( + gatt_local_characteristic.Uuid())); + + auto deferral = args.GetDeferral(); + + try { + // Find the characteristic value. + GattCharacteristicData* characteristic_data = + FindGattCharacteristicData(gatt_local_characteristic); + + if (characteristic_data == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to find characteristic=" + << ::winrt::to_string(::winrt::to_hstring( + gatt_local_characteristic.Uuid())); + return {}; + } + + GattReadRequest request = args.GetRequestAsync().get(); + if (request == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to get GATT read request."; + deferral.Complete(); + return {}; + } + + const ByteArray& data = characteristic_data->data; + + Buffer buffer = Buffer(data.size()); + std::memcpy(buffer.data(), data.data(), data.size()); + buffer.Length(data.size()); + request.RespondWithValue(buffer); + deferral.Complete(); + + NEARBY_LOGS(VERBOSE) << __func__ << ": Sent data to remote device."; + return {}; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } + + deferral.Complete(); + + NEARBY_LOGS(ERROR) << __func__ << ": Failed to send data to remote device."; + return {}; +} + +::winrt::fire_and_forget BleGattServer::Characteristic_WriteRequestedAsync( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic const& gatt_local_characteristic, + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattWriteRequestedEventArgs args) { + // In Nearby Connctions, don't support write charaterisctics right now. + throw std::logic_error("Not implemented."); +} + +void BleGattServer::Characteristic_SubscribedClientsChanged( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic const& gatt_local_characteristic, + ::winrt::Windows::Foundation::IInspectable const& args) { + NEARBY_LOGS(VERBOSE) << __func__ + << ": Subscribed clients changed. characteristic=" + << ::winrt::to_string(::winrt::to_hstring( + gatt_local_characteristic.Uuid())); + + try { + std::vector + added_subscribed_characteristics; + std::vector + removed_subscribed_characteristics; + + GattCharacteristicData* characteristic_data = + FindGattCharacteristicData(gatt_local_characteristic); + + if (characteristic_data == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to find characteristic=" + << ::winrt::to_string(::winrt::to_hstring( + gatt_local_characteristic.Uuid())); + return; + } + + auto current_subscribed_clients = + gatt_local_characteristic.SubscribedClients(); + + for (const auto& current_subscribed_client : current_subscribed_clients) { + bool found = false; + for (const auto& old_subscribed_client : + characteristic_data->subscribed_clients) { + if (current_subscribed_client.Session().DeviceId().Id() == + old_subscribed_client.Session().DeviceId().Id()) { + found = true; + break; + } + } + if (!found) { + // This is a new subscribed client. + added_subscribed_characteristics.push_back( + characteristic_data->gatt_characteristic); + } + } + + for (const auto& old_subscribed_client : + characteristic_data->subscribed_clients) { + bool found = false; + for (const auto& current_subscribed_client : current_subscribed_clients) { + if (current_subscribed_client.Session().DeviceId().Id() == + old_subscribed_client.Session().DeviceId().Id()) { + found = true; + break; + } + } + if (!found) { + // This is subscribed client to remove. + removed_subscribed_characteristics.push_back( + characteristic_data->gatt_characteristic); + } + } + + characteristic_data->subscribed_clients = current_subscribed_clients; + for (const auto& subscribed_characteristic : + added_subscribed_characteristics) { + gatt_connection_callback_.characteristic_subscription_cb( + subscribed_characteristic); + + NotifyValueChanged(subscribed_characteristic); + } + + for (const auto& subscribed_characteristic : + added_subscribed_characteristics) { + gatt_connection_callback_.characteristic_unsubscription_cb( + subscribed_characteristic); + } + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } +} + +void BleGattServer::ServiceProvider_AdvertisementStatusChanged( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProvider const& sender, + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProviderAdvertisementStatusChangedEventArgs const& args) { + NEARBY_LOGS(VERBOSE) << __func__ << ": Advertisement status changed. status=" + << ConvertGattStatusToString(args.Status()); +} + +void BleGattServer::NotifyValueChanged( + const api::ble_v2::GattCharacteristic& gatt_characteristic) { + try { + GattCharacteristicData* characteristic_data = + FindGattCharacteristicData(gatt_characteristic); + + if (characteristic_data == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to find characteristic=" + << std::string(gatt_characteristic.uuid); + return; + } + + if (characteristic_data->local_characteristic == nullptr) { + return; + } + + const ByteArray& data = characteristic_data->data; + + Buffer buffer = Buffer(data.size()); + std::memcpy(buffer.data(), data.data(), data.size()); + buffer.Length(data.size()); + + IVectorView results = + characteristic_data->local_characteristic.NotifyValueAsync(buffer) + .get(); + + for (const auto& result : results) { + if (result.Status() != GattCommunicationStatus::Success) { + NEARBY_LOGS(ERROR) + << __func__ << ": Failed to notify value change. remote device id=" + << ::winrt::to_string( + result.SubscribedClient().Session().DeviceId().Id()); + } + } + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ << ": Exception: " << exception.what(); + } catch (const winrt::hresult_error& error) { + NEARBY_LOGS(ERROR) << __func__ << ": WinRT exception: " << error.code() + << ": " << winrt::to_string(error.message()); + } +} + +BleGattServer::GattCharacteristicData* +BleGattServer::FindGattCharacteristicData( + const GattLocalCharacteristic& gatt_local_characteristic) { + for (auto& characteristic_data : gatt_characteristic_datas_) { + if (gatt_local_characteristic.Uuid() == + characteristic_data.local_characteristic.Uuid()) { + return &characteristic_data; + } + } + + return nullptr; +} + +BleGattServer::GattCharacteristicData* +BleGattServer::FindGattCharacteristicData( + const api::ble_v2::GattCharacteristic& gatt_characteristic) { + for (auto& characteristic_data : gatt_characteristic_datas_) { + if (gatt_characteristic.uuid == + characteristic_data.gatt_characteristic.uuid) { + return &characteristic_data; + } + } + + return nullptr; +} + +} // namespace windows +} // namespace nearby diff --git a/internal/platform/implementation/windows/ble_gatt_server.h b/internal/platform/implementation/windows/ble_gatt_server.h new file mode 100644 index 00000000..ab98f448 --- /dev/null +++ b/internal/platform/implementation/windows/ble_gatt_server.h @@ -0,0 +1,123 @@ +// Copyright 2023 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_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_GATT_SERVER_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_GATT_SERVER_H_ + +#include + +#include +#include + +#include "absl/container/flat_hash_map.h" +#include "absl/strings/string_view.h" +#include "internal/platform/byte_array.h" +#include "internal/platform/implementation/ble_v2.h" +#include "internal/platform/implementation/windows/bluetooth_adapter.h" +#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/base.h" + +namespace nearby { +namespace windows { + +class BleGattServer : public api::ble_v2::GattServer { + public: + BleGattServer(api::BluetoothAdapter* adapter, + api::ble_v2::ServerGattConnectionCallback callback); + ~BleGattServer() override = default; + absl::optional CreateCharacteristic( + const Uuid& service_uuid, const Uuid& characteristic_uuid, + const std::vector& + permissions, + const std::vector& properties) + override; + + bool UpdateCharacteristic( + const api::ble_v2::GattCharacteristic& characteristic, + const nearby::ByteArray& value) override; + + void Stop() override; + + bool StartAdvertisement(const ByteArray& service_data, bool is_connectable); + bool StopAdvertisement(); + + private: + // Used to save native data related to the GATT characteristic. + struct GattCharacteristicData { + api::ble_v2::GattCharacteristic gatt_characteristic; + ByteArray data; + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic local_characteristic = nullptr; + ::winrt::Windows::Foundation::Collections::IVectorView< + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattSubscribedClient> + subscribed_clients; + ::winrt::event_token read_token{}; + ::winrt::event_token write_token{}; + ::winrt::event_token subscribed_clients_changed_token{}; + }; + + bool InitializeGattServer(); + void NotifyValueChanged( + const api::ble_v2::GattCharacteristic& gatt_characteristic); + + GattCharacteristicData* FindGattCharacteristicData( + const ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic& gatt_local_characteristic); + GattCharacteristicData* FindGattCharacteristicData( + const api::ble_v2::GattCharacteristic& gatt_characteristic); + + ::winrt::fire_and_forget Characteristic_ReadRequestedAsync( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic const& gatt_local_characteristic, + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattReadRequestedEventArgs args); + ::winrt::fire_and_forget Characteristic_WriteRequestedAsync( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic const& gatt_local_characteristic, + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattWriteRequestedEventArgs args); + void Characteristic_SubscribedClientsChanged( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattLocalCharacteristic const& gatt_local_characteristic, + ::winrt::Windows::Foundation::IInspectable const& args); + + void ServiceProvider_AdvertisementStatusChanged( + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProvider const& sender, + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProviderAdvertisementStatusChangedEventArgs const& args); + + BluetoothAdapter* adapter_ = nullptr; + + ::winrt::Windows::Devices::Bluetooth::GenericAttributeProfile:: + GattServiceProvider gatt_service_provider_ = nullptr; + + Uuid service_uuid_; + std::vector gatt_characteristic_datas_; + + api::ble_v2::ServerGattConnectionCallback gatt_connection_callback_{}; + + ::winrt::event_token service_provider_advertisement_changed_token_{}; + bool is_advertising_ = false; + bool is_gatt_server_inited_ = false; +}; + +} // namespace windows +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_GATT_SERVER_H_