diff --git a/internal/platform/implementation/windows/BUILD b/internal/platform/implementation/windows/BUILD index 0e90af77..b6a958a1 100644 --- a/internal/platform/implementation/windows/BUILD +++ b/internal/platform/implementation/windows/BUILD @@ -48,6 +48,9 @@ cc_library( name = "comm", hdrs = [ "ble.h", + "ble_medium.h", + "ble_peripheral.h", + "ble_socket.h", "ble_v2.h", "bluetooth_adapter.h", "bluetooth_classic.h", @@ -100,6 +103,8 @@ cc_library( cc_library( name = "windows", srcs = [ + "ble_medium.cc", + "ble_socket.cc", "ble_v2.cc", "bluetooth_adapter.cc", "bluetooth_classic_device.cc", @@ -166,6 +171,7 @@ cc_test( srcs = [ "atomic_boolean_test.cc", "atomic_reference_test.cc", + "ble_medium_test.cc", "ble_v2_test.cc", "count_down_latch_test.cc", "crypto_test.cc", diff --git a/internal/platform/implementation/windows/ble.h b/internal/platform/implementation/windows/ble.h index 807b4e72..ea8cc176 100644 --- a/internal/platform/implementation/windows/ble.h +++ b/internal/platform/implementation/windows/ble.h @@ -1,4 +1,4 @@ -// Copyright 2020 Google LLC +// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,141 +15,8 @@ #ifndef PLATFORM_IMPL_WINDOWS_BLE_H_ #define PLATFORM_IMPL_WINDOWS_BLE_H_ -#include "internal/platform/implementation/ble.h" -#include "internal/platform/exception.h" -#include "internal/platform/input_stream.h" -#include "internal/platform/output_stream.h" - -namespace location { -namespace nearby { -namespace windows { - -// Opaque wrapper over a BLE peripheral. Must contain enough data about a -// particular BLE device to connect to its GATT server. -class BlePeripheral : public api::BlePeripheral { - public: - // TODO(b/184975123): replace with real implementation. - ~BlePeripheral() override = default; - - // TODO(b/184975123): replace with real implementation. - std::string GetName() const override { return std::string{""}; } - // TODO(b/184975123): replace with real implementation. - ByteArray GetAdvertisementBytes( - const std::string& service_id) const override { - return ByteArray{}; - } -}; - -class BleSocket : public api::BleSocket { - public: - // TODO(b/184975123): replace with real implementation. - ~BleSocket() override; - - // Returns the InputStream of the BleSocket. - // On error, returned stream will report Exception::kIo on any operation. - // - // The returned object is not owned by the caller, and can be invalidated once - // the BleSocket object is destroyed. - // TODO(b/184975123): replace with real implementation. - InputStream& GetInputStream() override { return fake_input_stream_; } - - // Returns the OutputStream of the BleSocket. - // On error, returned stream will report Exception::kIo on any operation. - // - // The returned object is not owned by the caller, and can be invalidated once - // the BleSocket object is destroyed. - // TODO(b/184975123): replace with real implementation. - OutputStream& GetOutputStream() override { return fake_output_stream_; } - - // Conforms to the same contract as - // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#close(). - // - // Returns Exception::kIo on error, Exception::kSuccess otherwise. - // TODO(b/184975123): replace with real implementation. - Exception Close() override { return Exception{}; } - - // Returns valid BlePeripheral pointer if there is a connection, and - // nullptr otherwise. - // TODO(b/184975123): replace with real implementation. - BlePeripheral* GetRemotePeripheral() override { return nullptr; } - - // Unhooked InputStream & OutputStream for empty implementation. - // TODO(b/184975123): replace with real implementation. - private: - class FakeInputStream : public InputStream { - ~FakeInputStream() override = default; - ExceptionOr Read(std::int64_t size) override { - return ExceptionOr(Exception::kFailed); - } - Exception Close() override { return {.value = Exception::kFailed}; } - }; - class FakeOutputStream : public OutputStream { - ~FakeOutputStream() override = default; - - Exception Write(const ByteArray& data) override { - return {.value = Exception::kFailed}; - } - Exception Flush() override { return {.value = Exception::kFailed}; } - Exception Close() override { return {.value = Exception::kFailed}; } - }; - FakeInputStream fake_input_stream_; - FakeOutputStream fake_output_stream_; -}; - -// Container of operations that can be performed over the BLE medium. -class BleMedium : public api::BleMedium { - public: - // TODO(b/184975123): replace with real implementation. - ~BleMedium() override = default; - - // TODO(b/184975123): replace with real implementation. - bool StartAdvertising( - const std::string& service_id, const ByteArray& advertisement_bytes, - const std::string& fast_advertisement_service_uuid) override { - return false; - } - // TODO(b/184975123): replace with real implementation. - bool StopAdvertising(const std::string& service_id) override { return false; } - - // Returns true once the BLE scan has been initiated. - // TODO(b/184975123): replace with real implementation. - bool StartScanning(const std::string& service_id, - const std::string& fast_advertisement_service_uuid, - DiscoveredPeripheralCallback callback) override { - return false; - } - - // Returns true once BLE scanning for service_id is well and truly stopped; - // after this returns, there must be no more invocations of the - // DiscoveredPeripheralCallback passed in to StartScanning() for service_id. - // TODO(b/184975123): replace with real implementation. - bool StopScanning(const std::string& service_id) override { return false; } - - // Returns true once BLE socket connection requests to service_id can be - // accepted. - // TODO(b/184975123): replace with real implementation. - bool StartAcceptingConnections(const std::string& service_id, - AcceptedConnectionCallback callback) override { - return false; - } - // TODO(b/184975123): replace with real implementation. - bool StopAcceptingConnections(const std::string& service_id) override { - return false; - } - - // Connects to a BLE peripheral. - // On success, returns a new BleSocket. - // On error, returns nullptr. - // TODO(b/184975123): replace with real implementation. - std::unique_ptr Connect( - api::BlePeripheral& peripheral, const std::string& service_id, - CancellationFlag* cancellation_flag) override { - return nullptr; - } -}; - -} // namespace windows -} // namespace nearby -} // namespace location +#include "internal/platform/implementation/windows/ble_peripheral.h" +#include "internal/platform/implementation/windows/ble_medium.h" +#include "internal/platform/implementation/windows/ble_socket.h" #endif // PLATFORM_IMPL_WINDOWS_BLE_H_ diff --git a/internal/platform/implementation/windows/ble_medium.cc b/internal/platform/implementation/windows/ble_medium.cc new file mode 100644 index 00000000..536f85bd --- /dev/null +++ b/internal/platform/implementation/windows/ble_medium.cc @@ -0,0 +1,354 @@ +// Copyright 2022 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 "internal/platform/implementation/windows/ble_medium.h" + +#include + +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/ble.h" +#include "internal/platform/logging.h" +#include "winrt/Windows.Devices.Bluetooth.Advertisement.h" +#include "winrt/Windows.Devices.Bluetooth.h" +#include "winrt/Windows.Foundation.Collections.h" +#include "winrt/Windows.Storage.Streams.h" + +namespace location { +namespace nearby { +namespace windows { +namespace { +using ::winrt::Windows::Devices::Bluetooth::BluetoothError; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisement; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementDataSection; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementDataTypes; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementPublisher; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementPublisherStatus; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementPublisherStatusChangedEventArgs; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementReceivedEventArgs; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcher; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcherStatus; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcherStoppedEventArgs; +using ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEScanningMode; +using ::winrt::Windows::Storage::Streams::DataWriter; + +template +using IVector = winrt::Windows::Foundation::Collections::IVector; +} // namespace + +BleMedium::BleMedium(api::BluetoothAdapter& adapter) + : adapter_(static_cast(&adapter)) {} + +bool BleMedium::StartAdvertising( + const std::string& service_id, const ByteArray& advertisement_bytes, + const std::string& fast_advertisement_service_uuid) { + absl::MutexLock lock(&mutex_); + + NEARBY_LOGS(INFO) << "Windows Ble StartAdvertising: service_id=" << service_id + << ", advertisement bytes=" << advertisement_bytes.data() + << "(" << advertisement_bytes.size() << ")," + << " fast advertisement service uuid=" + << fast_advertisement_service_uuid; + + DataWriter data_writer; + + for (int i = 0; i < advertisement_bytes.size(); ++i) { + data_writer.WriteByte( + static_cast(*(advertisement_bytes.data() + i))); + } + + BluetoothLEAdvertisementDataSection service_data = + BluetoothLEAdvertisementDataSection(0x16, data_writer.DetachBuffer()); + + IVector data_sections = + advertisement_.DataSections(); + data_sections.Append(service_data); + advertisement_.DataSections() = data_sections; + + publisher_ = BluetoothLEAdvertisementPublisher(advertisement_); + + publisher_token_ = + publisher_.StatusChanged({this, &BleMedium::PublisherHandler}); + + std::future publisher_state_future = + publisher_started_promise_.get_future(); + + publisher_.Start(); + + return publisher_state_future.get() == PublisherState::kStarted; +} + +bool BleMedium::StopAdvertising(const std::string& service_id) { + absl::MutexLock lock(&mutex_); + + NEARBY_LOGS(INFO) << "Windows Ble StopAdvertising: service_id=" << service_id; + + std::future publisher_state_future = + publisher_stopped_promise_.get_future(); + + publisher_.Stop(); + + return publisher_state_future.get() == PublisherState::kStopped; +} + +bool BleMedium::StartScanning( + const std::string& service_id, + const std::string& fast_advertisement_service_uuid, + DiscoveredPeripheralCallback callback) { + absl::MutexLock lock(&mutex_); + + NEARBY_LOGS(INFO) << "Windows Ble StartScanning: service_id=" << service_id; + + advertisement_received_callback_ = std::move(callback); + + watcher_token_ = watcher_.Stopped({this, &BleMedium::WatcherHandler}); + advertisement_received_token_ = + watcher_.Received({this, &BleMedium::AdvertisementReceivedHandler}); + + std::future watcher_state_future = + watcher_started_promise_.get_future(); + + // Active mode indicates that scan request packets will be sent to query for + // Scan Response + watcher_.ScanningMode(BluetoothLEScanningMode::Active); + watcher_.Start(); + + while (!is_watcher_started_) { + if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Created) { + watcher_started_promise_.set_value(WatcherState::kStarted); + return true; + } + } + return true; +} + +bool BleMedium::StopScanning(const std::string& service_id) { + absl::MutexLock lock(&mutex_); + + NEARBY_LOGS(INFO) << "Windows Ble StopScanning: service_id=" << service_id; + + std::future watcher_state_future = + watcher_stopped_promise_.get_future(); + + watcher_.Stop(); + + while (!is_watcher_stopped_) { + if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped) { + watcher_stopped_promise_.set_value(WatcherState::kStopped); + watcher_.Stopped(watcher_token_); + watcher_.Received(advertisement_received_token_); + return true; + } + } + return true; +} + +bool BleMedium::StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) { + NEARBY_LOGS(INFO) << "Windows Ble StartAcceptingConnections: service_id=" + << service_id; + return true; +} + +bool BleMedium::StopAcceptingConnections(const std::string& service_id) { + NEARBY_LOGS(INFO) << "Windows Ble StopAcceptingConnections: service_id=" + << service_id; + return true; +} + +std::unique_ptr BleMedium::Connect( + api::BlePeripheral& remote_peripheral, const std::string& service_id, + CancellationFlag* cancellation_flag) { + if (cancellation_flag->Cancelled()) { + NEARBY_LOGS(ERROR) << "Windows BLE Connect: Has been cancelled: " + "service_id=" + << service_id; + return {}; + } + + NEARBY_LOGS(ERROR) << "Windows Ble Connect: Cannot connect over BLE socket. " + "service_id=" + << service_id; + return {}; +} + +void BleMedium::PublisherHandler( + BluetoothLEAdvertisementPublisher publisher, + BluetoothLEAdvertisementPublisherStatusChangedEventArgs args) { + switch (args.Status()) { + case BluetoothLEAdvertisementPublisherStatus::Started: + publisher_started_promise_.set_value(PublisherState::kStarted); + break; + case BluetoothLEAdvertisementPublisherStatus::Stopped: + publisher_stopped_promise_.set_value(PublisherState::kStopped); + publisher_.StatusChanged(publisher_token_); + break; + case BluetoothLEAdvertisementPublisherStatus::Aborted: + switch (args.Error()) { + case BluetoothError::RadioNotAvailable: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to " + "radio not available."; + break; + case BluetoothError::ResourceInUse: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium advertising failed due to resource in use."; + break; + case BluetoothError::DisabledByPolicy: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to " + "disabled by policy."; + break; + case BluetoothError::DisabledByUser: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to " + "disabled by user."; + break; + case BluetoothError::NotSupported: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to " + "hardware not supported."; + break; + case BluetoothError::TransportNotSupported: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to " + "transport not supported."; + break; + case BluetoothError::ConsentRequired: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium advertising failed due to " + "consent required."; + break; + case BluetoothError::OtherError: + default: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium advertising failed due to unknown errors."; + break; + } + publisher_started_promise_.set_value(PublisherState::kError); + publisher_stopped_promise_.set_value(PublisherState::kError); + break; + default: + break; + } +} + +void BleMedium::WatcherHandler( + BluetoothLEAdvertisementWatcher watcher, + BluetoothLEAdvertisementWatcherStoppedEventArgs args) { + switch (args.Error()) { + case BluetoothError::RadioNotAvailable: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to radio not available."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::ResourceInUse: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to resource in use."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::DisabledByPolicy: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to disabled by policy."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::DisabledByUser: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to disabled by user."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::NotSupported: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to hardware not supported."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::TransportNotSupported: + NEARBY_LOGS(ERROR) << "Nearby BLE Medium scanning failed due to " + "transport not supported."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::ConsentRequired: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to consent required."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + case BluetoothError::OtherError: + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to unknown errors."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + break; + default: + if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Started) { + watcher_started_promise_.set_value(WatcherState::kStarted); + is_watcher_started_ = true; + } + if (watcher_.Status() == BluetoothLEAdvertisementWatcherStatus::Stopped) { + watcher_stopped_promise_.set_value(WatcherState::kStopped); + watcher_.Stopped(watcher_token_); + watcher_.Received(advertisement_received_token_); + is_watcher_stopped_ = true; + } else { + NEARBY_LOGS(ERROR) + << "Nearby BLE Medium scanning failed due to unknown errors."; + watcher_started_promise_.set_value(WatcherState::kError); + watcher_stopped_promise_.set_value(WatcherState::kError); + } + break; + } +} + +void BleMedium::AdvertisementReceivedHandler( + BluetoothLEAdvertisementWatcher watcher, + BluetoothLEAdvertisementReceivedEventArgs args) { + // Handle all BLE advertisements and determine whether the BLE Medium + // Advertisement Scan Response packet (containing Copresence UUID 0xFEF3) has + // been received in the handler + std::array bluetooth_base_array = { + static_cast(0x80), static_cast(0x00), + static_cast(0x00), static_cast(0x80), + static_cast(0x5F), static_cast(0x9B), + static_cast(0x34), static_cast(0xFB)}; + winrt::guid kCopresenceServiceUuid128bit( + static_cast(0x0000FEF3), static_cast(0x0000), + static_cast(0x1000), bluetooth_base_array); + + IVector guids = args.Advertisement().ServiceUuids(); + bool is_advertisement_found = false; + for (const winrt::guid& uuid : guids) { + if (uuid == kCopresenceServiceUuid128bit) { + is_advertisement_found = true; + } + } + if (is_advertisement_found == true) { + BlePeripheral peripheral; + advertisement_received_callback_.peripheral_discovered_cb(peripheral, + "\xfe\xf3", true); + } +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/windows/ble_medium.h b/internal/platform/implementation/windows/ble_medium.h new file mode 100644 index 00000000..6841734b --- /dev/null +++ b/internal/platform/implementation/windows/ble_medium.h @@ -0,0 +1,133 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_MEDIUM_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_MEDIUM_H_ + +#include + +#include +#include // NOLINT +#include + +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/ble.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/windows/ble.h" +#include "winrt/Windows.Devices.Bluetooth.Advertisement.h" + +namespace location { +namespace nearby { +namespace windows { + +// Only Fast Advertisement is supported where the remote device's static +// bluetooth MAC address is resolved from Nearby Share Contact certificates +// using salt and encrypted_metadata_key from the BLE advertisement packet. +// The MAC address is then used directly to establish a Bluetooth Classic +// RFCOMM socket. + +// Container of operations that can be performed over the BLE medium. +class BleMedium : public api::BleMedium { + public: + explicit BleMedium(api::BluetoothAdapter& adapter); + ~BleMedium() override = default; + + bool StartAdvertising( + const std::string& service_id, const ByteArray& advertisement_bytes, + const std::string& fast_advertisement_service_uuid) override + ABSL_LOCKS_EXCLUDED(mutex_); + + bool StopAdvertising(const std::string& service_id) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true once the BLE scan has been initiated. + bool StartScanning(const std::string& service_id, + const std::string& fast_advertisement_service_uuid, + DiscoveredPeripheralCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true once BLE scanning for service_id is well and truly stopped; + // after this returns, there must be no more invocations of the + // DiscoveredPeripheralCallback passed in to StartScanning() for service_id. + bool StopScanning(const std::string& service_id) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true once BLE socket connection requests to service_id can be + // accepted. + bool StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + + bool StopAcceptingConnections(const std::string& service_id) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Connects to a BLE peripheral. + // On success, returns a new BleSocket. + // On error, returns nullptr. + std::unique_ptr Connect( + api::BlePeripheral& peripheral, const std::string& service_id, + CancellationFlag* cancellation_flag) override ABSL_LOCKS_EXCLUDED(mutex_); + + private: + enum class PublisherState { kStarted = 0, kStopped, kError }; + + enum class WatcherState { kStarted = 0, kStopped, kError }; + + absl::Mutex mutex_; + api::BluetoothAdapter* adapter_; + ByteArray advertisement_byte_ ABSL_GUARDED_BY(mutex_); + + DiscoveredPeripheralCallback advertisement_received_callback_; + + // WinRT objects + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementPublisher publisher_; + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcher watcher_; + ::winrt::Windows::Devices::Bluetooth::Advertisement::BluetoothLEAdvertisement + advertisement_; + + void PublisherHandler( + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementPublisher publisher, + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementPublisherStatusChangedEventArgs args); + void AdvertisementReceivedHandler( + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcher watcher, + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementReceivedEventArgs args); + void WatcherHandler(::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcher watcher, + ::winrt::Windows::Devices::Bluetooth::Advertisement:: + BluetoothLEAdvertisementWatcherStoppedEventArgs args); + + ::winrt::event_token publisher_token_; + std::promise publisher_started_promise_; + std::promise publisher_stopped_promise_; + + ::winrt::event_token watcher_token_; + std::promise watcher_started_promise_; + std::promise watcher_stopped_promise_; + bool is_watcher_started_ = false; + bool is_watcher_stopped_ = false; + + ::winrt::event_token advertisement_received_token_; +}; + +} // namespace windows +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_MEDIUM_H_ diff --git a/internal/platform/implementation/windows/ble_medium_test.cc b/internal/platform/implementation/windows/ble_medium_test.cc new file mode 100644 index 00000000..79e04d7b --- /dev/null +++ b/internal/platform/implementation/windows/ble_medium_test.cc @@ -0,0 +1,203 @@ +// Copyright 2022 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 "internal/platform/implementation/windows/ble_medium.h" + +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/synchronization/notification.h" +#include "internal/platform/implementation/ble.h" +#include "internal/platform/implementation/bluetooth_adapter.h" +#include "internal/platform/implementation/windows/ble.h" +#include "internal/platform/implementation/windows/bluetooth_adapter.h" + +namespace location { +namespace nearby { +namespace windows { +namespace { + +constexpr uint8_t kCopresenceServiceUuid[] = {0xfe, 0xf3}; +constexpr uint8_t kVersionBitmask = 0xE0; +constexpr uint8_t kSocketVersionBitmask = 0x1C; +constexpr uint8_t kFastAdvertisementFlagBitmask = 0x02; +constexpr uint8_t kPcpBitmask = 0x1F; +constexpr uint8_t kVisibilityBitmask = 0x01; + +TEST(BleMedium, DISABLED_StartAdvertising) { + BluetoothAdapter bluetoothAdapter; + BleMedium ble_medium(bluetoothAdapter); + + std::array advertising_data_byte_array; + + // (2 bytes) 16-bit Service UUID 0xf3fe + advertising_data_byte_array.at(0) = + static_cast(kCopresenceServiceUuid[1]); + advertising_data_byte_array.at(1) = + static_cast(kCopresenceServiceUuid[0]); + + // (1 byte) version [3-bits] + socket_version [3-bits] + + // fast_advertisement_flag [1-bit] + reserved [1-bit] + uint8_t ble_medium_version = 0x02; // mediums::BleAdvertisement::Version::kV2 + uint8_t socket_version = + 0x02; // mediums::BleAdvertisement::SocketVersion::kV2 + bool fast_advertisement = true; // Is Fast Advertisement + + uint8_t ble_medium_advertisement_metadata_byte = + (ble_medium_version << 5) & kVersionBitmask; + ble_medium_advertisement_metadata_byte |= + (socket_version << 2) & kSocketVersionBitmask; + ble_medium_advertisement_metadata_byte |= + ((fast_advertisement ? 1 : 0) << 1) & kFastAdvertisementFlagBitmask; + + advertising_data_byte_array.at(2) = + static_cast(ble_medium_advertisement_metadata_byte); + + // (1 byte) body_length + advertising_data_byte_array.at(3) = static_cast( + 0x19); // always 25 bytes for Fast Advertisement + + // (1 byte) Nearby Connection version [3-bits] + pcp [5-bits] + uint8_t ble_connections_version = + 0x01; // connections::BleAdvertisement::Version::kV1 + uint8_t pcp = 0x02; // connections::Pcp::kP2pCluster + + uint8_t ble_connections_advertisement_metadata_byte = + (ble_connections_version << 5) & kVersionBitmask; + ble_connections_advertisement_metadata_byte |= pcp & kPcpBitmask; + advertising_data_byte_array.at(4) = + static_cast(ble_connections_advertisement_metadata_byte); + + // (4 bytes) endpoint_id + for (int i = 0; i < 4; ++i) { + advertising_data_byte_array.at(5 + i) = static_cast(0x00); + } + + // (1 byte) endpoint_info_size + advertising_data_byte_array.at(9) = static_cast( + 0x11); // always 17-bytes for Fast Advertisement + + // =========endpoint_info [17-bytes]============ + // (1 byte) Nearby Share version [3-bits] + visibility [1-bit] + reserved + // [4-bits] + uint8_t nearby_share_version = 0x00; // nearby share v1 + uint8_t visibility = 0x00; // [placeholder] sharing::Visibility::kAllContacts + + uint8_t nearby_share_metadata_byte = + (nearby_share_version << 5) & kVersionBitmask; + nearby_share_metadata_byte |= ((visibility & kVisibilityBitmask) << 4); + + advertising_data_byte_array.at(10) = static_cast(0x00); + + // (2 bytes) salt + for (int i = 0; i < 2; ++i) { + advertising_data_byte_array.at(11 + i) = static_cast(0x00); + } + + // (14 bytes) encrypted_metadata_key + for (int i = 0; i < 14; ++i) { + advertising_data_byte_array.at(13 + i) = static_cast(0x00); + } + // =========endpoint_info [17-bytes]============ + + // (2 bytes) device_token + for (int i = 0; i < 2; ++i) { + advertising_data_byte_array.at(27 + i) = static_cast(0x00); + } + + ByteArray advertising_data(advertising_data_byte_array); + + EXPECT_TRUE( + ble_medium.StartAdvertising("NearbyShare", advertising_data, "\xfe\xf3")); +} + +TEST(BleMedium, DISABLED_StopAdvertising) { + BluetoothAdapter bluetoothAdapter; + BleMedium ble_medium(bluetoothAdapter); + + std::array advertising_data_byte_array; + + // (2 bytes) 16-bit Service UUID 0xf3fe + advertising_data_byte_array.at(0) = + static_cast(kCopresenceServiceUuid[1]); + advertising_data_byte_array.at(1) = + static_cast(kCopresenceServiceUuid[0]); + + ByteArray advertising_data(advertising_data_byte_array); + + EXPECT_TRUE( + ble_medium.StartAdvertising("NearbyShare", advertising_data, "\xfe\xf3")); + EXPECT_TRUE(ble_medium.StopAdvertising("NearbyShare")); +} + +TEST(BleMedium, DISABLED_StartScanning) { + BluetoothAdapter bluetoothAdapter; + BleMedium ble_medium(bluetoothAdapter); + + BleMedium::DiscoveredPeripheralCallback discovered_peripheral_callback = { + .peripheral_discovered_cb = [this](api::BlePeripheral& peripheral, + const std::string& service_id, + bool fast_advertisement) {}, + .peripheral_lost_cb = [this](api::BlePeripheral& peripheral, + const std::string& service_id) {}}; + + EXPECT_TRUE(ble_medium.StartScanning("NearbyShare", "\xfe\xf3", + discovered_peripheral_callback)); +} + +TEST(BleMedium, DISABLED_ReceiveAdvertisement) { + absl::Notification advertisement_received_notification; + + BluetoothAdapter bluetoothAdapter; + BleMedium ble_medium(bluetoothAdapter); + + BleMedium::DiscoveredPeripheralCallback discovered_peripheral_callback = { + .peripheral_discovered_cb = + [this, &advertisement_received_notification]( + api::BlePeripheral& peripheral, const std::string& service_id, + bool fast_advertisement) { + advertisement_received_notification.Notify(); + }, + .peripheral_lost_cb = [this](api::BlePeripheral& peripheral, + const std::string& service_id) {}}; + + EXPECT_TRUE(ble_medium.StartScanning("NearbyShare", "\xfe\xf3", + discovered_peripheral_callback)); + EXPECT_TRUE( + advertisement_received_notification.WaitForNotificationWithTimeout( + absl::Seconds(5))); +} + +TEST(BleMedium, DISABLED_StopScanning) { + BluetoothAdapter bluetoothAdapter; + BleMedium ble_medium(bluetoothAdapter); + + BleMedium::DiscoveredPeripheralCallback discovered_peripheral_callback = { + .peripheral_discovered_cb = + [this](api::BlePeripheral& peripheral, const std::string& service_id, + bool fast_advertisement) { EXPECT_TRUE(fast_advertisement); }, + .peripheral_lost_cb = [this](api::BlePeripheral& peripheral, + const std::string& service_id) {}}; + + EXPECT_TRUE(ble_medium.StartScanning("NearbyShare", "\xfe\xf3", + discovered_peripheral_callback)); + EXPECT_TRUE(ble_medium.StopScanning("NearbyShare")); +} + +} // namespace +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/windows/ble_peripheral.h b/internal/platform/implementation/windows/ble_peripheral.h new file mode 100644 index 00000000..e2d7b879 --- /dev/null +++ b/internal/platform/implementation/windows/ble_peripheral.h @@ -0,0 +1,50 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_PERIPHERAL_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_PERIPHERAL_H_ + +#include + +#include "internal/platform/implementation/ble.h" + +namespace location { +namespace nearby { +namespace windows { + +// TODO(b/184975123): Implement BLE Peripheral +// This is just a fake stub to appease the BLE Medium abstraction. Windows +// does not support BLE GATT-based advertising/discovery and socket currently, +// so a BlePeripheral is not required. The remote device is recognized as a +// BluetoothClassicDevice instead. + +// Opaque wrapper over a BLE peripheral. Must contain enough data about a +// particular BLE device to connect to its GATT server. +class BlePeripheral : public api::BlePeripheral { + public: + ~BlePeripheral() override = default; + + std::string GetName() const override { return ""; } + + ByteArray GetAdvertisementBytes( + const std::string& service_id) const override { + return ByteArray{}; + } +}; + +} // namespace windows +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_PERIPHERAL_H_ diff --git a/internal/platform/implementation/windows/ble_socket.cc b/internal/platform/implementation/windows/ble_socket.cc new file mode 100644 index 00000000..1bb516b3 --- /dev/null +++ b/internal/platform/implementation/windows/ble_socket.cc @@ -0,0 +1,49 @@ +// Copyright 2022 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 "internal/platform/implementation/windows/ble_socket.h" + +#include "absl/synchronization/mutex.h" +#include "internal/platform/implementation/ble.h" + +namespace location { +namespace nearby { +namespace windows { + +InputStream& BleSocket::GetInputStream() { + return fake_input_stream_; +} + +OutputStream& BleSocket::GetOutputStream() { + return fake_output_stream_; +} + +bool BleSocket::IsClosed() const { + absl::MutexLock lock(&mutex_); + return closed_; +} + +Exception BleSocket::Close() { + absl::MutexLock lock(&mutex_); + return {Exception::kSuccess}; +} + +BlePeripheral* BleSocket::GetRemotePeripheral() { + absl::MutexLock lock(&mutex_); + return peripheral_; +} + +} // namespace windows +} // namespace nearby +} // namespace location diff --git a/internal/platform/implementation/windows/ble_socket.h b/internal/platform/implementation/windows/ble_socket.h new file mode 100644 index 00000000..a1232ef7 --- /dev/null +++ b/internal/platform/implementation/windows/ble_socket.h @@ -0,0 +1,83 @@ +// Copyright 2022 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 THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_SOCKET_H_ +#define THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_SOCKET_H_ + +#include "internal/platform/implementation/ble.h" +#include "internal/platform/implementation/windows/ble.h" +#include "internal/platform/exception.h" +#include "internal/platform/input_stream.h" +#include "internal/platform/output_stream.h" + +namespace location { +namespace nearby { +namespace windows { + +// TODO(b/184975123): Implement BLE Weave Socket +// This is just a fake stub to appease the BLE Medium abstraction. Windows +// does not support BLE Weave sockets currently. BLE is only used in the +// pre-connection phase handshake (advertising & discovering), connection is +// delegated to Bluetooth Classic Medium to establish RFCOMM socket. +class BleSocket : public api::BleSocket { + public: + ~BleSocket() override; + + // Returns the InputStream of this connected BleSocket. + InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns the OutputStream of this connected BleSocket. + // This stream is for local side to write. + OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true if socket is closed. + bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns valid BlePeripheral pointer if there is a connection, and + // nullptr otherwise. + BlePeripheral* GetRemotePeripheral() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Unhooked InputStream & OutputStream for empty implementation. + private: + class FakeInputStream : public InputStream { + ~FakeInputStream() override = default; + ExceptionOr Read(std::int64_t size) override { + return ExceptionOr(Exception::kFailed); + } + Exception Close() override { return {.value = Exception::kFailed}; } + }; + class FakeOutputStream : public OutputStream { + ~FakeOutputStream() override = default; + + Exception Write(const ByteArray& data) override { + return {.value = Exception::kFailed}; + } + Exception Flush() override { return {.value = Exception::kFailed}; } + Exception Close() override { return {.value = Exception::kFailed}; } + }; + FakeInputStream fake_input_stream_; + FakeOutputStream fake_output_stream_; + mutable absl::Mutex mutex_; + BlePeripheral* peripheral_; + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +} // namespace windows +} // namespace nearby +} // namespace location + +#endif // THIRD_PARTY_NEARBY_INTERNAL_PLATFORM_IMPLEMENTATION_WINDOWS_BLE_SOCKET_H_ diff --git a/internal/platform/implementation/windows/platform.cc b/internal/platform/implementation/windows/platform.cc index 0ad554fc..a74bf4d7 100644 --- a/internal/platform/implementation/windows/platform.cc +++ b/internal/platform/implementation/windows/platform.cc @@ -208,10 +208,9 @@ ImplementationPlatform::CreateBluetoothClassicMedium( return absl::make_unique(adapter); } -// TODO(b/184975123): replace with real implementation. std::unique_ptr ImplementationPlatform::CreateBleMedium( BluetoothAdapter& adapter) { - return absl::make_unique(); + return absl::make_unique(adapter); } // TODO(b/184975123): replace with real implementation.