From bff4051015748f8ff41c77e371301ac0ef43a97d Mon Sep 17 00:00:00 2001 From: guogang Date: Wed, 13 Jul 2022 08:39:45 -0700 Subject: [PATCH] Fixed the deadlock in BluetoothServerSocket PiperOrigin-RevId: 460724338 --- .../windows/bluetooth_adapter.cc | 9 +- .../windows/bluetooth_adapter.h | 7 +- .../windows/bluetooth_classic_medium.cc | 261 ++++++++++++++---- .../windows/bluetooth_classic_medium.h | 39 ++- .../bluetooth_classic_server_socket.cc | 223 ++++++--------- .../windows/bluetooth_classic_server_socket.h | 82 +++--- .../windows/wifi_hotspot_server_socket.cc | 52 +++- .../windows/wifi_lan_server_socket.cc | 60 +++- 8 files changed, 454 insertions(+), 279 deletions(-) diff --git a/internal/platform/implementation/windows/bluetooth_adapter.cc b/internal/platform/implementation/windows/bluetooth_adapter.cc index b760dacb..106bb747 100644 --- a/internal/platform/implementation/windows/bluetooth_adapter.cc +++ b/internal/platform/implementation/windows/bluetooth_adapter.cc @@ -147,6 +147,10 @@ bool BluetoothAdapter::SetScanMode(ScanMode scan_mode) { // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName() // Returns an empty string on error std::string BluetoothAdapter::GetName() const { + if (device_name_.has_value()) { + return *device_name_; + } + std::string instance_id(GetGenericBluetoothAdapterInstanceID()); if (instance_id.empty()) { @@ -217,9 +221,12 @@ bool BluetoothAdapter::SetName(absl::string_view name) { "Android cannot discover Windows bluetooth device " "name that exceeded the 37 bytes limit (11 " "characters in EndpointInfo)."; - return false; + device_name_ = std::string(name); + return true; } + device_name_ = std::nullopt; + if (registry_bluetooth_adapter_name_ == name) { NEARBY_LOGS(INFO) << __func__ diff --git a/internal/platform/implementation/windows/bluetooth_adapter.h b/internal/platform/implementation/windows/bluetooth_adapter.h index 8499ee92..9113825a 100644 --- a/internal/platform/implementation/windows/bluetooth_adapter.h +++ b/internal/platform/implementation/windows/bluetooth_adapter.h @@ -19,6 +19,7 @@ #include #include +#include #include #include "internal/platform/implementation/bluetooth_adapter.h" @@ -100,11 +101,15 @@ class BluetoothAdapter : public api::BluetoothAdapter { std::string registry_bluetooth_adapter_name_; IRadio windows_bluetooth_radio_; - char *GetGenericBluetoothAdapterInstanceID(void) const; + char *GetGenericBluetoothAdapterInstanceID() const; void find_and_replace(char *source, const char *strFind, const char *strReplace) const; ScanMode scan_mode_ = ScanMode::kNone; ScanModeCallback scan_mode_changed_ = nullptr; + + // Used to fake the device name when the device name is longer than android + // limitation. + std::optional device_name_; }; } // namespace windows diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.cc b/internal/platform/implementation/windows/bluetooth_classic_medium.cc index 18bc1248..a694ffbb 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.cc @@ -24,6 +24,7 @@ #include #include +#include "absl/synchronization/mutex.h" #include "internal/platform/cancellation_flag.h" #include "internal/platform/cancellation_flag_listener.h" #include "internal/platform/exception.h" @@ -43,11 +44,11 @@ namespace location { namespace nearby { namespace windows { +constexpr uint8_t kAndroidDiscoverableBluetoothNameMaxLength = 37; // bytes + BluetoothClassicMedium::BluetoothClassicMedium( api::BluetoothAdapter& bluetoothAdapter) : bluetooth_adapter_(dynamic_cast(bluetoothAdapter)) { - InitializeCriticalSection(&critical_section_); - InitializeDeviceWatcher(); bluetooth_adapter_.SetOnScanModeChanged(std::bind( @@ -58,19 +59,60 @@ BluetoothClassicMedium::~BluetoothClassicMedium() {} void BluetoothClassicMedium::OnScanModeChanged( BluetoothAdapter::ScanMode scanMode) { - scan_mode_ = scanMode; - bool radioDiscoverable = bluetooth_adapter_.GetScanMode() == - BluetoothAdapter::ScanMode::kConnectableDiscoverable; + absl::MutexLock lock(&mutex_); - if (bluetooth_server_socket_ != nullptr) { - bluetooth_server_socket_->SetScanMode(radioDiscoverable); + NEARBY_LOGS(INFO) << __func__ + << ": OnScanModeChanged is called with scanMode: " + << static_cast(scanMode); + + if (scanMode == scan_mode_) { + NEARBY_LOGS(INFO) << __func__ << ": No change of scan mode."; + return; + } + + scan_mode_ = scanMode; + bool radio_discoverable = + scan_mode_ == BluetoothAdapter::ScanMode::kConnectableDiscoverable; + + if (bluetooth_adapter_.GetName().size() > + kAndroidDiscoverableBluetoothNameMaxLength) { + // If the name longer than the android limitation, always set the value to + // false. + radio_discoverable = false; + } + + if (is_radio_discoverable_ == radio_discoverable) { + NEARBY_LOGS(INFO) << __func__ << ": No change of radio discovery."; + return; + } + + if (rfcomm_provider_ == nullptr) { + NEARBY_LOGS(INFO) << __func__ << ": No advertising."; + return; + } + + try { + rfcomm_provider_.StopAdvertising(); + rfcomm_provider_.StartAdvertising( + raw_server_socket_->stream_socket_listener(), radio_discoverable); + is_radio_discoverable_ = radio_discoverable; + return; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": OnScanModeChanged exception: " << exception.what(); + return; + } catch (const winrt::hresult_error& ex) { + NEARBY_LOGS(ERROR) << __func__ + << ": OnScanModeChanged exception: " << ex.code() << ": " + << winrt::to_string(ex.message()); + return; } } bool BluetoothClassicMedium::StartDiscovery( BluetoothClassicMedium::DiscoveryCallback discovery_callback) { + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "StartDiscovery is called."; - EnterCriticalSection(&critical_section_); bool result = false; discovery_callback_ = discovery_callback; @@ -79,14 +121,12 @@ bool BluetoothClassicMedium::StartDiscovery( result = StartScanning(); } - LeaveCriticalSection(&critical_section_); - return result; } bool BluetoothClassicMedium::StopDiscovery() { + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "StopDiscovery is called."; - EnterCriticalSection(&critical_section_); bool result = false; @@ -94,8 +134,6 @@ bool BluetoothClassicMedium::StopDiscovery() { result = StopScanning(); } - LeaveCriticalSection(&critical_section_); - return result; } @@ -138,6 +176,7 @@ void BluetoothClassicMedium::InitializeDeviceWatcher() { std::unique_ptr BluetoothClassicMedium::ConnectToService( api::BluetoothDevice& remote_device, const std::string& service_uuid, CancellationFlag* cancellation_flag) { + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "ConnectToService is called."; if (service_uuid.empty()) { NEARBY_LOGS(ERROR) << __func__ << ": service_uuid not specified."; @@ -220,8 +259,6 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( return nullptr; } - EnterCriticalSection(&critical_section_); - std::unique_ptr rfcomm_socket = std::make_unique(); @@ -230,7 +267,6 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( << __func__ << ": Bluetooth Classic socket connection cancelled for device: " << winrt::to_string(device_id) << ", service: " << service_uuid; - LeaveCriticalSection(&critical_section_); return nullptr; } location::nearby::CancellationFlagListener cancellation_flag_listener( @@ -244,7 +280,6 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( rfcomm_socket->Connect(requested_service.ConnectionHostName(), requested_service.ConnectionServiceName()); if (!success) { - LeaveCriticalSection(&critical_section_); return nullptr; } } catch (std::exception exception) { @@ -252,14 +287,9 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( // expects nullptr if it fails NEARBY_LOGS(ERROR) << __func__ << ": Exception connecting bluetooth async: " << exception.what(); - - LeaveCriticalSection(&critical_section_); - return nullptr; } - LeaveCriticalSection(&critical_section_); - return std::move(rfcomm_socket); } @@ -345,6 +375,7 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) { return false; } } + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord // // service_uuid is the canonical textual representation @@ -357,6 +388,7 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) { std::unique_ptr BluetoothClassicMedium::ListenForService(const std::string& service_name, const std::string& service_uuid) { + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "ListenForService is called with service name: " << service_name << "."; if (service_uuid.empty()) { @@ -369,26 +401,24 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name, return nullptr; } - auto bluetooth_server_socket = - std::make_unique( - service_name, service_uuid); + service_name_ = service_name; + service_uuid_ = service_uuid; - if (bluetooth_server_socket == nullptr) { - NEARBY_LOGS(ERROR) << __func__ << ": Failed to create the server socket."; - return nullptr; - } + scan_mode_ = bluetooth_adapter_.GetScanMode(); - bool radioDiscoverable = bluetooth_adapter_.GetScanMode() == - BluetoothAdapter::ScanMode::kConnectableDiscoverable; + NEARBY_LOGS(INFO) << __func__ + << ": scan_mode: " << static_cast(scan_mode_); + bool radio_discoverable = + scan_mode_ == BluetoothAdapter::ScanMode::kConnectableDiscoverable; - Exception result = bluetooth_server_socket->StartListening(radioDiscoverable); + bool result = StartAdvertising(radio_discoverable); - if (result.value != Exception::kSuccess) { + if (!result) { NEARBY_LOGS(ERROR) << __func__ << ": Failed to start listening."; return nullptr; } - return std::move(bluetooth_server_socket); + return std::move(server_socket_); } api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( @@ -432,7 +462,7 @@ bool BluetoothClassicMedium::StopScanning() { winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( DeviceWatcher sender, DeviceInformation deviceInfo) { - EnterCriticalSection(&critical_section_); + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "Device added " << winrt::to_string(deviceInfo.Id()); if (IsWatcherStarted()) { // Represents a Bluetooth device. @@ -449,8 +479,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( // We're already tracking this one NEARBY_LOGS(INFO) << // "DeviceWatcher_Added entered critical section."; - LeaveCriticalSection(&critical_section_); - return winrt::fire_and_forget(); } @@ -471,14 +499,12 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( } } - LeaveCriticalSection(&critical_section_); - return winrt::fire_and_forget(); } winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated( DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { - EnterCriticalSection(&critical_section_); + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "Device updated " @@ -487,14 +513,12 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated( if (!IsWatcherStarted()) { // Spurious call, watcher has stopped or wasn't started - LeaveCriticalSection(&critical_section_); return winrt::fire_and_forget(); } auto it = discovered_devices_by_id_.find(deviceInfoUpdate.Id()); if (it == discovered_devices_by_id_.end()) { - LeaveCriticalSection(&critical_section_); // Not tracking this device return winrt::fire_and_forget(); } @@ -505,21 +529,17 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated( *discovered_devices_by_id_[deviceInfoUpdate.Id()]); } - LeaveCriticalSection(&critical_section_); - return winrt::fire_and_forget(); } winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed( DeviceWatcher sender, DeviceInformationUpdate deviceInfo) { - EnterCriticalSection(&critical_section_); + absl::MutexLock lock(&mutex_); NEARBY_LOGS(INFO) << "Device removed " << discovered_devices_by_id_[deviceInfo.Id()]->GetName() << " (" << winrt::to_string(deviceInfo.Id()) << ")"; if (!IsWatcherStarted()) { - LeaveCriticalSection(&critical_section_); - return winrt::fire_and_forget(); } @@ -530,8 +550,6 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Removed( discovered_devices_by_id_.erase(deviceInfo.Id()); - LeaveCriticalSection(&critical_section_); - return winrt::fire_and_forget(); } @@ -556,6 +574,151 @@ bool BluetoothClassicMedium::IsWatcherRunning() { (status == DeviceWatcherStatus::Stopping); } +bool BluetoothClassicMedium::StartAdvertising(bool radio_discoverable) { + NEARBY_LOGS(INFO) << __func__ + << ": StartAdvertising is called with radio_discoverable: " + << radio_discoverable << "."; + + try { + if (rfcomm_provider_ != nullptr && + is_radio_discoverable_ == radio_discoverable) { + NEARBY_LOGS(WARNING) << __func__ + << ": Ignore StartAdvertising due to no change to " + "current advertising."; + return true; + } + + if (rfcomm_provider_ != nullptr && !StopAdvertising()) { + NEARBY_LOGS(WARNING) << __func__ + << ": Failed to StartAdvertising due to cannot stop " + "running advertising."; + return false; + } + + rfcomm_provider_ = + RfcommServiceProvider::CreateAsync( + RfcommServiceId::FromUuid(winrt::guid(service_uuid_))) + .get(); + + server_socket_ = std::make_unique( + winrt::to_string(rfcomm_provider_.ServiceId().AsString())); + + raw_server_socket_ = server_socket_.get(); + + if (!server_socket_->listen()) { + NEARBY_LOGS(ERROR) + << __func__ + << ": Failed to StartAdvertising due to cannot start socket."; + server_socket_->Close(); + server_socket_ = nullptr; + rfcomm_provider_ = nullptr; + return false; + } + + server_socket_->SetCloseNotifier([&]() { + absl::MutexLock lock(&mutex_); + StopAdvertising(); + }); + + // Set the SDP attributes and start Bluetooth advertising + InitializeServiceSdpAttributes(rfcomm_provider_, service_name_); + + // Start to advertising. + rfcomm_provider_.StartAdvertising(server_socket_->stream_socket_listener(), + radio_discoverable); + is_radio_discoverable_ = radio_discoverable; + + NEARBY_LOGS(INFO) << ": StartListening completed successfully."; + return 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 setting up for listen: " + << exception.what(); + + if (server_socket_ != nullptr) { + server_socket_->Close(); + server_socket_ = nullptr; + } + + if (rfcomm_provider_ != nullptr) { + rfcomm_provider_ = nullptr; + } + + return false; + } catch (const winrt::hresult_error& ex) { + NEARBY_LOGS(ERROR) << __func__ + << ": Exception setting up for listen: " << ex.code() + << ": " << winrt::to_string(ex.message()); + if (server_socket_ != nullptr) { + server_socket_->Close(); + server_socket_ = nullptr; + } + + if (rfcomm_provider_ != nullptr) { + rfcomm_provider_ = nullptr; + } + + return false; + } +} + +bool BluetoothClassicMedium::StopAdvertising() { + NEARBY_LOGS(INFO) << __func__ << ": StopAdvertising is called"; + + try { + if (rfcomm_provider_ == nullptr) { + NEARBY_LOGS(ERROR) << __func__ + << ": Ignore StopAdvertising due to no advertising."; + return true; + } + + rfcomm_provider_.StopAdvertising(); + rfcomm_provider_ = nullptr; + raw_server_socket_ = nullptr; + server_socket_ = nullptr; + + NEARBY_LOGS(INFO) << ": StopAdvertising completed successfully."; + return true; + } catch (std::exception exception) { + NEARBY_LOGS(ERROR) << __func__ + << ": StopAdvertising exception: " << exception.what(); + return false; + } catch (const winrt::hresult_error& ex) { + NEARBY_LOGS(ERROR) << __func__ + << ": StopAdvertising exception: " << ex.code() << ": " + << winrt::to_string(ex.message()); + return false; + } +} + +bool BluetoothClassicMedium::InitializeServiceSdpAttributes( + RfcommServiceProvider rfcomm_provider, std::string service_name) { + try { + 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. + rfcomm_provider.SdpRawAttributes().Insert( + Constants::SdpServiceNameAttributeId, sdpWriter.DetachBuffer()); + + return true; + } catch (...) { + NEARBY_LOGS(ERROR) << __func__ + << ": Failed to InitializeServiceSdpAttributes."; + return false; + } +} + } // namespace windows } // namespace nearby } // namespace location diff --git a/internal/platform/implementation/windows/bluetooth_classic_medium.h b/internal/platform/implementation/windows/bluetooth_classic_medium.h index 5a5b8557..ef90cf75 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_medium.h +++ b/internal/platform/implementation/windows/bluetooth_classic_medium.h @@ -15,6 +15,11 @@ #ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_ #define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_ +#include +#include +#include + +#include "absl/synchronization/mutex.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/windows/bluetooth_adapter.h" #include "internal/platform/implementation/windows/bluetooth_classic_device.h" @@ -47,6 +52,14 @@ using winrt::Windows::Devices::Enumeration::DeviceInformationUpdate; // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher?view=winrt-20348 using winrt::Windows::Devices::Enumeration::DeviceWatcher; +// 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; + // Describes the state of a DeviceWatcher object. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcherstatus?view=winrt-20348 using winrt::Windows::Devices::Enumeration::DeviceWatcherStatus; @@ -67,6 +80,10 @@ using winrt::Windows::Devices::Enumeration::DeviceAccessInformation; // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommserviceid?view=winrt-20348 using winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId; +// 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; + // Reads data from an input stream. // https://docs.microsoft.com/en-us/uwp/api/windows.storage.streams.datareader?view=winrt-20348 using winrt::Windows::Storage::Streams::DataReader; @@ -84,7 +101,7 @@ using winrt::Windows::Storage::Streams::DataWriter; // medium. class BluetoothClassicMedium : public api::BluetoothClassicMedium { public: - BluetoothClassicMedium(api::BluetoothAdapter& bluetoothAdapter); + explicit BluetoothClassicMedium(api::BluetoothAdapter& bluetoothAdapter); ~BluetoothClassicMedium() override; @@ -134,6 +151,10 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { private: bool StartScanning(); bool StopScanning(); + bool StartAdvertising(bool radio_discoverable); + bool StopAdvertising(); + bool InitializeServiceSdpAttributes(RfcommServiceProvider rfcomm_provider, + std::string service_name); bool IsWatcherStarted(); bool IsWatcherRunning(); void InitializeDeviceWatcher(); @@ -166,7 +187,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { DeviceWatcher device_watcher_ = nullptr; std::unique_ptr bluetooth_socket_; - std::unique_ptr bluetooth_server_socket_; std::string service_name_; std::string service_uuid_; @@ -176,14 +196,19 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { std::map> discovered_devices_by_id_; - // CRITICAL_SECTION is a lightweight synchronization mechanism - // https://docs.microsoft.com/en-us/windows/win32/sync/critical-section-objects - CRITICAL_SECTION critical_section_; - BluetoothAdapter& bluetooth_adapter_; - BluetoothAdapter::ScanMode scan_mode_; + BluetoothAdapter::ScanMode scan_mode_ = BluetoothAdapter::ScanMode::kUnknown; std::unique_ptr remote_device_to_connect_; + + // Used for advertising. + RfcommServiceProvider rfcomm_provider_ = nullptr; + std::unique_ptr server_socket_ = nullptr; + BluetoothServerSocket* raw_server_socket_ = nullptr; + bool is_radio_discoverable_ = false; + + // Used to enable thread safe for APIs. + absl::Mutex mutex_; }; } // namespace windows diff --git a/internal/platform/implementation/windows/bluetooth_classic_server_socket.cc b/internal/platform/implementation/windows/bluetooth_classic_server_socket.cc index d33518e4..affeee9a 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_server_socket.cc +++ b/internal/platform/implementation/windows/bluetooth_classic_server_socket.cc @@ -15,31 +15,24 @@ #include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h" #include +#include #include +#include #include +#include "internal/platform/exception.h" #include "internal/platform/implementation/windows/bluetooth_classic_socket.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.Collections.h" -#include "internal/platform/implementation/windows/utils.h" #include "internal/platform/logging.h" namespace location { namespace nearby { namespace windows { -BluetoothServerSocket::BluetoothServerSocket(const std::string service_name, - const std::string service_uuid) - : radio_discoverable_(false), - service_name_(service_name), - service_uuid_(service_uuid), - rfcomm_provider_(nullptr) { - InitializeCriticalSection(&critical_section_); -} +BluetoothServerSocket::BluetoothServerSocket(absl::string_view service_name) + : service_name_(service_name) {} -BluetoothServerSocket::~BluetoothServerSocket() {} +BluetoothServerSocket::~BluetoothServerSocket() { Close(); } -// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept() -// // Blocks until either: // - at least one incoming connection request is available, or // - ServerSocket is closed. @@ -47,166 +40,106 @@ BluetoothServerSocket::~BluetoothServerSocket() {} // Returns nullptr on error. // Once error is reported, it is permanent, and ServerSocket has to be closed. std::unique_ptr BluetoothServerSocket::Accept() { - while (bluetooth_sockets_.empty() && !closed_) { - Sleep(1000); + absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Accept is called."; + + while (!closed_ && pending_sockets_.empty()) { + cond_.Wait(&mutex_); } + if (closed_) return nullptr; - EnterCriticalSection(&critical_section_); - if (!closed_) { - std::unique_ptr bluetoothSocket = - std::move(bluetooth_sockets_.front()); - bluetooth_sockets_.pop(); - LeaveCriticalSection(&critical_section_); + StreamSocket bluetooth_socket = pending_sockets_.front(); + pending_sockets_.pop_front(); - return std::move(bluetoothSocket); - } else { - bluetooth_sockets_ = {}; - LeaveCriticalSection(&critical_section_); - } - - return nullptr; + NEARBY_LOGS(INFO) << __func__ << ": Accepted a remote connection."; + return std::make_unique(bluetooth_socket); } -Exception BluetoothServerSocket::StartListening(bool radioDiscoverable) { - EnterCriticalSection(&critical_section_); +void BluetoothServerSocket::SetCloseNotifier(std::function notifier) { + close_notifier_ = std::move(notifier); +} - radio_discoverable_ = radioDiscoverable; +// Returns Exception::kIo on error, Exception::kSuccess otherwise. +Exception BluetoothServerSocket::Close() { + try { + absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Close is called."; - // Create the StreamSocketListener + if (closed_) { + return {Exception::kSuccess}; + } + + if (stream_socket_listener_ != nullptr) { + stream_socket_listener_.ConnectionReceived(listener_event_token_); + stream_socket_listener_.Close(); + stream_socket_listener_ = nullptr; + + for (const auto& pending_socket : pending_sockets_) { + pending_socket.Close(); + } + + pending_sockets_ = {}; + } + closed_ = true; + cond_.SignalAll(); + + if (close_notifier_ != nullptr) { + close_notifier_(); + } + + NEARBY_LOGS(INFO) << __func__ << ": Close completed succesfully."; + return {Exception::kSuccess}; + } catch (...) { + closed_ = true; + cond_.SignalAll(); + + NEARBY_LOGS(INFO) << __func__ << ": Failed to close server socket."; + return {Exception::kIo}; + } +} + +bool BluetoothServerSocket::listen() { + // Setup stream socket listener. 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(args.Socket())); - } - LeaveCriticalSection(&critical_section_); - }); + // Setup socket event of ConnectionReceived. + listener_event_token_ = stream_socket_listener_.ConnectionReceived( + {this, &BluetoothServerSocket::Listener_ConnectionReceived}); try { - auto rfcommProviderRef = - RfcommServiceProvider::CreateAsync( - RfcommServiceId::FromUuid(winrt::guid(service_uuid_))) - .get(); - - rfcomm_provider_ = rfcommProviderRef; - stream_socket_listener_ - .BindServiceNameAsync( - winrt::to_hstring(rfcomm_provider_.ServiceId().AsString()), - SocketProtectionLevel::PlainSocket) + .BindServiceNameAsync(winrt::to_hstring(service_name_), + SocketProtectionLevel::PlainSocket) .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}; - } catch (const winrt::hresult_error& ex) { - NEARBY_LOGS(ERROR) << __func__ - << ": Exception setting up for listen: " << ex.code() - << ": " << winrt::to_string(ex.message()); - - LeaveCriticalSection(&critical_section_); - - return {Exception::kFailed}; + return true; + } catch (...) { + NEARBY_LOGS(WARNING) << "cannot accept connection on preferred port."; } - StartAdvertising(); - - LeaveCriticalSection(&critical_section_); - - return {Exception::kSuccess}; + return false; } -Exception BluetoothServerSocket::StartAdvertising() { - try { - rfcomm_provider_.StartAdvertising(stream_socket_listener_, - radio_discoverable_); - } 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(); +::winrt::fire_and_forget BluetoothServerSocket::Listener_ConnectionReceived( + StreamSocketListener listener, + StreamSocketListenerConnectionReceivedEventArgs const& args) { + absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Received connection."; - LeaveCriticalSection(&critical_section_); - - return {Exception::kFailed}; - } catch (const winrt::hresult_error& ex) { - NEARBY_LOGS(ERROR) << __func__ - << ": Exception calling StartAdvertising: " << ex.code() - << ": " << winrt::to_string(ex.message()); - - LeaveCriticalSection(&critical_section_); - - return {Exception::kFailed}; - } - - return {Exception::kSuccess}; -} - -void BluetoothServerSocket::StopAdvertising() { - rfcomm_provider_.StopAdvertising(); -} - -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. -Exception BluetoothServerSocket::Close() { - EnterCriticalSection(&critical_section_); if (closed_) { - LeaveCriticalSection(&critical_section_); - return {Exception::kSuccess}; + return ::winrt::fire_and_forget{}; } - rfcomm_provider_.StopAdvertising(); - closed_ = true; - bluetooth_sockets_ = {}; - LeaveCriticalSection(&critical_section_); - - return {Exception::kSuccess}; + pending_sockets_.push_back(args.Socket()); + cond_.SignalAll(); + return ::winrt::fire_and_forget{}; } + } // namespace windows } // namespace nearby } // namespace location diff --git a/internal/platform/implementation/windows/bluetooth_classic_server_socket.h b/internal/platform/implementation/windows/bluetooth_classic_server_socket.h index f1448860..5c54df26 100644 --- a/internal/platform/implementation/windows/bluetooth_classic_server_socket.h +++ b/internal/platform/implementation/windows/bluetooth_classic_server_socket.h @@ -17,13 +17,15 @@ #include +#include +#include #include +#include +#include "absl/strings/string_view.h" +#include "absl/synchronization/mutex.h" #include "internal/platform/implementation/bluetooth_classic.h" #include "internal/platform/implementation/windows/bluetooth_classic_socket.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Foundation.h" -#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h" #include "internal/platform/implementation/windows/generated/winrt/base.h" namespace location { @@ -40,30 +42,10 @@ using winrt::Windows::Networking::Sockets::StreamSocketListener; 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; - // Specifies the level of encryption to use on a StreamSocket object. // https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.socketprotectionlevel?view=winrt-22000 using winrt::Windows::Networking::Sockets::SocketProtectionLevel; @@ -71,8 +53,7 @@ using winrt::Windows::Networking::Sockets::SocketProtectionLevel; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. class BluetoothServerSocket : public api::BluetoothServerSocket { public: - BluetoothServerSocket(const std::string service_name, - const std::string service_uuid); + BluetoothServerSocket(absl::string_view service_name); ~BluetoothServerSocket() override; @@ -91,38 +72,41 @@ class BluetoothServerSocket : public api::BluetoothServerSocket { // Returns Exception::kIo on error, Exception::kSuccess otherwise. Exception Close() override; - Exception StartListening(bool radioDiscoverable); + // Called by the server side of a connection before passing ownership of + // WifiLanServerSocker to user, to track validity of a pointer to this + // server socket. + void SetCloseNotifier(std::function notifier); - void SetScanMode(bool radioDiscoverable) { - StopAdvertising(); - radio_discoverable_ = radioDiscoverable; - if (radio_discoverable_) { - StartAdvertising(); - } + bool listen(); + + const StreamSocketListener& stream_socket_listener() const { + return stream_socket_listener_; } private: - void InitializeServiceSdpAttributes(RfcommServiceProvider rfcommProvider, - std::string service_name); + // The listener is accepting incoming connections + ::winrt::fire_and_forget Listener_ConnectionReceived( + StreamSocketListener listener, + StreamSocketListenerConnectionReceivedEventArgs const& args); - Exception StartAdvertising(); - void StopAdvertising(); + // Retrieves IP addresses from local machine + std::vector GetIpAddresses() const; - // 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> bluetooth_sockets_; + mutable absl::Mutex mutex_; + absl::CondVar cond_; + std::deque pending_sockets_ ABSL_GUARDED_BY(mutex_); + StreamSocketListener stream_socket_listener_{nullptr}; + winrt::event_token listener_event_token_{}; - StreamSocketListener stream_socket_listener_; - winrt::event_token listener_token_; - CRITICAL_SECTION critical_section_; + // Close notifier + std::function close_notifier_ = nullptr; + + // IP addresses of the computer. mDNS uses them to advertise. + std::vector ip_addresses_{}; + + // Cache socket not be picked by upper layer + std::string service_name_; bool closed_ = false; - bool radio_discoverable_; - - const std::string service_name_; - const std::string service_uuid_; - - RfcommServiceProvider rfcomm_provider_; }; } // namespace windows diff --git a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc index bdd5f748..dbdcfd2c 100644 --- a/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_hotspot_server_socket.cc @@ -12,6 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + +#include +#include +#include +#include + +#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h" #include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi_hotspot.h" #include "internal/platform/logging.h" @@ -19,9 +27,12 @@ namespace location { namespace nearby { namespace windows { - namespace { - constexpr int kMaxRetries = 3; + +using ::winrt::Windows::Networking::Sockets::SocketQualityOfService; + +constexpr int kMaxRetries = 3; + } // namespace WifiHotspotServerSocket::WifiHotspotServerSocket(int port) : port_(port) {} @@ -53,6 +64,8 @@ int WifiHotspotServerSocket::GetPort() const { std::unique_ptr WifiHotspotServerSocket::Accept() { absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Accept is called."; + while (!closed_ && pending_sockets_.empty()) { cond_.Wait(&mutex_); } @@ -60,6 +73,8 @@ std::unique_ptr WifiHotspotServerSocket::Accept() { StreamSocket wifi_hotspot_socket = pending_sockets_.front(); pending_sockets_.pop_front(); + + NEARBY_LOGS(INFO) << __func__ << ": Accepted a remote connection."; return std::make_unique(wifi_hotspot_socket); } @@ -70,6 +85,8 @@ void WifiHotspotServerSocket::SetCloseNotifier(std::function notifier) { Exception WifiHotspotServerSocket::Close() { try { absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Close is called."; + if (closed_) { return {Exception::kSuccess}; } @@ -78,28 +95,32 @@ Exception WifiHotspotServerSocket::Close() { stream_socket_listener_.Close(); stream_socket_listener_ = nullptr; - if (!pending_sockets_.empty()) { - auto it = pending_sockets_.begin(); - while (it != pending_sockets_.end()) { - it->Close(); - } + for (const auto &pending_socket : pending_sockets_) { + pending_socket.Close(); } - cond_.SignalAll(); + pending_sockets_ = {}; } closed_ = true; + cond_.SignalAll(); if (close_notifier_ != nullptr) { close_notifier_(); } + + NEARBY_LOGS(INFO) << __func__ << ": Close completed succesfully."; return {Exception::kSuccess}; } catch (...) { + closed_ = true; + cond_.SignalAll(); + + NEARBY_LOGS(INFO) << __func__ << ": Failed to close server socket."; return {Exception::kIo}; } } bool WifiHotspotServerSocket::listen() { - // Check IP address + // Get current IP addresses of the device. hotspot_ipaddr_ = GetHotspotIpAddresses(); if (hotspot_ipaddr_.empty()) { @@ -108,10 +129,15 @@ bool WifiHotspotServerSocket::listen() { return false; } - // Save connection callback + // Setup stream socket listener. stream_socket_listener_ = StreamSocketListener(); - // Setup callback + stream_socket_listener_.Control().QualityOfService( + SocketQualityOfService::LowLatency); + + stream_socket_listener_.Control().KeepAlive(true); + + // Setup socket event of ConnectionReceived. listener_event_token_ = stream_socket_listener_.ConnectionReceived( {this, &WifiHotspotServerSocket::Listener_ConnectionReceived}); @@ -133,7 +159,7 @@ bool WifiHotspotServerSocket::listen() { try { stream_socket_listener_.BindServiceNameAsync({}).get(); - // need to save the port information + // need to save the port information. port_ = std::stoi(stream_socket_listener_.Information().LocalPort().c_str()); NEARBY_LOGS(INFO) << "Server Socket port: " << port_; @@ -150,6 +176,7 @@ fire_and_forget WifiHotspotServerSocket::Listener_ConnectionReceived( StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs const &args) { absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Received connection."; if (closed_) { return fire_and_forget{}; @@ -169,7 +196,6 @@ bool HasEnding(std::string const &full_string, std::string const &ending) { } } - std::vector WifiHotspotServerSocket::GetIpAddresses() const { std::vector result{}; auto host_names = NetworkInformation::GetHostNames(); diff --git a/internal/platform/implementation/windows/wifi_lan_server_socket.cc b/internal/platform/implementation/windows/wifi_lan_server_socket.cc index 82ca87ab..9bfceece 100644 --- a/internal/platform/implementation/windows/wifi_lan_server_socket.cc +++ b/internal/platform/implementation/windows/wifi_lan_server_socket.cc @@ -12,6 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include + +#include +#include +#include +#include + +#include "internal/platform/implementation/windows/generated/winrt/Windows.Networking.Sockets.h" #include "internal/platform/implementation/windows/utils.h" #include "internal/platform/implementation/windows/wifi_lan.h" #include "internal/platform/logging.h" @@ -19,12 +27,17 @@ namespace location { namespace nearby { namespace windows { +namespace { + +using ::winrt::Windows::Networking::Sockets::SocketQualityOfService; + +} WifiLanServerSocket::WifiLanServerSocket(int port) : port_(port) {} WifiLanServerSocket::~WifiLanServerSocket() { Close(); } -// Returns ip address. +// Returns the first IP address. std::string WifiLanServerSocket::GetIPAddress() const { if (stream_socket_listener_ == nullptr) { return {}; @@ -40,7 +53,7 @@ std::string WifiLanServerSocket::GetIPAddress() const { return ip_addresses_.front(); } -// Returns port. +// Returns socket port. int WifiLanServerSocket::GetPort() const { if (stream_socket_listener_ == nullptr) { return 0; @@ -57,6 +70,8 @@ int WifiLanServerSocket::GetPort() const { // Once error is reported, it is permanent, and ServerSocket has to be closed. std::unique_ptr WifiLanServerSocket::Accept() { absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Accept is called."; + while (!closed_ && pending_sockets_.empty()) { cond_.Wait(&mutex_); } @@ -64,6 +79,8 @@ std::unique_ptr WifiLanServerSocket::Accept() { StreamSocket wifi_lan_socket = pending_sockets_.front(); pending_sockets_.pop_front(); + + NEARBY_LOGS(INFO) << __func__ << ": Accepted a remote connection."; return std::make_unique(wifi_lan_socket); } @@ -75,6 +92,8 @@ void WifiLanServerSocket::SetCloseNotifier(std::function notifier) { Exception WifiLanServerSocket::Close() { try { absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Close is called."; + if (closed_) { return {Exception::kSuccess}; } @@ -83,27 +102,33 @@ Exception WifiLanServerSocket::Close() { stream_socket_listener_.Close(); stream_socket_listener_ = nullptr; - if (!pending_sockets_.empty()) { - auto it = pending_sockets_.begin(); - while (it != pending_sockets_.end()) { - it->Close(); - } + for (const auto& pending_socket : pending_sockets_) { + pending_socket.Close(); } - cond_.SignalAll(); + pending_sockets_ = {}; } + closed_ = true; + cond_.SignalAll(); + if (close_notifier_ != nullptr) { close_notifier_(); } + + NEARBY_LOGS(INFO) << __func__ << ": Close completed succesfully."; return {Exception::kSuccess}; } catch (...) { + closed_ = true; + cond_.SignalAll(); + + NEARBY_LOGS(INFO) << __func__ << ": Failed to close server socket."; return {Exception::kIo}; } } bool WifiLanServerSocket::listen() { - // Check IP address + // Get current IP addresses of the device. ip_addresses_ = GetIpAddresses(); if (ip_addresses_.empty()) { @@ -112,10 +137,15 @@ bool WifiLanServerSocket::listen() { return false; } - // Save connection callback + // Setup stream socket listener. stream_socket_listener_ = StreamSocketListener(); - // Setup callback + stream_socket_listener_.Control().QualityOfService( + SocketQualityOfService::LowLatency); + + stream_socket_listener_.Control().KeepAlive(true); + + // Setup socket event of ConnectionReceived. listener_event_token_ = stream_socket_listener_.ConnectionReceived( {this, &WifiLanServerSocket::Listener_ConnectionReceived}); @@ -135,7 +165,8 @@ bool WifiLanServerSocket::listen() { try { stream_socket_listener_.BindServiceNameAsync({}).get(); - // need to save the port information + + // Need to save the port information. port_ = std::stoi(stream_socket_listener_.Information().LocalPort().c_str()); return true; @@ -151,6 +182,7 @@ fire_and_forget WifiLanServerSocket::Listener_ConnectionReceived( StreamSocketListener listener, StreamSocketListenerConnectionReceivedEventArgs const& args) { absl::MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << __func__ << ": Received connection."; if (closed_) { return fire_and_forget{}; @@ -161,7 +193,7 @@ fire_and_forget WifiLanServerSocket::Listener_ConnectionReceived( return fire_and_forget{}; } -// Retrieves IP addresses from local machine +// Retrieves IP addresses from local machine. std::vector WifiLanServerSocket::GetIpAddresses() const { std::vector result{}; auto host_names = NetworkInformation::GetHostNames(); @@ -170,7 +202,7 @@ std::vector WifiLanServerSocket::GetIpAddresses() const { host_name.IPInformation().NetworkAdapter() != nullptr && host_name.Type() == HostNameType::Ipv4) { std::string ipv4_s = winrt::to_string(host_name.ToString()); - // Converts ip address from x.x.x.x to 4 bytes format + // Converts ip address from x.x.x.x to 4 bytes format. in_addr address; address.S_un.S_addr = inet_addr(ipv4_s.c_str()); char ipv4_b[5];