diff --git a/cpp/platform/impl/windows/BUILD b/cpp/platform/impl/windows/BUILD index e1d65ef3..d07e44ac 100644 --- a/cpp/platform/impl/windows/BUILD +++ b/cpp/platform/impl/windows/BUILD @@ -36,6 +36,7 @@ cc_library( "scheduled_executor.h", "settable_future.h", "submittable_executor.h", + "utils.h", ], compatible_with = ["//buildenv/target:non_prod"], deps = [ @@ -113,6 +114,7 @@ cc_library( "//platform/api:types", "//platform/impl/shared:file", "//platform/impl/windows/generated:types", + "//platform/public:types", ], ) diff --git a/cpp/platform/impl/windows/bluetooth_classic_device.cc b/cpp/platform/impl/windows/bluetooth_classic_device.cc index c1c01a0e..1b88796f 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_device.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_device.cc @@ -22,6 +22,8 @@ #include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.h" +#include "platform/impl/windows/utils.h" + namespace location { namespace nearby { namespace windows { @@ -29,10 +31,14 @@ namespace windows { BluetoothDevice::~BluetoothDevice() {} BluetoothDevice::BluetoothDevice( - const winrt::Windows::Devices::Bluetooth::BluetoothDevice &bluetoothDevice) + const winrt::Windows::Devices::Bluetooth::BluetoothDevice& bluetoothDevice) : windows_bluetooth_device_(bluetoothDevice) { id_ = winrt::to_string(bluetoothDevice.DeviceId()); - mac_address_ = bluetoothDevice.BluetoothAddress(); + + // Get the device address. + auto bluetoothAddress = bluetoothDevice.BluetoothAddress(); + + mac_address_ = uint64_to_mac_address_string(bluetoothAddress); } // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() @@ -41,8 +47,18 @@ std::string BluetoothDevice::GetName() const { } // Returns BT MAC address assigned to this device. -// TODO(b/184975123): replace with real implementation. -std::string BluetoothDevice::GetMacAddress() const { return "Un-implemented"; } +std::string BluetoothDevice::GetMacAddress() const { return mac_address_; } + +// We are using Uncached because: +// For the following APIs, Cached means only use values cached in the system +// cached (if not cached then don't fall back to querying the device). +// The device may be present, but not entered into the cache yet, so always +// check the actual device. +IAsyncOperation +BluetoothDevice::GetRfcommServicesForIdAsync(const RfcommServiceId serviceId) { + return windows_bluetooth_device_.GetRfcommServicesForIdAsync( + serviceId, BluetoothCacheMode::Uncached); +} } // namespace windows } // namespace nearby diff --git a/cpp/platform/impl/windows/bluetooth_classic_device.h b/cpp/platform/impl/windows/bluetooth_classic_device.h index 605ed276..2f3ffbae 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_device.h +++ b/cpp/platform/impl/windows/bluetooth_classic_device.h @@ -21,6 +21,7 @@ #include "platform/base/exception.h" #include "platform/base/input_stream.h" #include "platform/base/output_stream.h" + #include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.h" #include "platform/impl/windows/generated/winrt/Windows.Devices.Enumeration.h" #include "platform/impl/windows/generated/winrt/base.h" @@ -29,6 +30,24 @@ namespace location { namespace nearby { namespace windows { +// Represents an asynchronous operation, which returns a result upon completion. +// https://docs.microsoft.com/en-us/uwp/api/windows.foundation.iasyncoperation-1?view=winrt-20348 +using winrt::Windows::Foundation::IAsyncOperation; + +// The result of an Rfcomm device service request. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservicesresult?view=winrt-20348 +using winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceServicesResult; + +// 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; + +// Indicates whether applicable Bluetooth API methods should operate on values +// cached in the system, or whether they should retrieve those values from the +// Bluetooth device. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothcachemode?view=winrt-20348 +using winrt::Windows::Devices::Bluetooth::BluetoothCacheMode; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. class BluetoothDevice : public api::BluetoothDevice { public: @@ -49,6 +68,12 @@ class BluetoothDevice : public api::BluetoothDevice { std::string GetId() { return id_; } + winrt::Windows::Foundation::IAsyncOperation< + winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceServicesResult> + GetRfcommServicesForIdAsync( + const winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommServiceId + serviceId); + private: // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothdevice?view=winrt-20348 winrt::Windows::Devices::Bluetooth::BluetoothDevice windows_bluetooth_device_; diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.cc b/cpp/platform/impl/windows/bluetooth_classic_medium.cc index 547c081a..41d0d29c 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.cc @@ -14,18 +14,22 @@ #include "platform/impl/windows/bluetooth_classic_medium.h" +#include "platform/base/exception.h" #include "platform/impl/windows/bluetooth_classic_device.h" +#include "platform/impl/windows/bluetooth_classic_socket.h" +#include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h" #include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.h" #include "platform/impl/windows/generated/winrt/Windows.Devices.Enumeration.h" #include "platform/impl/windows/generated/winrt/Windows.Foundation.Collections.h" #include "platform/impl/windows/generated/winrt/base.h" +#include "platform/public/logging.h" namespace location { namespace nearby { namespace windows { BluetoothClassicMedium::BluetoothClassicMedium() { - InitializeCriticalSection(&critical_section); + InitializeCriticalSection(&critical_section_); // create watcher device_watcher_ = DeviceInformation::CreateWatcher( @@ -36,32 +40,35 @@ BluetoothClassicMedium::BluetoothClassicMedium() { // handles only the added event, it will not receive an update if a device is // added to the system after the initial device enumeration completes. // register event handlers before starting the watcher + // + // Event that is raised when a device is added to the collection enumerated + // by the DeviceWatcher. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.added?view=winrt-20348 - device_watcher_.Added(winrt::auto_revoke, - {this, &BluetoothClassicMedium::DeviceWatcher_Added}); + device_watcher_.Added({this, &BluetoothClassicMedium::DeviceWatcher_Added}); + // Event that is raised when a device is updated in the collection of + // enumerated devices. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.updated?view=winrt-20348 device_watcher_.Updated( - winrt::auto_revoke, {this, &BluetoothClassicMedium::DeviceWatcher_Updated}); + // Event that is raised when a device is removed from the collection of + // enumerated devices. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher.removed?view=winrt-20348 device_watcher_.Removed( - winrt::auto_revoke, {this, &BluetoothClassicMedium::DeviceWatcher_Removed}); } BluetoothClassicMedium::~BluetoothClassicMedium() {} // TODO(b/184975123): replace with real implementation. -location::nearby::api::BluetoothDevice* GetRemoteDevice( - const std::string& mac_address) { +api::BluetoothDevice* GetRemoteDevice(const std::string& mac_address) { return nullptr; } bool BluetoothClassicMedium::StartDiscovery( BluetoothClassicMedium::DiscoveryCallback discovery_callback) { - EnterCriticalSection(&critical_section); + EnterCriticalSection(&critical_section_); bool result = false; discovery_callback_ = discovery_callback; @@ -70,13 +77,13 @@ bool BluetoothClassicMedium::StartDiscovery( result = StartScanning(); } - LeaveCriticalSection(&critical_section); + LeaveCriticalSection(&critical_section_); return result; } bool BluetoothClassicMedium::StopDiscovery() { - EnterCriticalSection(&critical_section); + EnterCriticalSection(&critical_section_); bool result = false; @@ -84,19 +91,131 @@ bool BluetoothClassicMedium::StopDiscovery() { result = StopScanning(); } - LeaveCriticalSection(&critical_section); + LeaveCriticalSection(&critical_section_); return result; } -std::unique_ptr -BluetoothClassicMedium::ConnectToService( - location::nearby::api::BluetoothDevice& remote_device, - const std::string& service_uuid, - location::nearby::CancellationFlag* cancellation_flag) { - return nullptr; +std::unique_ptr BluetoothClassicMedium::ConnectToService( + api::BluetoothDevice& remote_device, const std::string& service_uuid, + CancellationFlag* cancellation_flag) { + if (service_uuid.empty()) { + return nullptr; + } + + if (cancellation_flag == nullptr) { + return nullptr; + } + + winrt::guid service(service_uuid); + + BluetoothDevice* currentDevice = + dynamic_cast(&remote_device); + + if (currentDevice == nullptr) { + return nullptr; + } + + winrt::hstring deviceId = winrt::to_hstring(currentDevice->GetId()); + + if (!HaveAccess(deviceId)) { + return nullptr; + } + + RfcommDeviceService requestedService( + GetRequestedService(currentDevice, service)); + + if (!CheckSdp(requestedService)) { + return nullptr; + } + + device_watcher_.Stop(); + + EnterCriticalSection(&critical_section_); + + std::unique_ptr rfcommSocket = + std::make_unique(); + + try { + rfcommSocket + ->ConnectAsync(requestedService.ConnectionHostName(), + requestedService.ConnectionServiceName()) + .get(); // .get forces synchonicity so we know if it fails immediately + } catch (std::exception exception) { + // We will log and eat the exception since the caller + // expects nullptr if it fails + NEARBY_LOGS(ERROR) << __func__ << ": Exception connecting bluetooth async: " + << exception.what(); + + LeaveCriticalSection(&critical_section_); + + return nullptr; + } + + LeaveCriticalSection(&critical_section_); + + return rfcommSocket; } +bool BluetoothClassicMedium::HaveAccess(winrt::hstring deviceId) { + DeviceAccessStatus accessStatus = + DeviceAccessInformation::CreateFromId(deviceId).CurrentStatus(); + + if (accessStatus == DeviceAccessStatus::DeniedByUser || + // This status is most likely caused by app permissions (did not declare + // the device in the app's package.appxmanifest) + // This status does not cover the case where the device is already opened + // by another app. + accessStatus == DeviceAccessStatus::DeniedBySystem || + // Most likely the device is opened by another app, but cannot be sure + accessStatus == DeviceAccessStatus::Unspecified) { + return false; + } + + return true; +} + +RfcommDeviceService BluetoothClassicMedium::GetRequestedService( + BluetoothDevice* device, winrt::guid service) { + RfcommServiceId rfcommServiceId = RfcommServiceId::FromUuid(service); + + // Retrieves all Rfcomm Services on the Remote Bluetooth Device matching the + // specified RfcommServiceId. + // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothdevice.getrfcommservicesforidasync?view=winrt-20348 + IAsyncOperation rfcommServices = + device->GetRfcommServicesForIdAsync(rfcommServiceId); + + RfcommDeviceService requestedService(nullptr); + + if (rfcommServices.get().Services().Size() > 0) { + requestedService = rfcommServices.get().Services().GetAt(0); + } else { + return nullptr; + } + + return requestedService; +} + +bool BluetoothClassicMedium::CheckSdp(RfcommDeviceService requestedService) { + // Do various checks of the SDP record to make sure you are talking to a + // device that actually supports the Bluetooth Rfcomm Service + // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservice.getsdprawattributesasync?view=winrt-20348 + auto attributes = /*await*/ requestedService.GetSdpRawAttributesAsync(); + if (!attributes.get().HasKey(SdpServiceNameAttributeId)) { + return false; + } + + auto attributeReader = DataReader::FromBuffer( + attributes.get().Lookup(SdpServiceNameAttributeId)); + + auto attributeType = attributeReader.ReadByte(); + + if (attributeType != SdpServiceNameAttributeType) { + return false; + } + + return true; +} // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord // // service_uuid is the canonical textual representation @@ -107,14 +226,14 @@ BluetoothClassicMedium::ConnectToService( // // Returns nullptr error. // TODO(b/184975123): replace with real implementation. -std::unique_ptr +std::unique_ptr BluetoothClassicMedium::ListenForService(const std::string& service_name, const std::string& service_uuid) { return nullptr; } // TODO(b/184975123): replace with real implementation. -location::nearby::api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( +api::BluetoothDevice* BluetoothClassicMedium::GetRemoteDevice( const std::string& mac_address) { return nullptr; } @@ -149,6 +268,7 @@ bool BluetoothClassicMedium::StopScanning() { winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( DeviceWatcher sender, DeviceInformation deviceInfo) { if (IsWatcherStarted()) { + // Represents a Bluetooth device. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.bluetoothdevice?view=winrt-20348 std::unique_ptr windowsBluetoothDevice; @@ -166,14 +286,14 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( [this, deviceInfo, wbd = std::move(windowsBluetoothDevice)]( auto&& async, winrt::Windows::Foundation::AsyncStatus status) { - EnterCriticalSection(&critical_section); + EnterCriticalSection(&critical_section_); std::unique_ptr bluetoothDevice = std::make_unique(async.GetResults()); devices_by_id_[deviceInfo.Id()] = std::move(bluetoothDevice); - LeaveCriticalSection(&critical_section); + LeaveCriticalSection(&critical_section_); if (discovery_callback_.device_discovered_cb != nullptr) { discovery_callback_.device_discovered_cb( @@ -189,7 +309,7 @@ winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Added( winrt::fire_and_forget BluetoothClassicMedium::DeviceWatcher_Updated( DeviceWatcher sender, DeviceInformationUpdate deviceInfoUpdate) { if (IsWatcherStarted()) { - // Check for device name change + // TODO(jfcarroll): Check for device name change } return winrt::fire_and_forget(); diff --git a/cpp/platform/impl/windows/bluetooth_classic_medium.h b/cpp/platform/impl/windows/bluetooth_classic_medium.h index 37e6ea0f..b79fed92 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium.h +++ b/cpp/platform/impl/windows/bluetooth_classic_medium.h @@ -26,17 +26,49 @@ namespace location { namespace nearby { namespace windows { +// Represents a device. This class allows access to well-known device properties +// as well as additional properties specified during device enumeration. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceinformation?view=winrt-20348 using winrt::Windows::Devices::Enumeration::DeviceInformation; + +// Represents the kind of DeviceInformation object. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceinformationkind?view=winrt-20348 using winrt::Windows::Devices::Enumeration::DeviceInformationKind; + +// Contains updated properties for a DeviceInformation object. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceinformationupdate?view=winrt-20348 using winrt::Windows::Devices::Enumeration::DeviceInformationUpdate; + +// Enumerates devices dynamically, so that the app receives notifications if +// devices are added, removed, or changed after the initial enumeration is +// complete. // https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.devicewatcher?view=winrt-20348 using winrt::Windows::Devices::Enumeration::DeviceWatcher; + +// 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; +// Represents an instance of a service on a Bluetooth basic rate device. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.bluetooth.rfcomm.rfcommdeviceservice?view=winrt-20348 +using winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService; + +// Indicates the status of the access to a device. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceaccessstatus?view=winrt-20348 +using winrt::Windows::Devices::Enumeration::DeviceAccessStatus; + +// Contains the information about access to a device. +// https://docs.microsoft.com/en-us/uwp/api/windows.devices.enumeration.deviceaccessinformation?view=winrt-20348 +using winrt::Windows::Devices::Enumeration::DeviceAccessInformation; + +// 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; + +// 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; + // Bluetooth protocol ID = \"{e0cbf06c-cd8b-4647-bb8a-263b43f0f974}\" // https://docs.microsoft.com/en-us/windows/uwp/devices-sensors/aep-service-class-ids #define BLUETOOTH_SELECTOR \ @@ -44,8 +76,7 @@ using winrt::Windows::Devices::Enumeration::DeviceWatcherStatus; // Container of operations that can be performed over the Bluetooth Classic // medium. -class BluetoothClassicMedium - : public location::nearby::api::BluetoothClassicMedium { +class BluetoothClassicMedium : public api::BluetoothClassicMedium { public: explicit BluetoothClassicMedium(); @@ -75,12 +106,10 @@ class BluetoothClassicMedium // UUID. // // On success, returns a new BluetoothSocket. - // On error, returns nullptr. - // TODO(b/184975123): replace with real implementation. - std::unique_ptr ConnectToService( - location::nearby::api::BluetoothDevice& remote_device, - const std::string& service_uuid, - location::nearby::CancellationFlag* cancellation_flag) override; + // On error, throw's an exception + std::unique_ptr ConnectToService( + api::BluetoothDevice& remote_device, const std::string& service_uuid, + CancellationFlag* cancellation_flag) override; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord // @@ -92,12 +121,12 @@ class BluetoothClassicMedium // // Returns nullptr error. // TODO(b/184975123): replace with real implementation. - std::unique_ptr - ListenForService(const std::string& service_name, - const std::string& service_uuid) override; + std::unique_ptr ListenForService( + const std::string& service_name, + const std::string& service_uuid) override; // TODO(b/184975123): replace with real implementation. - location::nearby::api::BluetoothDevice* GetRemoteDevice( + api::BluetoothDevice* GetRemoteDevice( const std::string& mac_address) override; private: @@ -106,6 +135,8 @@ class BluetoothClassicMedium bool IsWatcherStarted(); bool IsWatcherRunning(); + // This is for a coroutine whose return type is winrt::fire_and_forget, which + // handles async operations which don't have any dependencies. // https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/fire-and-forget winrt::fire_and_forget DeviceWatcher_Added(DeviceWatcher sender, DeviceInformation deviceInfo); @@ -116,14 +147,39 @@ class BluetoothClassicMedium winrt::fire_and_forget DeviceWatcher_Removed( DeviceWatcher sender, DeviceInformationUpdate deviceInfo); + // Check to make sure we can connect if we try + bool HaveAccess(winrt::hstring deviceId); + + // Get the service requested + winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService + GetRequestedService(location::nearby::windows::BluetoothDevice* device, + winrt::guid service); + + // Check to see that the device actually handles the requested service + bool CheckSdp(winrt::Windows::Devices::Bluetooth::Rfcomm::RfcommDeviceService + requestedService); + BluetoothClassicMedium::DiscoveryCallback discovery_callback_; DeviceWatcher device_watcher_ = nullptr; + + // The Id of the Service Name SDP attribute + const uint16 SdpServiceNameAttributeId = 0x100; + + // The SDP Type of the Service Name SDP attribute. + // The first byte in the SDP Attribute encodes the SDP Attribute Type as + // follows : + // - the Attribute Type size in the least significant 3 bits, + // - the SDP Attribute Type value in the most significant 5 bits. + const char SdpServiceNameAttributeType = (4 << 3) | 5; + + // hstring is the only type of string winrt understands. // https://docs.microsoft.com/en-us/uwp/cpp-ref-for-winrt/hstring std::map> 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; + CRITICAL_SECTION critical_section_; }; } // 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 89cde980..cc737f96 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_medium_test.cc @@ -19,9 +19,12 @@ #include -#include "gtest/gtest.h" +#include "platform/impl/windows/generated/winrt/Windows.Devices.Bluetooth.Rfcomm.h" +#include "absl/strings/str_format.h" #include "platform/impl/windows/bluetooth_classic_device.h" +#include "gtest/gtest.h" + // 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. // @@ -38,7 +41,15 @@ void device_discovered_cb(location::nearby::api::BluetoothDevice& device) { std::map::const_iterator it = deviceList.find(address); + std::string buffer = + absl::StrFormat("processing device %s : ", address.c_str()); + + OutputDebugStringA(buffer.c_str()); + if (it == deviceList.end()) { + buffer = absl::StrFormat("adding device %s\n", address.c_str()); + + OutputDebugStringA(buffer.c_str()); deviceList[device.GetMacAddress()] = &device; } } @@ -60,9 +71,14 @@ TEST(TestCaseName, TestName) { .device_lost_cb = device_lost_cb, }); - while (true) { - Sleep(1000); - } + Sleep(30000); + + auto currentDevice = deviceList["DC:DC:E2:F2:B5:99"]; + auto serviceUuid = winrt::Windows::Devices::Bluetooth::Rfcomm:: + RfcommServiceId::GenericFileTransfer(); + location::nearby::CancellationFlag cancellationFlag; + bcm->ConnectToService(*currentDevice, "a82efa21-ae5c-3dde-9bbc-f16da7b16c5a", + &cancellationFlag); EXPECT_EQ(1, 1); EXPECT_TRUE(true); diff --git a/cpp/platform/impl/windows/bluetooth_classic_socket.cc b/cpp/platform/impl/windows/bluetooth_classic_socket.cc index c231502d..dbc8ab7e 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_socket.cc +++ b/cpp/platform/impl/windows/bluetooth_classic_socket.cc @@ -14,6 +14,8 @@ #include "platform/impl/windows/bluetooth_classic_socket.h" +#include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h" + namespace location { namespace nearby { namespace windows { @@ -25,6 +27,8 @@ BluetoothSocket::~BluetoothSocket() {} // returned by BluetoothClassicMedium::ConnectToService() for client side or // BluetoothServerSocket::Accept() for server side of connection. +BluetoothSocket::BluetoothSocket() { windows_socket_ = StreamSocket(); } + // Returns the InputStream of this connected BluetoothSocket. // TODO(b/184975123): replace with real implementation. InputStream& BluetoothSocket::GetInputStream() { @@ -49,6 +53,16 @@ Exception BluetoothSocket::Close() { return Exception(); } // TODO(b/184975123): replace with real implementation. api::BluetoothDevice* BluetoothSocket::GetRemoteDevice() { return nullptr; } +// Starts an asynchronous operation on a StreamSocket object to connect to a +// remote network destination specified by a remote hostname and a remote +// service name. +winrt::Windows::Foundation::IAsyncAction BluetoothSocket::ConnectAsync( + HostName connectionHostName, winrt::hstring connectionServiceName) { + // https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.streamsocket.connectasync?view=winrt-20348 + return windows_socket_.ConnectAsync(connectionHostName, + connectionServiceName); +} + } // namespace windows } // namespace nearby } // namespace location diff --git a/cpp/platform/impl/windows/bluetooth_classic_socket.h b/cpp/platform/impl/windows/bluetooth_classic_socket.h index 18d88d71..5c8d9ac0 100644 --- a/cpp/platform/impl/windows/bluetooth_classic_socket.h +++ b/cpp/platform/impl/windows/bluetooth_classic_socket.h @@ -15,14 +15,28 @@ #ifndef PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SOCKET_H_ #define PLATFORM_IMPL_WINDOWS_BLUETOOTH_CLASSIC_SOCKET_H_ +#include "platform/impl/windows/generated/winrt/Windows.Foundation.h" +#include "platform/impl/windows/generated/winrt/Windows.Networking.Sockets.h" +#include "platform/impl/windows/generated/winrt/Windows.Storage.Streams.h" + #include "platform/api/bluetooth_classic.h" namespace location { namespace nearby { namespace windows { + +// Provides data for a hostname or an IP address. +// https://docs.microsoft.com/en-us/uwp/api/windows.networking.hostname?view=winrt-20348 +using winrt::Windows::Networking::HostName; + +// Supports network communication using a stream socket over Bluetooth RFCOMM. +// https://docs.microsoft.com/en-us/uwp/api/windows.networking.sockets.streamsocket?view=winrt-20348 +using winrt::Windows::Networking::Sockets::StreamSocket; + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. class BluetoothSocket : public api::BluetoothSocket { public: + BluetoothSocket(); // TODO(b/184975123): replace with real implementation. ~BluetoothSocket() override; @@ -52,6 +66,10 @@ class BluetoothSocket : public api::BluetoothSocket { // TODO(b/184975123): replace with real implementation. api::BluetoothDevice* GetRemoteDevice() override; + // Connect asynchronously to the target remote device + winrt::Windows::Foundation::IAsyncAction ConnectAsync( + HostName connectionHostName, winrt::hstring connectionServiceName); + private: class FakeInputStream : public InputStream { public: @@ -73,6 +91,8 @@ class BluetoothSocket : public api::BluetoothSocket { }; FakeInputStream fake_input_stream_; FakeOutputStream fake_output_stream_; + + StreamSocket windows_socket_; }; } // namespace windows diff --git a/cpp/platform/impl/windows/utils.cc b/cpp/platform/impl/windows/utils.cc new file mode 100644 index 00000000..db9bff65 --- /dev/null +++ b/cpp/platform/impl/windows/utils.cc @@ -0,0 +1,36 @@ +// 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/utils.h" + +#include "absl/strings/ascii.h" +#include "absl/strings/str_format.h" + +namespace location { +namespace nearby { +namespace windows { + +std::string uint64_to_mac_address_string(uint64_t bluetoothAddress) { + std::string buffer = absl::StrFormat( + "%2llx:%2llx:%2llx:%2llx:%2llx:%2llx", bluetoothAddress >> 40, + (bluetoothAddress >> 32) & 0xff, (bluetoothAddress >> 24) & 0xff, + (bluetoothAddress >> 16) & 0xff, (bluetoothAddress >> 8) & 0xff, + bluetoothAddress & 0xff); + + return absl::AsciiStrToUpper(buffer); +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/cpp/platform/impl/windows/utils.h b/cpp/platform/impl/windows/utils.h new file mode 100644 index 00000000..962466ec --- /dev/null +++ b/cpp/platform/impl/windows/utils.h @@ -0,0 +1,30 @@ +// 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. + +#ifndef PLATFORM_IMPL_WINDOWS_UTILS_H_ +#define PLATFORM_IMPL_WINDOWS_UTILS_H_ + +#include + +namespace location { +namespace nearby { +namespace windows { + +std::string uint64_to_mac_address_string(uint64_t bluetoothAddress); + +} +} // namespace nearby +} // namespace location + +#endif // PLATFORM_IMPL_WINDOWS_UTILS_H_ diff --git a/cpp/platform/impl/windows/utils_test.cc b/cpp/platform/impl/windows/utils_test.cc new file mode 100644 index 00000000..c639e857 --- /dev/null +++ b/cpp/platform/impl/windows/utils_test.cc @@ -0,0 +1,31 @@ +// 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/utils.h" + +#include "gtest/gtest.h" + +using location::nearby::windows::uint64_to_mac_address_string; + +TEST(UtilsTests, MacAddressToString) { + // Arrange + const uint64_t input = 0x000034363bc78c71; + std::string expected = "34:36:3B:C7:8C:71"; + + // Act + std::string result = uint64_to_mac_address_string(input); + + // Assert + EXPECT_EQ(result, expected); +} diff --git a/cpp/platform/public/BUILD b/cpp/platform/public/BUILD index 6ff6995d..45951c4a 100644 --- a/cpp/platform/public/BUILD +++ b/cpp/platform/public/BUILD @@ -54,6 +54,7 @@ cc_library( "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", "//core:__subpackages__", "//platform/base:__pkg__", + "//platform/impl/windows:__subpackages__", "//platform/public:__pkg__", ], deps = [