// Copyright 2020 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // https://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "platform/impl/windows/bluetooth_classic_server_socket.h" #include #include #include #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(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() {} // 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. // 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. std::unique_ptr BluetoothServerSocket::Accept() { while (bluetooth_sockets_.empty() && !closed_) { Sleep(1000); } EnterCriticalSection(&critical_section_); if (!closed_) { std::unique_ptr 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(bool radioDiscoverable) { EnterCriticalSection(&critical_section_); radio_discoverable_ = radioDiscoverable; // 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(args.Socket())); } LeaveCriticalSection(&critical_section_); }); 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) .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}; } StartAdvertising(); LeaveCriticalSection(&critical_section_); return {Exception::kSuccess}; } 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(); 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_); closed_ = true; bluetooth_sockets_ = {}; rfcomm_provider_.StopAdvertising(); LeaveCriticalSection(&critical_section_); return {Exception::kSuccess}; } } // namespace windows } // namespace nearby } // namespace location