diff --git a/cpp/platform/impl/windows/bluetooth_adapter.cc b/cpp/platform/impl/windows/bluetooth_adapter.cc index cd3531fb..6ae127c4 100644 --- a/cpp/platform/impl/windows/bluetooth_adapter.cc +++ b/cpp/platform/impl/windows/bluetooth_adapter.cc @@ -90,16 +90,18 @@ BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const { // Synchronously sets the scan mode of the adapter, and returns true if the // operation was a success. -// TODO(jcarroll): Setup an event for this and hook the bluetooth medium into -// the event to allow for updates. bool BluetoothAdapter::SetScanMode(ScanMode scan_mode) { scan_mode_ = scan_mode; + + if (scan_mode_changed_ != nullptr) { + scan_mode_changed_(scan_mode); + } + return true; } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName() // Returns an empty string on error -// TODO(b/184975123): replace with real implementation. std::string BluetoothAdapter::GetName() const { char *instanceID = GetGenericBluetoothAdapterInstanceID(); diff --git a/cpp/platform/impl/windows/bluetooth_adapter.h b/cpp/platform/impl/windows/bluetooth_adapter.h index 42054fc0..2e57dc6e 100644 --- a/cpp/platform/impl/windows/bluetooth_adapter.h +++ b/cpp/platform/impl/windows/bluetooth_adapter.h @@ -25,60 +25,68 @@ namespace location { namespace nearby { namespace windows { - // Represents a Bluetooth adapter. - // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter?view=winrt-20348 - using winrt::Windows::Devices::Bluetooth::IBluetoothAdapter; +// Represents a Bluetooth adapter. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothadapter?view=winrt-20348 +using winrt::Windows::Devices::Bluetooth::IBluetoothAdapter; - // Represents a radio device on the system. - // https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio?view=winrt-20348 - using winrt::Windows::Devices::Radios::IRadio; +// Represents a radio device on the system. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radio?view=winrt-20348 +using winrt::Windows::Devices::Radios::IRadio; - // Enumeration that describes possible radio states. - // https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radiostate?view=winrt-20348 - using winrt::Windows::Devices::Radios::RadioState; +// Enumeration that describes possible radio states. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.radios.radiostate?view=winrt-20348 +using winrt::Windows::Devices::Radios::RadioState; - // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html +// https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html class BluetoothAdapter : public api::BluetoothAdapter { public: BluetoothAdapter(); - // TODO(b/184975123): replace with real implementation. ~BluetoothAdapter() override = default; + typedef std::function ScanModeCallback; + // Synchronously sets the status of the BluetoothAdapter to 'status', and // returns true if the operation was a success. bool SetStatus(Status status) override; + // Returns true if the BluetoothAdapter's current status is // Status::Value::kEnabled. bool IsEnabled() const override; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode() - // // Returns ScanMode::kUnknown on error. ScanMode GetScanMode() const override; + // Synchronously sets the scan mode of the adapter, and returns true if the // operation was a success. bool SetScanMode(ScanMode scan_mode) override; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName() // Returns an empty string on error - // TODO(b/184975123): replace with real implementation. std::string GetName() const override; + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String) bool SetName(absl::string_view name) override; // Returns BT MAC address assigned to this adapter. std::string GetMacAddress() const override; + void SetOnScanModeChanged(ScanModeCallback callback) { + if (scan_mode_changed_ == nullptr) { + scan_mode_changed_ = callback; + } + } + private: - IBluetoothAdapter - windows_bluetooth_adapter_; + IBluetoothAdapter windows_bluetooth_adapter_; IRadio windows_bluetooth_radio_; char *GetGenericBluetoothAdapterInstanceID(void) const; void find_and_replace(char *source, const char *strFind, const char *strReplace) const; ScanMode scan_mode_ = ScanMode::kNone; + ScanModeCallback scan_mode_changed_ = nullptr; }; } // namespace windows diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.cc b/cpp/platform/impl/windows/bluetooth_classic_medium.cc index b29e2c64..071d509d 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.cc @@ -14,7 +14,7 @@ #include "platform/impl/windows/bluetooth_classic_medium.h" -#include +#include #include #include @@ -42,16 +42,27 @@ namespace nearby { namespace windows { BluetoothClassicMedium::BluetoothClassicMedium( - const api::BluetoothAdapter& bluetoothAdapter) - : bluetooth_adapter_( - dynamic_cast(bluetoothAdapter)) { + api::BluetoothAdapter& bluetoothAdapter) + : bluetooth_adapter_(dynamic_cast(bluetoothAdapter)) { InitializeCriticalSection(&critical_section_); InitializeDeviceWatcher(); + + bluetooth_adapter_.SetOnScanModeChanged(std::bind( + &BluetoothClassicMedium::OnScanModeChanged, this, std::placeholders::_1)); } BluetoothClassicMedium::~BluetoothClassicMedium() {} +void BluetoothClassicMedium::OnScanModeChanged( + BluetoothAdapter::ScanMode scanMode) { + scan_mode_ = scanMode; + bool radioDiscoverable = bluetooth_adapter_.GetScanMode() == + BluetoothAdapter::ScanMode::kConnectableDiscoverable; + + bluetooth_server_socket_->SetScanMode(radioDiscoverable); +} + bool BluetoothClassicMedium::StartDiscovery( BluetoothClassicMedium::DiscoveryCallback discovery_callback) { EnterCriticalSection(&critical_section_); @@ -117,6 +128,7 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( api::BluetoothDevice& remote_device, const std::string& service_uuid, CancellationFlag* cancellation_flag) { if (service_uuid.empty()) { + NEARBY_LOGS(ERROR) << __func__ << ": service_uuid not specified."; return nullptr; } @@ -127,12 +139,15 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( // Must check for valid pattern as the guid constructor will throw on an // invalid format if (!regex_match(service_uuid, pattern)) { + NEARBY_LOGS(ERROR) << __func__ + << ": invalid service_uuid: " << service_uuid; return nullptr; } winrt::guid service(service_uuid); if (cancellation_flag == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": cancellation_flag not specified."; return nullptr; } @@ -140,12 +155,15 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( dynamic_cast(&remote_device); if (currentDevice == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to get current device."; return nullptr; } winrt::hstring deviceId = winrt::to_hstring(currentDevice->GetId()); if (!HaveAccess(deviceId)) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to gain access to device: " + << winrt::to_string(deviceId); return nullptr; } @@ -153,6 +171,7 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( GetRequestedService(currentDevice, service)); if (!CheckSdp(requestedService)) { + NEARBY_LOGS(ERROR) << __func__ << ": Invalid SDP."; return nullptr; } @@ -221,6 +240,7 @@ RfcommDeviceService BluetoothClassicMedium::GetRequestedService( if (rfcommServices.get().Services().Size() > 0) { requestedService = rfcommServices.get().Services().GetAt(0); } else { + NEARBY_LOGS(ERROR) << __func__ << ": No services found."; return nullptr; } @@ -233,6 +253,7 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) { // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservice.getsdprawattributesasync?view=winrt-20348 auto attributes = /*await*/ requestedService.GetSdpRawAttributesAsync(); if (!attributes.get().HasKey(Constants::SdpServiceNameAttributeId)) { + NEARBY_LOGS(ERROR) << __func__ << ": Missing SdpServiceNameAttributeId."; return false; } @@ -242,6 +263,7 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) { auto attributeType = attributeReader.ReadByte(); if (attributeType != Constants::SdpServiceNameAttributeType) { + NEARBY_LOGS(ERROR) << __func__ << ": Missing SdpServiceNameAttributeType."; return false; } @@ -256,27 +278,37 @@ bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) { // UUID. // // Returns nullptr error. -// TODO(b/184975123): replace with real implementation. std::unique_ptr BluetoothClassicMedium::ListenForService(const std::string& service_name, const std::string& service_uuid) { if (service_uuid.empty()) { + NEARBY_LOGS(ERROR) << __func__ << ": service_uuid was empty."; return nullptr; } if (service_name.empty()) { + NEARBY_LOGS(ERROR) << __func__ << ": service_name was empty."; return nullptr; } auto bluetooth_server_socket = - std::make_unique(); + std::make_unique( + service_name, service_uuid); - bool radioDiscoverable = - bluetooth_adapter_.GetScanMode() == - BluetoothAdapter::ScanMode::kConnectableDiscoverable; + if (bluetooth_server_socket == nullptr) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to create the server socket."; + return nullptr; + } - bluetooth_server_socket->StartListening(service_name, service_uuid, - radioDiscoverable); + bool radioDiscoverable = bluetooth_adapter_.GetScanMode() == + BluetoothAdapter::ScanMode::kConnectableDiscoverable; + + Exception result = bluetooth_server_socket->StartListening(radioDiscoverable); + + if (result.value != Exception::kSuccess) { + NEARBY_LOGS(ERROR) << __func__ << ": Failed to start listening."; + return nullptr; + } return std::move(bluetooth_server_socket); } @@ -301,6 +333,9 @@ bool BluetoothClassicMedium::StartScanning() { } } + NEARBY_LOGS(ERROR) + << __func__ + << ": Attempted to start scanning when watcher already started."; return false; } @@ -309,7 +344,9 @@ bool BluetoothClassicMedium::StopScanning() { device_watcher_.Stop(); return true; } - + NEARBY_LOGS(ERROR) + << __func__ + << ": Attempted to stop scanning when watcher already stopped."; return false; } diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.h b/cpp/platform/impl/windows/bluetooth_classic_medium.h index a00d2d24..09b39263 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.h +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.h @@ -16,10 +16,10 @@ #define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_MEDIUM_H_ #include "platform/api/bluetooth_classic.h" +#include "platform/impl/windows/bluetooth_adapter.h" #include "platform/impl/windows/bluetooth_classic_device.h" #include "platform/impl/windows/bluetooth_classic_server_socket.h" #include "platform/impl/windows/bluetooth_classic_socket.h" -#include "platform/impl/windows/bluetooth_adapter.h" #include "platform/impl/windows/generated/winrt/Windows.Devices.Enumeration.h" #include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h" #include "platform/impl/windows/generated/winrt/base.h" @@ -84,8 +84,7 @@ using winrt::Windows::Storage::Streams::DataWriter; // medium. class BluetoothClassicMedium : public api::BluetoothClassicMedium { public: - BluetoothClassicMedium() = default; - BluetoothClassicMedium(const api::BluetoothAdapter& bluetoothAdapter); + BluetoothClassicMedium(api::BluetoothAdapter& bluetoothAdapter); ~BluetoothClassicMedium() override; @@ -129,7 +128,6 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { const std::string& service_name, const std::string& service_uuid) override; - // TODO(b/184975123): replace with real implementation. api::BluetoothDevice* GetRemoteDevice( const std::string& mac_address) override; @@ -139,6 +137,7 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { bool IsWatcherStarted(); bool IsWatcherRunning(); void InitializeDeviceWatcher(); + void OnScanModeChanged(BluetoothAdapter::ScanMode scanMode); // This is for a coroutine whose return type is winrt::fire_and_forget, which // handles async operations which don't have any dependencies. @@ -167,6 +166,10 @@ 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_; // hstring is the only type of string winrt understands. // https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/hstring @@ -176,7 +179,9 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { // https://docs.microsoft.com/en-us/windows/win32/sync/critical-section-objects CRITICAL_SECTION critical_section_; - BluetoothAdapter bluetooth_adapter_; + BluetoothAdapter& bluetooth_adapter_; + + BluetoothAdapter::ScanMode scan_mode_; }; } // namespace windows diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc b/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc index 06bf3c48..63d3b01b 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc @@ -14,8 +14,8 @@ #include "platform/impl/windows/bluetooth_classic_medium.h" -#include #include +#include #include @@ -31,8 +31,8 @@ // TODO(jfcarroll): Find a way to mock winrt components in order to properly // unit test this. Once that's done, unit tests can be written, in a later C/L. // -// CAUTION: THIS IS NOT A REAL TEST, THIS EXERCISES THE SCANNER, IT DOES NOT -// STOP AND IS INTENDED SOLELY FOR DEBUG AND DEMONSTRATION PURPOSES. DO NOT +// CAUTION: THIS IS NOT A REAL TEST, THIS EXERCISES THE SCANNER, IT MAY NOT +// STOP/HANG AND IS INTENDED SOLELY FOR DEBUG AND DEMONSTRATION PURPOSES. DO NOT // ATTEMPT TO BUILD AND RUN THIS TEST ON GOOGLE3 YOU HAVE BEEN WARNED using location::nearby::windows::BluetoothDevice; @@ -88,6 +88,11 @@ TEST_F(BluetoothClassicMediumTests, ManualTest) { std::make_unique( bluetoothAdapter); + bluetoothAdapter.SetScanMode(location::nearby::windows::BluetoothAdapter:: + ScanMode::kConnectableDiscoverable); + + auto mode = bluetoothAdapter.GetScanMode(); + bcm->StartDiscovery( location::nearby::api::BluetoothClassicMedium::DiscoveryCallback{ .device_discovered_cb = device_discovered_cb, diff --git a/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc b/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc index 5af12b69..9c8c1350 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_server_socket.cc @@ -26,7 +26,12 @@ namespace location { namespace nearby { namespace windows { -BluetoothServerSocket::BluetoothServerSocket() : rfcomm_provider_(nullptr) { +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_); } @@ -61,12 +66,10 @@ std::unique_ptr BluetoothServerSocket::Accept() { return nullptr; } -Exception BluetoothServerSocket::StartListening(const std::string& service_name, - const std::string& service_uuid, - bool radioDiscoverable) { +Exception BluetoothServerSocket::StartListening(bool radioDiscoverable) { EnterCriticalSection(&critical_section_); - winrt::guid service(service_uuid); + radio_discoverable_ = radioDiscoverable; // Create the StreamSocketListener stream_socket_listener_ = StreamSocketListener(); @@ -97,18 +100,19 @@ Exception BluetoothServerSocket::StartListening(const std::string& service_name, try { auto rfcommProviderRef = - RfcommServiceProvider::CreateAsync(RfcommServiceId::FromUuid(service)) + RfcommServiceProvider::CreateAsync( + RfcommServiceId::FromUuid(winrt::guid(service_uuid_))) .get(); rfcomm_provider_ = &rfcommProviderRef; std::wstring_convert> converter; stream_socket_listener_ - .BindServiceNameAsync(winrt::to_hstring(service_name.c_str())) + .BindServiceNameAsync(winrt::to_hstring(service_name_.c_str())) .get(); // Set the SDP attributes and start Bluetooth advertising - InitializeServiceSdpAttributes(*rfcomm_provider_, service_name); + InitializeServiceSdpAttributes(*rfcomm_provider_, service_name_); } catch (std::exception exception) { // We will log and eat the exception since the caller // expects nullptr if it fails @@ -120,9 +124,18 @@ Exception BluetoothServerSocket::StartListening(const std::string& service_name, return {Exception::kFailed}; } + StartAdvertising(); + + LeaveCriticalSection(&critical_section_); + + return {Exception::kSuccess}; +} + +Exception BluetoothServerSocket::StartAdvertising() { try { rfcomm_provider_->StartAdvertising( - stream_socket_listener_.as(), radioDiscoverable); + stream_socket_listener_.as(), + radio_discoverable_); } catch (std::exception exception) { // We will log and eat the exception since the caller // expects nullptr if it fails @@ -134,11 +147,13 @@ Exception BluetoothServerSocket::StartListening(const std::string& service_name, return {Exception::kFailed}; } - LeaveCriticalSection(&critical_section_); - return {Exception::kSuccess}; } +void BluetoothServerSocket::StopAdvertising() { + rfcomm_provider_->StopAdvertising(); +} + void BluetoothServerSocket::InitializeServiceSdpAttributes( RfcommServiceProvider rfcommProvider, std::string service_name) { auto sdpWriter = DataWriter(); diff --git a/cpp/platform/impl/windows/bluetooth_classic_server_socket.h b/cpp/platform/impl/windows/bluetooth_classic_server_socket.h index bbb274af..b291ea3c 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_server_socket.h +++ b/cpp/platform/impl/windows/bluetooth_classic_server_socket.h @@ -67,9 +67,9 @@ using winrt::Windows::Storage::Streams::UnicodeEncoding; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. class BluetoothServerSocket : public api::BluetoothServerSocket { public: - BluetoothServerSocket(); + BluetoothServerSocket(const std::string service_name, + const std::string service_uuid); - // TODO(b/184975123): replace with real implementation. ~BluetoothServerSocket() override; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept() @@ -87,23 +87,34 @@ class BluetoothServerSocket : public api::BluetoothServerSocket { // Returns Exception::kIo on error, Exception::kSuccess otherwise. Exception Close() override; - Exception StartListening(const std::string& service_name, - const std::string& service_uuid, - bool radioDiscoverable); + Exception StartListening(bool radioDiscoverable); + + void SetScanMode(bool radioDiscoverable) { + radio_discoverable_ = radioDiscoverable; + StopAdvertising(); + StartAdvertising(); + } private: void InitializeServiceSdpAttributes(RfcommServiceProvider rfcommProvider, std::string service_name); + Exception StartAdvertising(); + void StopAdvertising(); + // 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_; StreamSocketListener stream_socket_listener_; - + winrt::event_token listener_token_; CRITICAL_SECTION critical_section_; bool closed_ = false; + bool radio_discoverable_; + + const std::string service_name_; + const std::string service_uuid_; RfcommServiceProvider* rfcomm_provider_; }; diff --git a/cpp/platform/impl/windows/bluetooth_classic_socket.h b/cpp/platform/impl/windows/bluetooth_classic_socket.h index d15aea01..39d357be 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_socket.h +++ b/cpp/platform/impl/windows/bluetooth_classic_socket.h @@ -70,7 +70,6 @@ class BluetoothSocket : public api::BluetoothSocket { winrt::Windows::Networking::Sockets::StreamSocket streamSocket) : windows_socket_(streamSocket) {} - // TODO(b/184975123): replace with real implementation. ~BluetoothSocket() override; // NOTE: @@ -88,7 +87,6 @@ class BluetoothSocket : public api::BluetoothSocket { // Closes both input and output streams, marks Socket as closed. // After this call object should be treated as not connected. // Returns Exception::kIo on error, Exception::kSuccess otherwise. - // TODO(b/184975123): replace with real implementation. Exception Close() override; // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() diff --git a/cpp/platform/impl/windows/platform.cc b/cpp/platform/impl/windows/platform.cc index f04940ef..b50fd5d3 100644 --- a/cpp/platform/impl/windows/platform.cc +++ b/cpp/platform/impl/windows/platform.cc @@ -56,7 +56,6 @@ std::unique_ptr ImplementationPlatform::CreateAtomicUint32( return absl::make_unique(); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateCountDownLatch( std::int32_t count) { return absl::make_unique(count); @@ -89,35 +88,30 @@ std::unique_ptr ImplementationPlatform::CreateLogMessage( return absl::make_unique(file, line, severity); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateSingleThreadExecutor() { return absl::make_unique(); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateMultiThreadExecutor( std::int32_t max_concurrency) { - return absl::make_unique(); + return absl::make_unique(max_concurrency); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateScheduledExecutor() { return absl::make_unique(); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateBluetoothAdapter() { return absl::make_unique(); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium( - BluetoothAdapter& adapter) { + nearby::api::BluetoothAdapter& adapter) { return absl::make_unique( adapter); }