Initial implementation of BluetoothServerSocket

PiperOrigin-RevId: 390461649
This commit is contained in:
jfcarroll
2021-08-12 14:33:26 -07:00
committed by Copybara-Service
parent 512ba2af43
commit 07d33cfa9e
6 changed files with 300 additions and 63 deletions
@@ -14,18 +14,23 @@
#include "platform/impl/windows/bluetooth_classic_medium.h"
#include <codecvt>
#include <locale>
#include <string>
#include "platform/base/cancellation_flag.h"
#include "platform/base/cancellation_flag_listener.h"
#include "platform/base/exception.h"
#include "platform/impl/windows/bluetooth_classic_device.h"
#include "platform/impl/windows/bluetooth_classic_server_socket.h"
#include "platform/impl/windows/bluetooth_classic_socket.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "platform/impl/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "platform/impl/windows/generated/winrt/base.h"
#include "platform/base/exception.h"
#include "platform/impl/windows/bluetooth_classic_device.h"
#include "platform/impl/windows/bluetooth_classic_socket.h"
#include "platform/impl/windows/utils.h"
#include "platform/public/logging.h"
#include "platform/base/cancellation_flag.h"
#include "platform/base/cancellation_flag_listener.h"
namespace location {
namespace nearby {
@@ -34,32 +39,7 @@ namespace windows {
BluetoothClassicMedium::BluetoothClassicMedium() {
InitializeCriticalSection(&critical_section_);
// create watcher
device_watcher_ = DeviceInformation::CreateWatcher(
BLUETOOTH_SELECTOR, nullptr, DeviceInformationKind::AssociationEndpoint);
// An app must subscribe to all of the added, removed, and updated events to
// be notified when there are device additions, removals or updates. If an app
// handles only the added event, it will not receive an update if a device is
// added to the system after the initial device enumeration completes.
// register event handlers before starting the watcher
//
// Event that is raised when a device is added to the collection enumerated
// by the DeviceWatcher.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.added?view=winrt-20348
device_watcher_.Added({this, &BluetoothClassicMedium::DeviceWatcher_Added});
// Event that is raised when a device is updated in the collection of
// enumerated devices.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.updated?view=winrt-20348
device_watcher_.Updated(
{this, &BluetoothClassicMedium::DeviceWatcher_Updated});
// Event that is raised when a device is removed from the collection of
// enumerated devices.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.removed?view=winrt-20348
device_watcher_.Removed(
{this, &BluetoothClassicMedium::DeviceWatcher_Removed});
InitializeDeviceWatcher();
}
BluetoothClassicMedium::~BluetoothClassicMedium() {}
@@ -99,6 +79,37 @@ bool BluetoothClassicMedium::StopDiscovery() {
return result;
}
void BluetoothClassicMedium::InitializeDeviceWatcher() {
// create watcher
device_watcher_ = DeviceInformation::CreateWatcher(
BLUETOOTH_SELECTOR, // aqsFilter
nullptr, // additionalProperties
DeviceInformationKind::AssociationEndpoint); // kind
// An app must subscribe to all of the added, removed, and updated events to
// be notified when there are device additions, removals or updates. If an app
// handles only the added event, it will not receive an update if a device is
// added to the system after the initial device enumeration completes.
// register event handlers before starting the watcher
//
// Event that is raised when a device is added to the collection enumerated
// by the DeviceWatcher.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.added?view=winrt-20348
device_watcher_.Added({this, &BluetoothClassicMedium::DeviceWatcher_Added});
// Event that is raised when a device is updated in the collection of
// enumerated devices.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.updated?view=winrt-20348
device_watcher_.Updated(
{this, &BluetoothClassicMedium::DeviceWatcher_Updated});
// Event that is raised when a device is removed from the collection of
// enumerated devices.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.removed?view=winrt-20348
device_watcher_.Removed(
{this, &BluetoothClassicMedium::DeviceWatcher_Removed});
}
std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
api::BluetoothDevice& remote_device, const std::string& service_uuid,
CancellationFlag* cancellation_flag) {
@@ -141,8 +152,7 @@ std::unique_ptr<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
location::nearby::CancellationFlagListener cancellationFlagListener(
cancellation_flag,
[&rfcommSocket]() { rfcommSocket.get()->CancelIOAsync().get();
});
[&rfcommSocket]() { rfcommSocket.get()->CancelIOAsync().get(); });
try {
rfcommSocket
@@ -209,16 +219,16 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) {
// device that actually supports the Bluetooth Rfcomm Service
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservice.getsdprawattributesasync?view=winrt-20348
auto attributes = /*await*/ requestedService.GetSdpRawAttributesAsync();
if (!attributes.get().HasKey(SdpServiceNameAttributeId)) {
if (!attributes.get().HasKey(Constants::SdpServiceNameAttributeId)) {
return false;
}
auto attributeReader = DataReader::FromBuffer(
attributes.get().Lookup(SdpServiceNameAttributeId));
attributes.get().Lookup(Constants::SdpServiceNameAttributeId));
auto attributeType = attributeReader.ReadByte();
if (attributeType != SdpServiceNameAttributeType) {
if (attributeType != Constants::SdpServiceNameAttributeType) {
return false;
}
@@ -237,7 +247,20 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) {
std::unique_ptr<api::BluetoothServerSocket>
BluetoothClassicMedium::ListenForService(const std::string& service_name,
const std::string& service_uuid) {
return nullptr;
if (service_uuid.empty()) {
return nullptr;
}
if (service_name.empty()) {
return nullptr;
}
auto bluetooth_server_socket =
std::make_unique<location::nearby::windows::BluetoothServerSocket>();
bluetooth_server_socket->StartListening(service_name, service_uuid);
return std::move(bluetooth_server_socket);
}
// TODO(b/184975123): replace with real implementation.
@@ -301,12 +324,12 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added(
devices_by_id_[deviceInfo.Id()] = std::move(bluetoothDevice);
LeaveCriticalSection(&critical_section_);
if (discovery_callback_.device_discovered_cb != nullptr) {
discovery_callback_.device_discovered_cb(
*devices_by_id_[deviceInfo.Id()]);
}
LeaveCriticalSection(&critical_section_);
});
}
}
@@ -326,6 +349,8 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated(
winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed(
DeviceWatcher sender, DeviceInformationUpdate deviceInfo) {
if (IsWatcherStarted()) {
EnterCriticalSection(&critical_section_);
if (discovery_callback_.device_lost_cb != nullptr) {
discovery_callback_.device_lost_cb(*devices_by_id_[deviceInfo.Id()]);
}
@@ -333,6 +358,8 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed(
devices_by_id_.erase(deviceInfo.Id());
}
LeaveCriticalSection(&critical_section_);
return winrt::fire_and_forget();
}
@@ -18,8 +18,12 @@
#include <Windows.h>
#include <stdio.h>
#include "platform/api/bluetooth_classic.h"
#include "platform/impl/windows/bluetooth_classic_device.h"
#include "platform/impl/windows/bluetooth_classic_server_socket.h"
#include "platform/impl/windows/bluetooth_classic_socket.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Enumeration.h"
#include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "platform/impl/windows/generated/winrt/base.h"
namespace location {
@@ -69,6 +73,10 @@ using winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId;
// https://docs.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-20348
using winrt::Windows::Storage::Streams::DataReader;
// Writes data to an output stream.
// https://docs.microsoft.com/en-us/uwp/api/windows.storage.streams.datawriter?view=winrt-20348
using winrt::Windows::Storage::Streams::DataWriter;
// Bluetooth protocol ID = \"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\"
// https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/aep-service-class-ids
#define BLUETOOTH_SELECTOR \
@@ -80,7 +88,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
public:
explicit BluetoothClassicMedium();
// TODO(b/184975123): replace with real implementation.
~BluetoothClassicMedium() override;
// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery()
@@ -91,7 +98,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// Returns true once discovery is well and truly stopped; after this returns,
// there must be no more invocations of the DiscoveryCallback passed in to
// StartDiscovery().
// TODO(b/184975123): replace with real implementation.
bool StopDiscovery() override;
// A combination of
@@ -120,7 +126,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
// UUID.
//
// Returns nullptr error.
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<api::BluetoothServerSocket> ListenForService(
const std::string& service_name,
const std::string& service_uuid) override;
@@ -134,6 +139,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
bool StopScanning();
bool IsWatcherStarted();
bool IsWatcherRunning();
void InitializeDeviceWatcher();
// This is for a coroutine whose return type is winrt::fire_and_forget, which
// handles async operations which don't have any dependencies.
@@ -151,27 +157,17 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
bool HaveAccess(winrt::hstring deviceId);
// Get the service requested
winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService
GetRequestedService(location::nearby::windows::BluetoothDevice* device,
winrt::guid service);
RfcommDeviceService GetRequestedService(BluetoothDevice* device,
winrt::guid service);
// Check to see that the device actually handles the requested service
bool CheckSdp(winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService
requestedService);
bool CheckSdp(RfcommDeviceService requestedService);
BluetoothClassicMedium::DiscoveryCallback discovery_callback_;
DeviceWatcher device_watcher_ = nullptr;
// The Id of the Service Name SDP attribute
const uint16 SdpServiceNameAttributeId = 0x100;
// The SDP Type of the Service Name SDP attribute.
// The first byte in the SDP Attribute encodes the SDP Attribute Type as
// follows :
// - the Attribute Type size in the least significant 3 bits,
// - the SDP Attribute Type value in the most significant 5 bits.
const char SdpServiceNameAttributeType = (4 << 3) | 5;
std::unique_ptr<BluetoothSocket> bluetooth_socket_;
// hstring is the only type of string winrt understands.
// https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/hstring
@@ -14,9 +14,22 @@
#include "platform/impl/windows/bluetooth_classic_server_socket.h"
#include <codecvt>
#include <locale>
#include <string>
#include "platform/impl/windows/bluetooth_classic_socket.h"
#include "platform/impl/windows/generated/winrt/Windows.Foundation.Collections.h"
#include "platform/impl/windows/utils.h"
#include "platform/public/logging.h"
namespace location {
namespace nearby {
namespace windows {
BluetoothServerSocket::BluetoothServerSocket() {
InitializeCriticalSection(&critical_section_);
}
BluetoothServerSocket::~BluetoothServerSocket() {}
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept()
@@ -27,17 +40,135 @@ BluetoothServerSocket::~BluetoothServerSocket() {}
// On success, returns connected socket, ready to exchange data.
// Returns nullptr on error.
// Once error is reported, it is permanent, and ServerSocket has to be closed.
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<api::BluetoothSocket> BluetoothServerSocket::Accept() {
while (bluetooth_sockets_.empty() && !closed_) {
Sleep(1000);
}
EnterCriticalSection(&critical_section_);
if (!closed_) {
std::unique_ptr<BluetoothSocket> bluetoothSocket =
std::move(bluetooth_sockets_.front());
bluetooth_sockets_.pop();
LeaveCriticalSection(&critical_section_);
return std::move(bluetoothSocket);
} else {
bluetooth_sockets_ = {};
LeaveCriticalSection(&critical_section_);
}
return nullptr;
}
Exception BluetoothServerSocket::StartListening(
const std::string& service_name, const std::string& service_uuid) {
EnterCriticalSection(&critical_section_);
winrt::guid service(service_uuid);
// Create the StreamSocketListener
stream_socket_listener_ = StreamSocketListener();
// Configure control property
stream_socket_listener_.Control().QualityOfService(
SocketQualityOfService::LowLatency);
stream_socket_listener_.Control().KeepAlive(true);
// Note From the perspective of a StreamSocket, a Parallel Patterns Library
// (PPL) completion handler is done executing (and the socket is eligible for
// disposal) before the continuation body runs. So, to keep your socket from
// being disposed if you want to use it inside a continuation, you'll need to
// use one of the techniques described in References to StreamSockets in C++
// PPL continuations.
// Assign ConnectionReceived event to event handler on the server socket
stream_socket_listener_.ConnectionReceived(
[this](StreamSocketListener streamSocketListener,
StreamSocketListenerConnectionReceivedEventArgs args) {
EnterCriticalSection(&critical_section_);
if (!closed_) {
this->bluetooth_sockets_.push(
std::make_unique<BluetoothSocket>(args.Socket()));
}
LeaveCriticalSection(&critical_section_);
});
try {
auto rfcommProviderRef =
RfcommServiceProvider::CreateAsync(RfcommServiceId::FromUuid(service))
.get();
rfcomm_provider_ = &rfcommProviderRef;
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
stream_socket_listener_
.BindServiceNameAsync(winrt::to_hstring(service_name.c_str()))
.get();
// Set the SDP attributes and start Bluetooth advertising
InitializeServiceSdpAttributes(*rfcomm_provider_, service_name);
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
NEARBY_LOGS(ERROR) << __func__ << ": Exception setting up for listen: "
<< exception.what();
LeaveCriticalSection(&critical_section_);
return {Exception::kFailed};
}
try {
rfcomm_provider_->StartAdvertising(
stream_socket_listener_.as<StreamSocketListener>(), true);
} catch (std::exception exception) {
// We will log and eat the exception since the caller
// expects nullptr if it fails
NEARBY_LOGS(ERROR) << __func__ << ": Exception calling StartAdvertising: "
<< exception.what();
LeaveCriticalSection(&critical_section_);
return {Exception::kFailed};
}
LeaveCriticalSection(&critical_section_);
return {Exception::kSuccess};
}
void BluetoothServerSocket::InitializeServiceSdpAttributes(
RfcommServiceProvider rfcommProvider, std::string service_name) {
auto sdpWriter = DataWriter();
// Write the Service Name Attribute.
sdpWriter.WriteByte(Constants::SdpServiceNameAttributeType);
// The length of the UTF-8 encoded Service Name SDP Attribute.
sdpWriter.WriteByte(service_name.size());
// The UTF-8 encoded Service Name value.
sdpWriter.UnicodeEncoding(UnicodeEncoding::Utf8);
sdpWriter.WriteString(winrt::to_hstring(service_name));
// Set the SDP Attribute on the RFCOMM Service Provider.
rfcommProvider.SdpRawAttributes().Insert(Constants::SdpServiceNameAttributeId,
sdpWriter.DetachBuffer());
}
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close()
//
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
// TODO(b/184975123): replace with real implementation.
Exception BluetoothServerSocket::Close() { return Exception(); }
Exception BluetoothServerSocket::Close() {
EnterCriticalSection(&critical_section_);
closed_ = true;
bluetooth_sockets_ = {};
rfcomm_provider_->StopAdvertising();
LeaveCriticalSection(&critical_section_);
return {Exception::kSuccess};
}
} // namespace windows
} // namespace nearby
} // namespace location
@@ -15,14 +15,60 @@
#ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SERVER_SOCKET_H_
#define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SERVER_SOCKET_H_
#include <Windows.h>
#include <queue>
#include "platform/api/bluetooth_classic.h"
#include "platform/impl/windows/bluetooth_classic_socket.h"
#include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h"
#include "platform/impl/windows/generated/winrt/Windows.Foundation.h"
#include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "platform/impl/windows/generated/winrt/base.h"
namespace location {
namespace nearby {
namespace windows {
// Supports listening for an incoming network connection using Bluetooth RFCOMM.
// https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.streamsocketlistener?view=winrt-20348
using winrt::Windows::Networking::Sockets::StreamSocketListener;
// Provides data for a ConnectionReceived event on a StreamSocketListener
// object.
// https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.streamsocketlistenerconnectionreceivedeventargs?view=winrt-20348
using winrt::Windows::Networking::Sockets::
StreamSocketListenerConnectionReceivedEventArgs;
// Represents an asynchronous action.
// https://docs.microsoft.com/en-us/uwp/api/windows.foundation.iasyncaction?view=winrt-20348
using winrt::Windows::Foundation::IAsyncAction;
// Specifies the quality of service for a StreamSocket object.
// https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.socketqualityofservice?view=winrt-20348
using winrt::Windows::Networking::Sockets::SocketQualityOfService;
// Represents an instance of a local RFCOMM service.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommserviceprovider?view=winrt-20348
using winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceProvider;
// Represents an RFCOMM service ID.
// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommserviceid?view=winrt-20348
using winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId;
// Writes data to an output stream.
// https://docs.microsoft.com/en-us/uwp/api/windows.storage.streams.datawriter?view=winrt-20348
using winrt::Windows::Storage::Streams::DataWriter;
// Specifies the type of character encoding for a stream.
// https://docs.microsoft.com/en-us/uwp/api/windows.storage.streams.unicodeencoding?view=winrt-20348
using winrt::Windows::Storage::Streams::UnicodeEncoding;
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html.
class BluetoothServerSocket : public api::BluetoothServerSocket {
public:
BluetoothServerSocket();
// TODO(b/184975123): replace with real implementation.
~BluetoothServerSocket() override;
@@ -34,14 +80,31 @@ class BluetoothServerSocket : public api::BluetoothServerSocket {
// On success, returns connected socket, ready to exchange data.
// Returns nullptr on error.
// Once error is reported, it is permanent, and ServerSocket has to be closed.
// TODO(b/184975123): replace with real implementation.
std::unique_ptr<api::BluetoothSocket> Accept() override;
// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close()
//
// Returns Exception::kIo on error, Exception::kSuccess otherwise.
// TODO(b/184975123): replace with real implementation.
Exception Close() override;
Exception StartListening(const std::string& service_name,
const std::string& service_uuid);
private:
void InitializeServiceSdpAttributes(RfcommServiceProvider rfcommProvider,
std::string service_name);
// This is used to store sockets in case Accept hasn't been called. Once
// Accept has been called the socket is popped from the queue and returned to
// the caller
std::queue<std::unique_ptr<BluetoothSocket>> bluetooth_sockets_;
StreamSocketListener stream_socket_listener_;
CRITICAL_SECTION critical_section_;
bool closed_ = false;
RfcommServiceProvider* rfcomm_provider_;
};
} // namespace windows
@@ -16,6 +16,7 @@
#define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SOCKET_H_
#include "platform/api/bluetooth_classic.h"
#include "platform/impl/windows/generated/winrt/Windows.Foundation.h"
#include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h"
#include "platform/impl/windows/generated/winrt/Windows.Storage.Streams.h"
@@ -65,6 +66,10 @@ class BluetoothSocket : public api::BluetoothSocket {
public:
BluetoothSocket();
BluetoothSocket(
winrt::Windows::Networking::Sockets::StreamSocket streamSocket)
: windows_socket_(streamSocket) {}
// TODO(b/184975123): replace with real implementation.
~BluetoothSocket() override;
+16 -1
View File
@@ -15,6 +15,9 @@
#ifndef PLATFORM_IMPL_WINDOWS_UTILS_H_
#define PLATFORM_IMPL_WINDOWS_UTILS_H_
#include <Windows.h>
#include <stdio.h>
#include <string>
namespace location {
@@ -23,7 +26,19 @@ namespace windows {
std::string uint64_to_mac_address_string(uint64_t bluetoothAddress);
}
namespace Constants {
// The Id of the Service Name SDP attribute
const uint16_t SdpServiceNameAttributeId = 0x100;
// The SDP Type of the Service Name SDP attribute.
// The first byte in the SDP Attribute encodes the SDP Attribute Type as
// follows:
// - the Attribute Type size in the least significant 3 bits,
// - the SDP Attribute Type value in the most significant 5 bits.
const char SdpServiceNameAttributeType = (4 << 3) | 5;
} // namespace Constants
} // namespace windows
} // namespace nearby
} // namespace location