mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 22:56:12 -04:00
Added initial BLE GATT server implementation for BLE v2 support
PiperOrigin-RevId: 512666999
This commit is contained in:
committed by
Copybara-Service
parent
96327865cd
commit
843a4c1a35
@@ -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",
|
||||
|
||||
@@ -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 <cstddef>
|
||||
#include <cstring>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<BluetoothAdapter*>(adapter)) {
|
||||
DCHECK_NE(adapter_, nullptr);
|
||||
gatt_connection_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
absl::optional<api::ble_v2::GattCharacteristic>
|
||||
BleGattServer::CreateCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid,
|
||||
const std::vector<api::ble_v2::GattCharacteristic::Permission>& permissions,
|
||||
const std::vector<api::ble_v2::GattCharacteristic::Property>& 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<int>(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<int>(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<uint8_t>(*(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<api::ble_v2::GattCharacteristic>
|
||||
added_subscribed_characteristics;
|
||||
std::vector<api::ble_v2::GattCharacteristic>
|
||||
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<GattClientNotificationResult> 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
|
||||
@@ -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 <windows.h>
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#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<api::ble_v2::GattCharacteristic> CreateCharacteristic(
|
||||
const Uuid& service_uuid, const Uuid& characteristic_uuid,
|
||||
const std::vector<api::ble_v2::GattCharacteristic::Permission>&
|
||||
permissions,
|
||||
const std::vector<api::ble_v2::GattCharacteristic::Property>& 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<GattCharacteristicData> 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_
|
||||
Reference in New Issue
Block a user