Fixed the deadlock in BluetoothServerSocket

PiperOrigin-RevId: 460724338
This commit is contained in:
guogang
2022-07-13 08:41:13 -07:00
committed by Copybara-Service
parent e4a0f280e2
commit bff4051015
8 changed files with 454 additions and 279 deletions
@@ -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__
@@ -19,6 +19,7 @@
#include <windows.h>
#include <functional>
#include <optional>
#include <string>
#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<std::string> device_name_;
};
} // namespace windows
@@ -24,6 +24,7 @@
#include <string>
#include <utility>
#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&>(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<int>(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<api::BluetoothSocket> 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<api::BluetoothSocket> BluetoothClassicMedium::ConnectToService(
return nullptr;
}
EnterCriticalSection(&critical_section_);
std::unique_ptr<BluetoothSocket> rfcomm_socket =
std::make_unique<BluetoothSocket>();
@@ -230,7 +267,6 @@ std::unique_ptr<api::BluetoothSocket> 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<api::BluetoothSocket> 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<api::BluetoothSocket> 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<api::BluetoothServerSocket>
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<location::nearby::windows::BluetoothServerSocket>(
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<int>(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<BluetoothServerSocket>(
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
@@ -15,6 +15,11 @@
#ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_
#define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_
#include <map>
#include <memory>
#include <string>
#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<BluetoothSocket> bluetooth_socket_;
std::unique_ptr<BluetoothServerSocket> bluetooth_server_socket_;
std::string service_name_;
std::string service_uuid_;
@@ -176,14 +196,19 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium {
std::map<winrt::hstring, std::unique_ptr<BluetoothDevice>>
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<BluetoothDevice> remote_device_to_connect_;
// Used for advertising.
RfcommServiceProvider rfcomm_provider_ = nullptr;
std::unique_ptr<BluetoothServerSocket> 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
@@ -15,31 +15,24 @@
#include "internal/platform/implementation/windows/bluetooth_classic_server_socket.h"
#include <codecvt>
#include <functional>
#include <locale>
#include <memory>
#include <string>
#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<api::BluetoothSocket> 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> 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<BluetoothSocket>(bluetooth_socket);
}
Exception BluetoothServerSocket::StartListening(bool radioDiscoverable) {
EnterCriticalSection(&critical_section_);
void BluetoothServerSocket::SetCloseNotifier(std::function<void()> 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<BluetoothSocket>(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
@@ -17,13 +17,15 @@
#include <Windows.h>
#include <functional>
#include <memory>
#include <queue>
#include <string>
#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<void()> 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<std::string> 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<std::unique_ptr<BluetoothSocket>> bluetooth_sockets_;
mutable absl::Mutex mutex_;
absl::CondVar cond_;
std::deque<StreamSocket> 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<void()> close_notifier_ = nullptr;
// IP addresses of the computer. mDNS uses them to advertise.
std::vector<std::string> 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
@@ -12,6 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#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<api::WifiHotspotSocket> 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<api::WifiHotspotSocket> 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<WifiHotspotSocket>(wifi_hotspot_socket);
}
@@ -70,6 +85,8 @@ void WifiHotspotServerSocket::SetCloseNotifier(std::function<void()> 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<std::string> WifiHotspotServerSocket::GetIpAddresses() const {
std::vector<std::string> result{};
auto host_names = NetworkInformation::GetHostNames();
@@ -12,6 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <windows.h>
#include <functional>
#include <memory>
#include <string>
#include <vector>
#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<api::WifiLanSocket> 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<api::WifiLanSocket> 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<WifiLanSocket>(wifi_lan_socket);
}
@@ -75,6 +92,8 @@ void WifiLanServerSocket::SetCloseNotifier(std::function<void()> 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<std::string> WifiLanServerSocket::GetIpAddresses() const {
std::vector<std::string> result{};
auto host_names = NetworkInformation::GetHostNames();
@@ -170,7 +202,7 @@ std::vector<std::string> 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];