From e2a07376f407812a0f0156e7e756b00e74c0befc Mon Sep 17 00:00:00 2001 From: Himanshu Jaju Date: Thu, 4 Jun 2020 19:24:33 +0100 Subject: [PATCH 1/2] Roll forward to cl/314747126 Change-Id: I858d3227b44ca6d727b39fae95358df58fce9c1b --- cpp/core/internal/mediums/webrtc/BUILD | 8 +- .../mediums/webrtc/signaling_frames.h | 2 +- .../internal/mediums/webrtc/webrtc_socket.h | 2 +- .../mediums/webrtc/webrtc_socket_test.cc | 2 +- cpp/core_v2/core.cc | 2 + cpp/core_v2/internal/base_pcp_handler.cc | 3 + cpp/core_v2/internal/endpoint_manager.cc | 5 + cpp/core_v2/internal/mediums/BUILD | 10 +- .../internal/mediums/bluetooth_classic.cc | 377 ++++++++++++++++++ .../internal/mediums/bluetooth_classic.h | 178 +++++++++ .../mediums/bluetooth_classic_test.cc | 194 +++++++++ .../internal/mediums/bluetooth_radio.cc | 2 + cpp/core_v2/internal/mediums/mediums.cc | 17 + cpp/core_v2/internal/mediums/mediums.h | 40 ++ cpp/core_v2/internal/mediums/webrtc/BUILD | 8 +- .../internal/mediums/webrtc/connection_flow.h | 4 +- .../webrtc/local_ice_candidate_listener.h | 2 +- .../webrtc/peer_connection_observer_impl.h | 2 +- .../mediums/webrtc/signaling_frames.h | 2 +- .../internal/mediums/webrtc/webrtc_socket.h | 2 +- .../mediums/webrtc/webrtc_socket_test.cc | 2 +- cpp/core_v2/internal/offline_frames.cc | 2 +- cpp/platform/api/BUILD | 2 +- cpp/platform/api/webrtc.h | 2 +- cpp/platform_v2/api/BUILD | 2 +- cpp/platform_v2/api/platform.h | 8 +- cpp/platform_v2/api/webrtc.h | 2 +- cpp/platform_v2/base/medium_environment.cc | 21 + cpp/platform_v2/base/medium_environment.h | 11 + cpp/platform_v2/impl/g3/BUILD | 12 +- cpp/platform_v2/impl/g3/bluetooth_adapter.cc | 7 +- cpp/platform_v2/impl/g3/bluetooth_adapter.h | 4 + cpp/platform_v2/impl/g3/bluetooth_classic.cc | 240 +++++++++++ cpp/platform_v2/impl/g3/bluetooth_classic.h | 218 ++++++++++ cpp/platform_v2/impl/g3/platform.cc | 12 +- cpp/platform_v2/impl/g3/webrtc.cc | 2 +- cpp/platform_v2/impl/g3/webrtc.h | 2 +- cpp/platform_v2/public/BUILD | 15 +- cpp/platform_v2/public/bluetooth_adapter.h | 42 +- cpp/platform_v2/public/bluetooth_classic.cc | 85 ++++ cpp/platform_v2/public/bluetooth_classic.h | 205 ++++++++++ .../public/bluetooth_classic_test.cc | 197 +++++++++ cpp/platform_v2/public/webrtc.h | 2 +- 43 files changed, 1910 insertions(+), 47 deletions(-) create mode 100644 cpp/core_v2/internal/mediums/bluetooth_classic.cc create mode 100644 cpp/core_v2/internal/mediums/bluetooth_classic.h create mode 100644 cpp/core_v2/internal/mediums/bluetooth_classic_test.cc create mode 100644 cpp/core_v2/internal/mediums/mediums.cc create mode 100644 cpp/core_v2/internal/mediums/mediums.h create mode 100644 cpp/platform_v2/impl/g3/bluetooth_classic.cc create mode 100644 cpp/platform_v2/impl/g3/bluetooth_classic.h create mode 100644 cpp/platform_v2/public/bluetooth_classic.cc create mode 100644 cpp/platform_v2/public/bluetooth_classic.h create mode 100644 cpp/platform_v2/public/bluetooth_classic_test.cc diff --git a/cpp/core/internal/mediums/webrtc/BUILD b/cpp/core/internal/mediums/webrtc/BUILD index 56cf5608..5ab6e446 100644 --- a/cpp/core/internal/mediums/webrtc/BUILD +++ b/cpp/core/internal/mediums/webrtc/BUILD @@ -7,7 +7,7 @@ cc_library( deps = [ "//platform:utils", "//platform/api", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) @@ -20,7 +20,7 @@ cc_test( "//platform/api", "//platform/impl/g3", # buildcleaner: keep "//testing/base/public:gunit_main", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) @@ -45,7 +45,7 @@ cc_library( ":peer_id", "//platform:types", "//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) @@ -72,6 +72,6 @@ cc_test( "//platform/impl/g3", # buildcleaner: keep "//net/proto2/public:proto2", "//testing/base/public:gunit_main", - "//webrtc/pc:peerconnection", # buildcleaner: keep + "//webrtc/files/stable/webrtc/pc:peerconnection", # buildcleaner: keep ], ) diff --git a/cpp/core/internal/mediums/webrtc/signaling_frames.h b/cpp/core/internal/mediums/webrtc/signaling_frames.h index fb885a58..fec7046c 100644 --- a/cpp/core/internal/mediums/webrtc/signaling_frames.h +++ b/cpp/core/internal/mediums/webrtc/signaling_frames.h @@ -7,7 +7,7 @@ #include "platform/byte_array.h" #include "platform/ptr.h" #include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core/internal/mediums/webrtc/webrtc_socket.h b/cpp/core/internal/mediums/webrtc/webrtc_socket.h index d0ec4104..5a55e9d9 100644 --- a/cpp/core/internal/mediums/webrtc/webrtc_socket.h +++ b/cpp/core/internal/mediums/webrtc/webrtc_socket.h @@ -6,7 +6,7 @@ #include "platform/api/output_stream.h" #include "platform/api/socket.h" #include "platform/pipe.h" -#include "webrtc/api/data_channel_interface.h" +#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" namespace location { namespace nearby { diff --git a/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc b/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc index be83d9f1..503b8cd8 100644 --- a/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc +++ b/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc @@ -5,7 +5,7 @@ #include "platform/ptr.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "webrtc/api/data_channel_interface.h" +#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/core.cc b/cpp/core_v2/core.cc index c7848047..412f987c 100644 --- a/cpp/core_v2/core.cc +++ b/cpp/core_v2/core.cc @@ -12,6 +12,8 @@ namespace location { namespace nearby { namespace connections { +constexpr absl::Duration Core::kWaitForDisconnect; + Core::~Core() { CountDownLatch latch(1); router_.ClientDisconnecting( diff --git a/cpp/core_v2/internal/base_pcp_handler.cc b/cpp/core_v2/internal/base_pcp_handler.cc index e57801e6..38459a40 100644 --- a/cpp/core_v2/internal/base_pcp_handler.cc +++ b/cpp/core_v2/internal/base_pcp_handler.cc @@ -21,6 +21,9 @@ namespace connections { using ::location::nearby::proto::connections::Medium; using ::securegcm::UKey2Handshake; +constexpr absl::Duration BasePcpHandler::kConnectionRequestReadTimeout; +constexpr absl::Duration BasePcpHandler::kRejectedConnectionCloseDelay; + BasePcpHandler::BasePcpHandler(EndpointManager* endpoint_manager, EndpointChannelManager* channel_manager) : endpoint_manager_(endpoint_manager), channel_manager_(channel_manager) {} diff --git a/cpp/core_v2/internal/endpoint_manager.cc b/cpp/core_v2/internal/endpoint_manager.cc index 501df7a3..5d28a6c0 100644 --- a/cpp/core_v2/internal/endpoint_manager.cc +++ b/cpp/core_v2/internal/endpoint_manager.cc @@ -16,6 +16,11 @@ namespace connections { using ::location::nearby::proto::connections::Medium; +constexpr absl::Duration EndpointManager::kKeepAliveWriteInterval; +constexpr absl::Duration EndpointManager::kKeepAliveReadTimeout; +constexpr absl::Duration EndpointManager::kProcessEndpointDisconnectionTimeout; +constexpr absl::Time EndpointManager::kInvalidTimestamp; + // A Runnable that continuously grabs the most recent EndpointChannel available // for an endpoint. // diff --git a/cpp/core_v2/internal/mediums/BUILD b/cpp/core_v2/internal/mediums/BUILD index c15fc9bb..cba5c8d8 100644 --- a/cpp/core_v2/internal/mediums/BUILD +++ b/cpp/core_v2/internal/mediums/BUILD @@ -6,7 +6,9 @@ cc_library( "ble_advertisement_header.cc", "ble_packet.cc", "bloom_filter.cc", + "bluetooth_classic.cc", "bluetooth_radio.cc", + "mediums.cc", "uuid.cc", ], hdrs = [ @@ -16,14 +18,17 @@ cc_library( "ble_packet.h", "ble_peripheral.h", "bloom_filter.h", + "bluetooth_classic.h", "bluetooth_radio.h", "lost_entity_tracker.h", + "mediums.h", "uuid.h", ], visibility = [ - "//core_v2/internal:__pkg__", + "//core_v2/internal:__subpackages__", ], deps = [ + "//core_v2:core_types", "//platform_v2/base", "//platform_v2/public:comm", "//platform_v2/public:logging", @@ -53,6 +58,7 @@ cc_library( cc_test( name = "core_v2_internal_mediums_test", + size = "small", srcs = [ "advertisement_read_result_test.cc", "ble_advertisement_header_test.cc", @@ -60,6 +66,7 @@ cc_test( "ble_packet_test.cc", "ble_peripheral_test.cc", "bloom_filter_test.cc", + "bluetooth_classic_test.cc", "bluetooth_radio_test.cc", "lost_entity_tracker_test.cc", "uuid_test.cc", @@ -68,6 +75,7 @@ cc_test( deps = [ ":mediums", "//platform_v2/base", + "//platform_v2/base:test_util", "//platform_v2/impl/g3", # build_cleaner: keep "//platform_v2/public:comm", "//platform_v2/public:logging", diff --git a/cpp/core_v2/internal/mediums/bluetooth_classic.cc b/cpp/core_v2/internal/mediums/bluetooth_classic.cc new file mode 100644 index 00000000..39ebda64 --- /dev/null +++ b/cpp/core_v2/internal/mediums/bluetooth_classic.cc @@ -0,0 +1,377 @@ +#include "core_v2/internal/mediums/bluetooth_classic.h" + +#include +#include +#include + +#include "core_v2/internal/mediums/uuid.h" +#include "platform_v2/public/logging.h" +#include "platform_v2/public/mutex_lock.h" + +namespace location { +namespace nearby { +namespace connections { + +BluetoothClassic::BluetoothClassic(BluetoothRadio& radio) : radio_(radio) {} + +BluetoothClassic::~BluetoothClassic() { + // Destructor is not taking locks, but methods it is calling are. + StopDiscovery(); + while (!server_sockets_.empty()) { + StopAcceptingConnections(server_sockets_.begin()->first); + } + TurnOffDiscoverability(); + + // All the AcceptLoopRunnable objects in here should already have gotten an + // opportunity to shut themselves down cleanly in the calls to + // StopAcceptingConnections() above. + accept_loops_runner_.Shutdown(); +} + +bool BluetoothClassic::IsAvailable() const { + MutexLock lock(&mutex_); + + return IsAvailableLocked(); +} + +bool BluetoothClassic::IsAvailableLocked() const { + return medium_.IsValid() && adapter_.IsValid(); +} + +bool BluetoothClassic::TurnOnDiscoverability(const std::string& device_name) { + MutexLock lock(&mutex_); + + if (device_name.empty()) { + NEARBY_LOG(INFO, + "Refusing to turn on BT discoverability. Empty device name."); + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOG(INFO, "Can't turn on BT discoverability. BT is off."); + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOG(INFO, "Can't turn on BT discoverability. BT is not available."); + return false; + } + + if (IsDiscoverable()) { + NEARBY_LOG(INFO, + "Refusing to turn on BT discoverability; new name='%s'; " + "current name='%s'", + device_name.c_str(), adapter_.GetName().c_str()); + return false; + } + + if (!ModifyDeviceName(device_name)) { + NEARBY_LOG(INFO, + "Failed to turn on BT discoverability; " + "failed to set name to %s", + device_name.c_str()); + return false; + } + + if (!ModifyScanMode(ScanMode::kConnectableDiscoverable)) { + NEARBY_LOG(INFO, + "Failed to turn on BT discoverability; " + "failed to set scan_mode to %d", + ScanMode::kConnectableDiscoverable); + + // Don't forget to perform this rollback of the partial state changes we've + // made til now. + RestoreDeviceName(); + return false; + } + + NEARBY_LOG(INFO, "Turned on BT discoverability with device_name=%s", + device_name.c_str()); + return true; +} + +bool BluetoothClassic::TurnOffDiscoverability() { + MutexLock lock(&mutex_); + + if (!IsDiscoverable()) { + NEARBY_LOG(INFO, "Can't turn off BT discoverability; it is already off"); + return false; + } + + RestoreScanMode(); + RestoreDeviceName(); + + NEARBY_LOG(INFO, "Turned Bluetooth discoverability off"); + return true; +} + +bool BluetoothClassic::IsDiscoverable() const { + return (!original_device_name_.empty() && + (adapter_.GetScanMode() == ScanMode::kConnectableDiscoverable)); +} + +bool BluetoothClassic::ModifyDeviceName(const std::string& device_name) { + if (original_device_name_.empty()) { + original_device_name_ = adapter_.GetName(); + } + + return adapter_.SetName(device_name); +} + +bool BluetoothClassic::ModifyScanMode(ScanMode scan_mode) { + if (original_scan_mode_ == ScanMode::kUnknown) { + original_scan_mode_ = adapter_.GetScanMode(); + } + + if (!adapter_.SetScanMode(scan_mode)) { + original_scan_mode_ = ScanMode::kUnknown; + return false; + } + + return true; +} + +bool BluetoothClassic::RestoreScanMode() { + if (original_scan_mode_ == ScanMode::kUnknown || + !adapter_.SetScanMode(original_scan_mode_)) { + NEARBY_LOG(INFO, "Failed to restore original Bluetooth scan mode to %d", + original_scan_mode_); + return false; + } + + // Regardless of whether or not we could actually restore the Bluetooth scan + // mode, reset our relevant state. + original_scan_mode_ = ScanMode::kUnknown; + return true; +} + +bool BluetoothClassic::RestoreDeviceName() { + if (original_device_name_.empty() || + !adapter_.SetName(original_device_name_)) { + NEARBY_LOG(INFO, "Failed to restore original Bluetooth device name to %s", + original_device_name_.c_str()); + return false; + } + original_device_name_.clear(); + return true; +} + +bool BluetoothClassic::StartDiscovery(DiscoveredDeviceCallback callback) { + MutexLock lock(&mutex_); + + if (!radio_.IsEnabled()) { + NEARBY_LOG(INFO, "Can't discover BT devices because BT isn't enabled."); + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOG(INFO, "Can't discover BT devices because BT isn't available."); + return false; + } + + if (IsDiscovering()) { + NEARBY_LOG(INFO, + "Refusing to start discovery of BT devices because another " + "discovery is already in-progress."); + return false; + } + + if (!medium_.StartDiscovery(callback)) { + NEARBY_LOG(INFO, "Failed to start discovery of BT devices."); + return false; + } + + // Mark the fact that we're currently performing a Bluetooth scan. + scan_info_.valid = true; + + return true; +} + +bool BluetoothClassic::StopDiscovery() { + MutexLock lock(&mutex_); + + if (!IsDiscovering()) { + NEARBY_LOG(INFO, + "Can't stop discovery of BT devices because it never started."); + return false; + } + + if (!medium_.StopDiscovery()) { + NEARBY_LOG(INFO, "Failed to stop discovery of Bluetooth devices."); + return false; + } + + scan_info_.valid = false; + return true; +} + +bool BluetoothClassic::IsDiscovering() const { return scan_info_.valid; } + +bool BluetoothClassic::StartAcceptingConnections( + const std::string& service_name, AcceptedConnectionCallback callback) { + MutexLock lock(&mutex_); + + if (service_name.empty()) { + NEARBY_LOG( + INFO, + "Refusing to start accepting BT connections; service name is empty."); + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOG(INFO, + "Can't create BT server socket [service=%s]; BT is disabled.", + service_name.c_str()); + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOG( + INFO, + "Can't start accepting BT connections [service=%s]; BT not available.", + service_name.c_str()); + return false; + } + + if (IsAcceptingConnectionsLocked(service_name)) { + NEARBY_LOG(INFO, + "Refusing to start accepting BT connections [service=%s]; BT " + "server is already in-progress with the same name.", + service_name.c_str()); + return false; + } + + BluetoothServerSocket socket = medium_.ListenForService( + service_name, GenerateUuidFromString(service_name)); + if (!socket.IsValid()) { + NEARBY_LOG(INFO, "Failed to start accepting Bluetooth connections for %s.", + service_name.c_str()); + return false; + } + + // Mark the fact that there's an in-progress Bluetooth server accepting + // connections. + auto owned_socket = + server_sockets_.emplace(service_name, std::move(socket)).first->second; + + // Start the accept loop on a dedicated thread - this stays alive and + // listening for new incoming connections until StopAcceptingConnections() is + // invoked. + accept_loops_runner_.Execute([callback = std::move(callback), + server_socket = std::move(owned_socket), + service_name]() mutable { + while (true) { + BluetoothSocket client_socket = server_socket.Accept(); + if (!client_socket.IsValid()) { + server_socket.Close(); + break; + } + + callback.accepted_cb(std::move(client_socket)); + } + }); + + return true; +} + +bool BluetoothClassic::IsAcceptingConnections(const std::string& service_name) { + MutexLock lock(&mutex_); + + return IsAcceptingConnectionsLocked(service_name); +} + +bool BluetoothClassic::IsAcceptingConnectionsLocked( + const std::string& service_name) { + return server_sockets_.find(service_name) != server_sockets_.end(); +} + +bool BluetoothClassic::StopAcceptingConnections( + const std::string& service_name) { + MutexLock lock(&mutex_); + + if (service_name.empty()) { + NEARBY_LOG(INFO, + "Unable to stop accepting BT connections because the " + "service_name is empty."); + return false; + } + + const auto& it = server_sockets_.find(service_name); + if (it == server_sockets_.end()) { + NEARBY_LOG(INFO, + "Can't stop accepting BT connections for %s because it was " + "never started.", + service_name.c_str()); + return false; + } + + // Closing the BluetoothServerSocket will kick off the suicide of the thread + // in accept_loops_thread_pool_ that blocks on BluetoothServerSocket.accept(). + // That may take some time to complete, but there's no particular reason to + // wait around for it. + auto item = server_sockets_.extract(it); + + // Store a handle to the BluetoothServerSocket, so we can use it after + // removing the entry from server_sockets_; making it scoped + // is a bonus that takes care of deallocation before we leave this method. + BluetoothServerSocket& listening_socket = item.mapped(); + + // Regardless of whether or not we fail to close the existing + // BluetoothServerSocket, remove it from server_sockets_ so that it + // frees up this service for another round. + + // Finally, close the BluetoothServerSocket. + if (!listening_socket.Close().Ok()) { + NEARBY_LOG(INFO, "Failed to close BT server socket for %s.", + service_name.c_str()); + return false; + } + + return true; +} + +BluetoothSocket BluetoothClassic::Connect(BluetoothDevice& bluetooth_device, + const std::string& service_name) { + MutexLock lock(&mutex_); + NEARBY_LOG(INFO, "BluetoothClassic::Connect: device=%p", &bluetooth_device); + // Socket to return. To allow for NRVO to work, it has to be a single object. + BluetoothSocket socket; + + if (service_name.empty()) { + NEARBY_LOG( + INFO, + "Refusing to create client BT socket because service_name is empty."); + return socket; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOG(INFO, + "Can't create client BT socket [service=%s]: BT isn't enabled.", + service_name.c_str()); + return socket; + } + + if (!IsAvailableLocked()) { + NEARBY_LOG( + INFO, "Can't create client BT socket [service=%s]; BT isn't available.", + service_name.c_str()); + return socket; + } + + socket = medium_.ConnectToService(bluetooth_device, + GenerateUuidFromString(service_name)); + if (!socket.IsValid()) { + NEARBY_LOG(INFO, "Failed to Connect via BT [service=%s]", + service_name.c_str()); + } + + return socket; +} + +std::string BluetoothClassic::GenerateUuidFromString(const std::string& data) { + return std::string(Uuid(data)); +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/bluetooth_classic.h b/cpp/core_v2/internal/mediums/bluetooth_classic.h new file mode 100644 index 00000000..69308309 --- /dev/null +++ b/cpp/core_v2/internal/mediums/bluetooth_classic.h @@ -0,0 +1,178 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_BLUETOOTH_CLASSIC_H_ +#define CORE_V2_INTERNAL_MEDIUMS_BLUETOOTH_CLASSIC_H_ + +#include +#include + +#include "core_v2/internal/mediums/bluetooth_radio.h" +#include "core_v2/listeners.h" +#include "platform_v2/base/byte_array.h" +#include "platform_v2/public/bluetooth_adapter.h" +#include "platform_v2/public/bluetooth_classic.h" +#include "platform_v2/public/multi_thread_executor.h" +#include "platform_v2/public/mutex.h" +#include "absl/container/flat_hash_map.h" + +namespace location { +namespace nearby { +namespace connections { + +class BluetoothClassic { + public: + using DiscoveredDeviceCallback = BluetoothClassicMedium::DiscoveryCallback; + using ScanMode = BluetoothAdapter::ScanMode; + + // Callback that is invoked when a new connection is accepted. + struct AcceptedConnectionCallback { + std::function accepted_cb = + DefaultCallback(); + }; + + explicit BluetoothClassic(BluetoothRadio& bluetooth_radio); + ~BluetoothClassic(); + + // Returns true, if BT communications are supported by a platform. + bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Sets custom device name, and then enables BT discoverable mode. + // Returns true, if name and scan mode are successfully set, and false + // otherwise. + // Called by server. + bool TurnOnDiscoverability(const std::string& device_name) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables BT discoverability, and restores scan mode and device name to + // what they were before the call to TurnOnDiscoverability(). + // Returns false if no successful call TurnOnDiscoverability() was previously + // made, otherwise returns true. + // Called by server. + bool TurnOffDiscoverability() ABSL_LOCKS_EXCLUDED(mutex_); + + // Enables BT discovery mode. Will report any discoverable devices in range + // through a callback. + // Returns true, if discovery mode was enabled, false otherwise. + // Called by client. + bool StartDiscovery(DiscoveredDeviceCallback callback) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables BT discovery mode. + // Returns true, if discovery mode was previously enabled, false otherwise. + // Called by client. + bool StopDiscovery() ABSL_LOCKS_EXCLUDED(mutex_); + + // Starts a worker thread, creates a BT server socket, associates it with a + // service name; in a worker thread repeatedly calls ServerSocket::Accept(). + // Any connected sockets returned from Accept() are passed to a callback. + // Returns true, if server socket was successfully created, false otherwise. + // Called by server. + bool StartAcceptingConnections(const std::string& service_name, + AcceptedConnectionCallback callback) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true, if object is currently running a Accept() loop. + bool IsAcceptingConnections(const std::string& service_name) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Closes server socket corresponding to a service name. This automatically + // terminates Accept() loop, if it were running. + // Called by server. + bool StopAcceptingConnections(const std::string& service_name) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true if this object owns a valid platform implementation. + bool IsMediumValid() const ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + return medium_.IsValid(); + } + + // Returns true if this object has a valid BluetoothAdapter reference. + bool IsAdapterValid() const ABSL_LOCKS_EXCLUDED(mutex_) { + MutexLock lock(&mutex_); + return adapter_.IsValid(); + } + + // Establishes connection to BT service that was might be started on another + // device with StartAcceptingConnections() using the same service_name. + // Blocks until connection is established, or server-side is terminated. + // Returns socket instance. On success, BluetoothSocket.IsValid() return true. + // Called by client. + BluetoothSocket Connect(BluetoothDevice& bluetooth_device, + const std::string& service_name) + ABSL_LOCKS_EXCLUDED(mutex_); + + private: + struct ScanInfo { + bool valid = false; + }; + + static constexpr int kMaxConcurrentAcceptLoops = 5; + + // Constructs UUID object from arbitrary string, using MD5 hash, and then + // converts UUID to a readable UUID string and returns it. + static std::string GenerateUuidFromString(const std::string& data); + + // Same as IsAvailable(), but must be called with mutex_ held. + bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Same as IsAcceptingConnections(), but must be called with mutex_ held. + bool IsAcceptingConnectionsLocked(const std::string& service_name) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Returns true, if discoverability is enabled with TurnOnDiscoverability(). + bool IsDiscoverable() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Assignes a different name to BT adapter. + // Returns true if successful. Stores original device name. + bool ModifyDeviceName(const std::string& device_name) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Changes current scan mode. This is an implementation of + // TurnDiscoveradility() method. Stores original scan mode. + bool ModifyScanMode(ScanMode scan_mode) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Restores original device name (the one before the very first call to + // ModifyDeviceName()). Returns true if successful. + bool RestoreScanMode() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Restores original device scan mode (the one before the very first call to + // ModifyScanMode()). Returns true if successful. + bool RestoreDeviceName() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Returns true if device is currently in discovery mode. + bool IsDiscovering() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + mutable Mutex mutex_; + BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); + BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){ + radio_.GetBluetoothAdapter()}; + BluetoothClassicMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; + + // A bundle of state required to do a Bluetooth Classic scan. When non-null, + // we are currently performing a Bluetooth scan. + ScanInfo scan_info_ ABSL_GUARDED_BY(mutex_); + + // The original scan mode (that controls visibility to scanners) of the device + // before we modified it. Restored when we stop advertising. + ScanMode original_scan_mode_ ABSL_GUARDED_BY(mutex_) = ScanMode::kUnknown; + + // The original Bluetooth device name, before we modified it. If non-empty, we + // are currently Bluetooth discoverable. Restored when we stop advertising. + std::string original_device_name_ ABSL_GUARDED_BY(mutex_); + + // A thread pool dedicated to running all the accept loops from + // StartAcceptingConnections(). + MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops}; + + // A map of service Name -> ServerSocket. If map is non-empty, we + // are currently listening for incoming connections. + // BluetoothServerSocket instances are used from accept_loops_runner_, + // and thus require pointer stability. + absl::flat_hash_map server_sockets_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_BLUETOOTH_CLASSIC_H_ diff --git a/cpp/core_v2/internal/mediums/bluetooth_classic_test.cc b/cpp/core_v2/internal/mediums/bluetooth_classic_test.cc new file mode 100644 index 00000000..33fae825 --- /dev/null +++ b/cpp/core_v2/internal/mediums/bluetooth_classic_test.cc @@ -0,0 +1,194 @@ +#include "core_v2/internal/mediums/bluetooth_classic.h" + +#include + +#include "core_v2/internal/mediums/bluetooth_radio.h" +#include "platform_v2/base/medium_environment.h" +#include "platform_v2/public/bluetooth_classic.h" +#include "platform_v2/public/count_down_latch.h" +#include "platform_v2/public/logging.h" +#include "platform_v2/public/system_clock.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/time/time.h" + +namespace location { +namespace nearby { +namespace connections { +namespace { + +constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); + +class BluetoothClassicTest : public ::testing::Test { + protected: + using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback; + + BluetoothClassicTest() { + env_.Reset(); + radio_a_ = std::make_unique(); + radio_b_ = std::make_unique(); + bt_a_ = std::make_unique(*radio_a_); + bt_b_ = std::make_unique(*radio_b_); + radio_a_->GetBluetoothAdapter().SetName("Device-A"); + radio_b_->GetBluetoothAdapter().SetName("Device-B"); + radio_a_->Enable(); + radio_b_->Enable(); + env_.Sync(); + } + + ~BluetoothClassicTest() override { + env_.Sync(false); + radio_a_->Disable(); + radio_b_->Disable(); + bt_a_.reset(); + bt_b_.reset(); + env_.Sync(false); + radio_a_.reset(); + radio_b_.reset(); + env_.Reset(); + } + + MediumEnvironment& env_{MediumEnvironment::Instance()}; + + std::unique_ptr radio_a_; + std::unique_ptr radio_b_; + std::unique_ptr bt_a_; + std::unique_ptr bt_b_; +}; + +TEST_F(BluetoothClassicTest, CanConstructValidObject) { + EXPECT_TRUE(bt_a_->IsMediumValid()); + EXPECT_TRUE(bt_a_->IsAdapterValid()); + EXPECT_TRUE(bt_a_->IsAvailable()); + EXPECT_TRUE(bt_b_->IsMediumValid()); + EXPECT_TRUE(bt_b_->IsAdapterValid()); + EXPECT_TRUE(bt_b_->IsAvailable()); + EXPECT_NE(&radio_a_->GetBluetoothAdapter(), &radio_b_->GetBluetoothAdapter()); +} + +TEST_F(BluetoothClassicTest, CanStartAdvertising) { + constexpr absl::string_view kDeviceName{"Simulated BT device #1"}; + EXPECT_TRUE(bt_a_->TurnOnDiscoverability(std::string(kDeviceName))); + EXPECT_EQ(radio_a_->GetBluetoothAdapter().GetName(), kDeviceName); +} + +TEST_F(BluetoothClassicTest, CanStopAdvertising) { + constexpr absl::string_view kDeviceName{"Simulated BT device #1"}; + EXPECT_TRUE(bt_a_->TurnOnDiscoverability(std::string(kDeviceName))); + EXPECT_EQ(radio_a_->GetBluetoothAdapter().GetName(), kDeviceName); + EXPECT_TRUE(bt_a_->TurnOffDiscoverability()); +} + +TEST_F(BluetoothClassicTest, CanStartDiscovery) { + constexpr absl::string_view kDeviceName{"Simulated BT device #1"}; + EXPECT_TRUE(bt_a_->TurnOnDiscoverability(std::string(kDeviceName))); + EXPECT_EQ(radio_a_->GetBluetoothAdapter().GetName(), kDeviceName); + CountDownLatch latch(1); + EXPECT_TRUE(bt_b_->StartDiscovery({ + .device_discovered_cb = + [&latch](BluetoothDevice& device) { latch.CountDown(); }, + })); + EXPECT_TRUE(latch.Await(kWaitDuration).result()); + EXPECT_TRUE(bt_a_->TurnOffDiscoverability()); +} + +TEST_F(BluetoothClassicTest, CanStopDiscovery) { + CountDownLatch latch(1); + EXPECT_TRUE(bt_a_->StartDiscovery({ + .device_discovered_cb = + [&latch](BluetoothDevice& device) { latch.CountDown(); }, + })); + EXPECT_FALSE(latch.Await(kWaitDuration).result()); + EXPECT_TRUE(bt_a_->StopDiscovery()); +} + +TEST_F(BluetoothClassicTest, CanStartAcceptingConnections) { + constexpr absl::string_view kDeviceName{"Simulated BT device #1"}; + constexpr absl::string_view kServiceName{"service name"}; + + BluetoothRadio& radio_for_client = *radio_a_; + BluetoothRadio& radio_for_server = *radio_b_; + BluetoothClassic& bt_client = *bt_a_; + BluetoothClassic& bt_server = *bt_b_; + + EXPECT_TRUE(radio_for_client.IsEnabled()); + EXPECT_TRUE(radio_for_server.IsEnabled()); + + EXPECT_TRUE(bt_server.TurnOnDiscoverability(std::string(kDeviceName))); + EXPECT_EQ(radio_for_server.GetBluetoothAdapter().GetName(), kDeviceName); + CountDownLatch latch(1); + BluetoothDevice discovered_device; + EXPECT_TRUE(bt_client.StartDiscovery({ + .device_discovered_cb = + [&latch, &discovered_device](BluetoothDevice& device) { + discovered_device = device; + NEARBY_LOG(INFO, "Discovered device=%p [impl=%p]", &device, + &device.GetImpl()); + latch.CountDown(); + }, + })); + EXPECT_TRUE(latch.Await(kWaitDuration).result()); + EXPECT_TRUE(bt_server.TurnOffDiscoverability()); + EXPECT_TRUE(discovered_device.IsValid()); + EXPECT_TRUE( + bt_server.StartAcceptingConnections(std::string(kServiceName), {})); + // Allow StartAcceptingConnections do something, before stopping it. + // This is best effort, because no callbacks are invoked in this scenario. + SystemClock::Sleep(kWaitDuration); + EXPECT_TRUE(bt_server.StopAcceptingConnections(std::string(kServiceName))); +} + +TEST_F(BluetoothClassicTest, CanConnect) { + constexpr absl::string_view kDeviceName{"Simulated BT device #1"}; + constexpr absl::string_view kServiceName{"service name"}; + + BluetoothRadio& radio_for_client = *radio_a_; + BluetoothRadio& radio_for_server = *radio_b_; + BluetoothClassic& bt_client = *bt_a_; + BluetoothClassic& bt_server = *bt_b_; + + EXPECT_TRUE(radio_for_client.IsEnabled()); + EXPECT_TRUE(radio_for_server.IsEnabled()); + + EXPECT_TRUE(bt_server.TurnOnDiscoverability(std::string(kDeviceName))); + EXPECT_EQ(radio_for_server.GetBluetoothAdapter().GetName(), + std::string(kDeviceName)); + CountDownLatch latch(1); + BluetoothDevice discovered_device; + EXPECT_TRUE(bt_client.StartDiscovery({ + .device_discovered_cb = + [&latch, &discovered_device](BluetoothDevice& device) { + discovered_device = device; + NEARBY_LOG(INFO, "Discovered device=%p [impl=%p]", &device, + &device.GetImpl()); + latch.CountDown(); + }, + })); + EXPECT_TRUE(latch.Await(kWaitDuration).result()); + EXPECT_TRUE(bt_server.TurnOffDiscoverability()); + ASSERT_TRUE(discovered_device.IsValid()); + BluetoothSocket socket_for_server; + CountDownLatch accept_latch(1); + EXPECT_TRUE(bt_server.StartAcceptingConnections( + std::string(kServiceName), + { + .accepted_cb = + [&socket_for_server, &accept_latch](BluetoothSocket socket) { + socket_for_server = std::move(socket); + accept_latch.CountDown(); + }, + })); + BluetoothSocket socket_for_client = + bt_client.Connect(discovered_device, std::string(kServiceName)); + EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(bt_server.StopAcceptingConnections(std::string(kServiceName))); + EXPECT_TRUE(socket_for_server.IsValid()); + EXPECT_TRUE(socket_for_client.IsValid()); + EXPECT_TRUE(socket_for_server.GetRemoteDevice().IsValid()); + EXPECT_TRUE(socket_for_client.GetRemoteDevice().IsValid()); +} + +} // namespace +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/bluetooth_radio.cc b/cpp/core_v2/internal/mediums/bluetooth_radio.cc index 77a7ec00..c7a650a4 100644 --- a/cpp/core_v2/internal/mediums/bluetooth_radio.cc +++ b/cpp/core_v2/internal/mediums/bluetooth_radio.cc @@ -8,6 +8,8 @@ namespace location { namespace nearby { namespace connections { +constexpr absl::Duration BluetoothRadio::kPauseBetweenToggle; + BluetoothRadio::BluetoothRadio() { if (!IsAdapterValid()) { NEARBY_LOG(ERROR, "Bluetooth adapter is not valid: BT is not supported"); diff --git a/cpp/core_v2/internal/mediums/mediums.cc b/cpp/core_v2/internal/mediums/mediums.cc new file mode 100644 index 00000000..aa070252 --- /dev/null +++ b/cpp/core_v2/internal/mediums/mediums.cc @@ -0,0 +1,17 @@ +#include "core_v2/internal/mediums/mediums.h" + +namespace location { +namespace nearby { +namespace connections { + +BluetoothRadio& Mediums::GetBluetoothRadio() { + return bluetooth_radio_; +} + +BluetoothClassic& Mediums::GetBluetoothClassic() { + return bluetooth_classic_; +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/mediums.h b/cpp/core_v2/internal/mediums/mediums.h new file mode 100644 index 00000000..230ba61e --- /dev/null +++ b/cpp/core_v2/internal/mediums/mediums.h @@ -0,0 +1,40 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_MEDIUMS_H_ +#define CORE_V2_INTERNAL_MEDIUMS_MEDIUMS_H_ + +#include "core_v2/internal/mediums/bluetooth_classic.h" +#include "core_v2/internal/mediums/bluetooth_radio.h" + +namespace location { +namespace nearby { +namespace connections { + +// Facilitates convenient and reliable usage of various wireless mediums. +class Mediums { + public: + Mediums() = default; + ~Mediums() = default; + + // Returns a handle to the Bluetooth radio. + BluetoothRadio& GetBluetoothRadio(); + + // Returns a handle to the Bluetooth Classic medium. + BluetoothClassic& GetBluetoothClassic(); + + private: + // The order of declaration is critical for both construction and + // destruction. + // + // 1) Construction: The individual mediums have a dependency on the + // corresponding radio, so the radio must be initialized first. + // + // 2) Destruction: The individual mediums should be shut down before the + // corresponding radio. + BluetoothRadio bluetooth_radio_; + BluetoothClassic bluetooth_classic_{bluetooth_radio_}; +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_MEDIUMS_H_ diff --git a/cpp/core_v2/internal/mediums/webrtc/BUILD b/cpp/core_v2/internal/mediums/webrtc/BUILD index 9805da7e..d354a426 100644 --- a/cpp/core_v2/internal/mediums/webrtc/BUILD +++ b/cpp/core_v2/internal/mediums/webrtc/BUILD @@ -19,7 +19,7 @@ cc_library( "//platform_v2/public:logging", "//platform_v2/public:types", "//absl/memory", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) @@ -35,7 +35,7 @@ cc_test( "//platform_v2/impl/g3", # buildcleaner: keep "//platform_v2/public:comm", "//testing/base/public:gunit_main", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) @@ -61,7 +61,7 @@ cc_test( "//platform_v2/impl/g3", # buildcleaner: keep "//net/proto2/public:proto2", "//testing/base/public:gunit_main", - "//webrtc/pc:peerconnection", # buildcleaner: keep + "//webrtc/files/stable/webrtc/pc:peerconnection", # buildcleaner: keep ], ) @@ -84,6 +84,6 @@ cc_library( ":peer_id", "//platform_v2/base", "//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) diff --git a/cpp/core_v2/internal/mediums/webrtc/connection_flow.h b/cpp/core_v2/internal/mediums/webrtc/connection_flow.h index 7f5ca6dc..b2b4d523 100644 --- a/cpp/core_v2/internal/mediums/webrtc/connection_flow.h +++ b/cpp/core_v2/internal/mediums/webrtc/connection_flow.h @@ -10,8 +10,8 @@ #include "platform_v2/public/future.h" #include "platform_v2/public/single_thread_executor.h" #include "platform_v2/public/webrtc.h" -#include "webrtc/api/data_channel_interface.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h b/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h index 62adf483..101b6ee0 100644 --- a/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h +++ b/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h @@ -2,7 +2,7 @@ #define CORE_V2_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ #include "core_v2/listeners.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h b/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h index 7c30ef7b..fd4491d0 100644 --- a/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h +++ b/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h @@ -3,7 +3,7 @@ #include "core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h" #include "platform_v2/public/single_thread_executor.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h b/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h index 78fe328a..63a92718 100644 --- a/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h +++ b/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h @@ -6,7 +6,7 @@ #include "core_v2/internal/mediums/webrtc/peer_id.h" #include "platform_v2/base/byte_array.h" #include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h index c0268f65..e5d90939 100644 --- a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h +++ b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h @@ -11,7 +11,7 @@ #include "platform_v2/public/condition_variable.h" #include "platform_v2/public/mutex.h" #include "platform_v2/public/pipe.h" -#include "webrtc/api/data_channel_interface.h" +#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" namespace location { namespace nearby { namespace connections { diff --git a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc index 423b06ed..89184569 100644 --- a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc +++ b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc @@ -5,7 +5,7 @@ #include "platform_v2/base/byte_array.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "webrtc/api/data_channel_interface.h" +#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/offline_frames.cc b/cpp/core_v2/internal/offline_frames.cc index fc5c5572..792922bb 100644 --- a/cpp/core_v2/internal/offline_frames.cc +++ b/cpp/core_v2/internal/offline_frames.cc @@ -3,7 +3,7 @@ #include #include -#include "google/protobuf/message_lite.h" +#include "core/internal/message_lite.h" #include "platform_v2/base/byte_array.h" namespace location { diff --git a/cpp/platform/api/BUILD b/cpp/platform/api/BUILD index 62c38942..1b155f0c 100644 --- a/cpp/platform/api/BUILD +++ b/cpp/platform/api/BUILD @@ -47,7 +47,7 @@ cc_library( "//platform/port:string", "//absl/strings", "//absl/types:any", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) diff --git a/cpp/platform/api/webrtc.h b/cpp/platform/api/webrtc.h index 39e09515..c428c0cb 100644 --- a/cpp/platform/api/webrtc.h +++ b/cpp/platform/api/webrtc.h @@ -5,7 +5,7 @@ #include "platform/byte_array.h" #include "platform/ptr.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/platform_v2/api/BUILD b/cpp/platform_v2/api/BUILD index a0d9013f..cfe2df3d 100644 --- a/cpp/platform_v2/api/BUILD +++ b/cpp/platform_v2/api/BUILD @@ -52,7 +52,7 @@ cc_library( "//platform_v2/base", "//absl/strings", "//absl/types:optional", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) diff --git a/cpp/platform_v2/api/platform.h b/cpp/platform_v2/api/platform.h index f710897f..05b36280 100644 --- a/cpp/platform_v2/api/platform.h +++ b/cpp/platform_v2/api/platform.h @@ -65,9 +65,11 @@ class ImplementationPlatform { // Protocol implementations, domain-specific support static std::unique_ptr CreateBluetoothAdapter(); - static std::unique_ptr CreateBluetoothClassicMedium(); - static std::unique_ptr CreateBleMedium(); - static std::unique_ptr CreateBleV2Medium(); + static std::unique_ptr CreateBluetoothClassicMedium( + BluetoothAdapter&); + static std::unique_ptr CreateBleMedium(BluetoothAdapter&); + static std::unique_ptr CreateBleV2Medium( + BluetoothAdapter&); static std::unique_ptr CreateServerSyncMedium(); static std::unique_ptr CreateWifiMedium(); static std::unique_ptr CreateWifiLanMedium(); diff --git a/cpp/platform_v2/api/webrtc.h b/cpp/platform_v2/api/webrtc.h index d07bc699..7d89b281 100644 --- a/cpp/platform_v2/api/webrtc.h +++ b/cpp/platform_v2/api/webrtc.h @@ -5,7 +5,7 @@ #include "platform_v2/base/byte_array.h" #include "absl/strings/string_view.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/platform_v2/base/medium_environment.cc b/cpp/platform_v2/base/medium_environment.cc index 6c47cc33..4430a003 100644 --- a/cpp/platform_v2/base/medium_environment.cc +++ b/cpp/platform_v2/base/medium_environment.cc @@ -21,8 +21,23 @@ MediumEnvironment& MediumEnvironment::Instance() { return *env; } +void MediumEnvironment::Start() { + if (!enabled_.exchange(true)) { + NEARBY_LOG(INFO, "MediumEnvironment::Start()"); + Reset(); + } +} + +void MediumEnvironment::Stop() { + if (enabled_.exchange(false)) { + NEARBY_LOG(INFO, "MediumEnvironment::Stop()"); + Sync(false); + } +} + void MediumEnvironment::Reset() { RunOnMediumEnvironmentThread([this]() { + NEARBY_LOG(INFO, "MediumEnvironment::Reset()"); bluetooth_adapters_.clear(); bluetooth_mediums_.clear(); }); @@ -31,6 +46,7 @@ void MediumEnvironment::Reset() { void MediumEnvironment::Sync(bool enable_notifications) { enable_notifications_ = enable_notifications; + NEARBY_LOG(INFO, "MediumEnvironment::sync(%d)", enable_notifications); int count = 0; do { CountDownLatch latch(1); @@ -50,6 +66,7 @@ void MediumEnvironment::Sync(bool enable_notifications) { void MediumEnvironment::OnBluetoothAdapterChangedState( api::BluetoothAdapter& adapter, api::BluetoothDevice& adapter_device, std::string name, bool enabled, api::BluetoothAdapter::ScanMode mode) { + if (!enabled_) return; RunOnMediumEnvironmentThread([this, &adapter, &adapter_device, name = std::move(name), enabled, mode]() { NEARBY_LOG(INFO, @@ -74,6 +91,7 @@ void MediumEnvironment::OnDeviceStateChanged( BluetoothMediumContext& info, api::BluetoothDevice& device, const std::string& name, api::BluetoothAdapter::ScanMode mode, bool enabled) { + if (!enabled_) return; auto item = info.devices.find(&device); if (item == info.devices.end()) { NEARBY_LOG( @@ -136,6 +154,7 @@ void MediumEnvironment::RunOnMediumEnvironmentThread( void MediumEnvironment::RegisterBluetoothMedium( api::BluetoothClassicMedium& medium, api::BluetoothAdapter& medium_adapter) { + if (!enabled_) return; RunOnMediumEnvironmentThread([this, &medium, &medium_adapter]() { auto& context = bluetooth_mediums_ .insert({&medium, @@ -156,6 +175,7 @@ void MediumEnvironment::RegisterBluetoothMedium( void MediumEnvironment::UpdateBluetoothMedium( api::BluetoothClassicMedium& medium, BluetoothDiscoveryCallback callback) { + if (!enabled_) return; RunOnMediumEnvironmentThread([this, &medium, callback = std::move(callback)]() { auto item = bluetooth_mediums_.find(&medium); @@ -178,6 +198,7 @@ void MediumEnvironment::UpdateBluetoothMedium( void MediumEnvironment::UnregisterBluetoothMedium( api::BluetoothClassicMedium& medium) { + if (!enabled_) return; RunOnMediumEnvironmentThread([this, &medium]() { auto item = bluetooth_mediums_.extract(&medium); if (item.empty()) return; diff --git a/cpp/platform_v2/base/medium_environment.h b/cpp/platform_v2/base/medium_environment.h index b00eafc2..44e83e1a 100644 --- a/cpp/platform_v2/base/medium_environment.h +++ b/cpp/platform_v2/base/medium_environment.h @@ -27,6 +27,16 @@ class MediumEnvironment { // Creates and returns a reference to the global test environment instance. static MediumEnvironment& Instance(); + // Global ON/OFF switch for medium environment. + // Start & Stop work as On/Off switch for this object. + // Default state (after creation) is ON, to make it compatible with early + // tests that are already using it and relying on it being ON. + + // Enables Medium environment. + void Start(); + // Disables Medium environment. + void Stop(); + // Clears state. No notifications are sent. void Reset(); @@ -95,6 +105,7 @@ class MediumEnvironment { api::BluetoothAdapter::ScanMode mode, bool enabled); void RunOnMediumEnvironmentThread(std::function runnable); + std::atomic_bool enabled_ = true; std::atomic_int job_count_ = 0; std::atomic_bool enable_notifications_ = false; SingleThreadExecutor executor_; diff --git a/cpp/platform_v2/impl/g3/BUILD b/cpp/platform_v2/impl/g3/BUILD index 58b31cb9..69a23663 100644 --- a/cpp/platform_v2/impl/g3/BUILD +++ b/cpp/platform_v2/impl/g3/BUILD @@ -39,10 +39,12 @@ cc_library( testonly = True, srcs = [ "bluetooth_adapter.cc", + "bluetooth_classic.cc", "webrtc.cc", ], hdrs = [ "bluetooth_adapter.h", + "bluetooth_classic.h", "webrtc.h", ], visibility = [ @@ -51,13 +53,17 @@ cc_library( deps = [ ":types", "//platform_v2/api:comm", + "//platform_v2/base", + "//platform_v2/base:logging", "//platform_v2/base:test_util", "//absl/base:core_headers", + "//absl/container:flat_hash_map", + "//absl/container:flat_hash_set", "//absl/strings", "//absl/synchronization", - "//webrtc/api:create_peerconnection_factory", #buildcleaner: keep - "//webrtc/api:libjingle_peerconnection_api", - "//webrtc/api/task_queue:default_task_queue_factory", + "//webrtc/files/stable/webrtc/api:create_peerconnection_factory", #buildcleaner: keep + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api/task_queue:default_task_queue_factory", ], ) diff --git a/cpp/platform_v2/impl/g3/bluetooth_adapter.cc b/cpp/platform_v2/impl/g3/bluetooth_adapter.cc index 505ead84..748513b7 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_adapter.cc +++ b/cpp/platform_v2/impl/g3/bluetooth_adapter.cc @@ -3,6 +3,7 @@ #include #include "platform_v2/base/medium_environment.h" +#include "platform_v2/impl/g3/bluetooth_classic.h" namespace location { namespace nearby { @@ -11,9 +12,13 @@ namespace g3 { BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter) : adapter_(*adapter) {} +std::string BluetoothDevice::GetName() const { return adapter_.GetName(); } + BluetoothAdapter::~BluetoothAdapter() { SetStatus(Status::kDisabled); } -std::string BluetoothDevice::GetName() const { return adapter_.GetName(); } +void BluetoothAdapter::SetMedium(api::BluetoothClassicMedium* medium) { + medium_ = medium; +} bool BluetoothAdapter::SetStatus(Status status) { BluetoothAdapter::ScanMode mode; diff --git a/cpp/platform_v2/impl/g3/bluetooth_adapter.h b/cpp/platform_v2/impl/g3/bluetooth_adapter.h index 9747d7e0..8ce2b719 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_adapter.h +++ b/cpp/platform_v2/impl/g3/bluetooth_adapter.h @@ -70,9 +70,13 @@ class BluetoothAdapter : public api::BluetoothAdapter { BluetoothDevice& GetDevice() { return device_; } + void SetMedium(api::BluetoothClassicMedium* medium); + api::BluetoothClassicMedium* GetMedium() { return medium_; } + private: mutable absl::Mutex mutex_; BluetoothDevice device_{this}; + api::BluetoothClassicMedium* medium_ = nullptr; ScanMode mode_ ABSL_GUARDED_BY(mutex_) = ScanMode::kNone; std::string name_ ABSL_GUARDED_BY(mutex_) = "unknown G3 BT device"; bool enabled_ ABSL_GUARDED_BY(mutex_) = false; diff --git a/cpp/platform_v2/impl/g3/bluetooth_classic.cc b/cpp/platform_v2/impl/g3/bluetooth_classic.cc new file mode 100644 index 00000000..12232eb6 --- /dev/null +++ b/cpp/platform_v2/impl/g3/bluetooth_classic.cc @@ -0,0 +1,240 @@ +#include "platform_v2/impl/g3/bluetooth_classic.h" + +#include +#include + +#include "platform_v2/api/bluetooth_classic.h" +#include "platform_v2/base/logging.h" +#include "platform_v2/base/medium_environment.h" +#include "platform_v2/impl/g3/bluetooth_adapter.h" +#include "absl/synchronization/mutex.h" + +namespace location { +namespace nearby { +namespace g3 { + +void BluetoothSocket::Connect(BluetoothSocket& other) { + absl::MutexLock lock(&mutex_); + remote_socket_ = &other; +} + +bool BluetoothSocket::IsConnected() const { + absl::MutexLock lock(&mutex_); + return IsConnectedLocked(); +} + +bool BluetoothSocket::IsClosed() const { + absl::MutexLock lock(&mutex_); + return closed_; +} + +bool BluetoothSocket::IsConnectedLocked() const { + return remote_socket_ != nullptr; +} + +InputStream& BluetoothSocket::GetInputStream() { + auto* remote_socket = GetRemoteSocket(); + CHECK(remote_socket != nullptr); + return remote_socket->GetLocalInputStream(); +} + +OutputStream& BluetoothSocket::GetOutputStream() { + return GetLocalOutputStream(); +} + +InputStream& BluetoothSocket::GetLocalInputStream() { + absl::MutexLock lock(&mutex_); + return output_.GetInputStream(); +} + +OutputStream& BluetoothSocket::GetLocalOutputStream() { + absl::MutexLock lock(&mutex_); + return output_.GetOutputStream(); +} + +Exception BluetoothSocket::Close() { + BluetoothSocket* remote_socket = nullptr; + { + absl::MutexLock lock(&mutex_); + if (!closed_) { + remote_socket = remote_socket_; + output_.GetOutputStream().Close(); + output_.GetInputStream().Close(); + closed_ = true; + } + } + if (remote_socket != nullptr) { + remote_socket->Close(); + } + return {Exception::kSuccess}; +} + +BluetoothSocket* BluetoothSocket::GetRemoteSocket() { + absl::MutexLock lock(&mutex_); + return remote_socket_; +} + +BluetoothDevice* BluetoothSocket::GetRemoteDevice() { + BluetoothAdapter* remote_adapter = nullptr; + { + absl::MutexLock lock(&mutex_); + if (remote_socket_ == nullptr || remote_socket_->adapter_ == nullptr) { + return nullptr; + } + remote_adapter = remote_socket_->adapter_; + } + return remote_adapter ? &remote_adapter->GetDevice() : nullptr; +} + +std::unique_ptr BluetoothServerSocket::Accept() { + absl::MutexLock lock(&mutex_); + while (pending_sockets_.empty()) { + cond_.Wait(&mutex_); + if (closed_) break; + } + // whether or not we were running in the wait loop, return early if closed. + if (closed_) return {}; + auto* remote_socket = + pending_sockets_.extract(pending_sockets_.begin()).value(); + CHECK(remote_socket); + auto local_socket = std::make_unique(adapter_); + local_socket->Connect(*remote_socket); + remote_socket->Connect(*local_socket); + cond_.SignalAll(); + return local_socket; +} + +bool BluetoothServerSocket::Connect(BluetoothSocket& socket) { + absl::MutexLock lock(&mutex_); + if (closed_) return false; + if (socket.IsConnected()) { + NEARBY_LOG(ERROR, + "Failed to connect to BT server socket: already connected"); + return true; // already connected. + } + // add client socket to the pending list + pending_sockets_.emplace(&socket); + cond_.SignalAll(); + while (!socket.IsConnected()) { + cond_.Wait(&mutex_); + if (closed_) return false; + } + return true; +} + +void BluetoothServerSocket::SetCloseNotifier(std::function notifier) { + absl::MutexLock lock(&mutex_); + close_notifier_ = std::move(notifier); +} + +BluetoothServerSocket::~BluetoothServerSocket() { + absl::MutexLock lock(&mutex_); + DoClose(); +} + +Exception BluetoothServerSocket::Close() { + absl::MutexLock lock(&mutex_); + return DoClose(); +} + +Exception BluetoothServerSocket::DoClose() { + bool should_notify = !closed_; + closed_ = true; + if (should_notify) { + cond_.SignalAll(); + if (close_notifier_) { + auto notifier = std::move(close_notifier_); + mutex_.Unlock(); + // Notifier may contain calls to public API, and may cause deadlock, if + // mutex_ is held during the call. + notifier(); + mutex_.Lock(); + } + } + return {Exception::kSuccess}; +} + +BluetoothClassicMedium::BluetoothClassicMedium(api::BluetoothAdapter& adapter) + // TODO(apolyudov): implement and use downcast<> with static assertions. + : adapter_(static_cast(&adapter)) { + adapter_->SetMedium(this); + auto& env = MediumEnvironment::Instance(); + env.RegisterBluetoothMedium(*this, GetAdapter()); +} + +BluetoothClassicMedium::~BluetoothClassicMedium() { + adapter_->SetMedium(nullptr); + auto& env = MediumEnvironment::Instance(); + env.UnregisterBluetoothMedium(*this); +} + +bool BluetoothClassicMedium::StartDiscovery(DiscoveryCallback callback) { + auto& env = MediumEnvironment::Instance(); + env.UpdateBluetoothMedium(*this, std::move(callback)); + return true; +} + +bool BluetoothClassicMedium::StopDiscovery() { + auto& env = MediumEnvironment::Instance(); + env.UpdateBluetoothMedium(*this, {}); + return true; +} + +std::unique_ptr BluetoothClassicMedium::ConnectToService( + api::BluetoothDevice& remote_device, const std::string& service_uuid) { + NEARBY_LOG(INFO, + "G3 ConnectToService [self]: medium=%p, adapter=%p, device=%p", + this, &GetAdapter(), &GetAdapter().GetDevice()); + // First, find an instance of remote medium, that exposed this device. + auto& adapter = static_cast(remote_device).GetAdapter(); + auto* medium = static_cast(adapter.GetMedium()); + + if (!medium) return {}; // Adapter is not bound to medium. Bail out. + + BluetoothServerSocket* server_socket = nullptr; + NEARBY_LOG( + INFO, + "G3 ConnectToService [peer]: medium=%p, adapter=%p, device=%p, uuid=%s", + medium, &adapter, &remote_device, service_uuid.c_str()); + // Then, find our server socket context in this medium. + { + absl::MutexLock medium_lock(&medium->mutex_); + auto item = medium->sockets_.find(service_uuid); + server_socket = item != sockets_.end() ? item->second : nullptr; + if (server_socket == nullptr) { + NEARBY_LOG(ERROR, "Failed to find BT Server socket: uuid=%s", + service_uuid.c_str()); + return {}; + } + } + + auto socket = std::make_unique(&GetAdapter()); + // Finally, Request to connect to this socket. + if (!server_socket->Connect(*socket)) { + NEARBY_LOG(ERROR, "Failed to connect to existing BT Server socket: uuid=%s", + service_uuid.c_str()); + return {}; + } + + NEARBY_LOG(INFO, "G3 ConnectToService: connected: socket=%p", socket.get()); + return socket; +} + +std::unique_ptr +BluetoothClassicMedium::ListenForService(const std::string& service_name, + const std::string& service_uuid) { + auto socket = std::make_unique(GetAdapter()); + socket->SetCloseNotifier([this, uuid = service_uuid]() { + absl::MutexLock lock(&mutex_); + sockets_.erase(uuid); + }); + NEARBY_LOG(INFO, "Adding service: medium=%p, uuid=%s", this, + service_uuid.c_str()); + absl::MutexLock lock(&mutex_); + sockets_.emplace(service_uuid, socket.get()); + return socket; +} + +} // namespace g3 +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/impl/g3/bluetooth_classic.h b/cpp/platform_v2/impl/g3/bluetooth_classic.h new file mode 100644 index 00000000..77dfca5a --- /dev/null +++ b/cpp/platform_v2/impl/g3/bluetooth_classic.h @@ -0,0 +1,218 @@ +#ifndef PLATFORM_V2_IMPL_G3_BLUETOOTH_CLASSIC_H_ +#define PLATFORM_V2_IMPL_G3_BLUETOOTH_CLASSIC_H_ + +#include +#include + +#include "platform_v2/api/bluetooth_classic.h" +#include "platform_v2/base/byte_array.h" +#include "platform_v2/base/exception.h" +#include "platform_v2/base/input_stream.h" +#include "platform_v2/base/listeners.h" +#include "platform_v2/base/output_stream.h" +#include "platform_v2/impl/g3/bluetooth_adapter.h" +#include "platform_v2/impl/g3/pipe.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/synchronization/mutex.h" + +namespace location { +namespace nearby { +namespace g3 { + +// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. +class BluetoothSocket : public api::BluetoothSocket { + public: + BluetoothSocket() = default; + explicit BluetoothSocket(BluetoothAdapter* adapter) : adapter_(adapter) {} + ~BluetoothSocket() override = default; + + // Connects to another BluetoothSocket, to form a functional low-level + // channel. From this point on, and until Close is called, connection exists. + void Connect(BluetoothSocket& other); + + // NOTE: + // It is an undefined behavior if GetInputStream() or GetOutputStream() is + // called for a not-connected BluetoothSocket, i.e. any object that is not + // returned by BluetoothClassicMedium::ConnectToService() for client side or + // BluetoothServerSocket::Accept() for server side of connection. + + // Returns the InputStream of this connected BluetoothSocket. + InputStream& GetInputStream() override; + + // Returns the OutputStream of this connected BluetoothSocket. + // This stream is for local side to write. + OutputStream& GetOutputStream() override; + + // Returns address of a remote BluetoothSocket or nullptr. + BluetoothSocket* GetRemoteSocket() ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true if connection exists to the (possibly closed) remote socket. + bool IsConnected() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true if socket is closed. + bool IsClosed() const ABSL_LOCKS_EXCLUDED(mutex_); + + // 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. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() + // Returns valid BluetoothDevice pointer if there is a connection, and + // nullptr otherwise. + BluetoothDevice* GetRemoteDevice() override ABSL_LOCKS_EXCLUDED(mutex_); + + private: + // Returns true if connection exists to the (possibly closed) remote socket. + bool IsConnectedLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Returns InputStream of our side of a connection. + // This is what the remote side is supposed to read from. + // This is a helper for GetInputStream() method. + InputStream& GetLocalInputStream() ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns OutputStream of our side of a connection. + // This is what the local size is supposed to write to. + // This is a helper for GetOutputStream() method. + OutputStream& GetLocalOutputStream() ABSL_LOCKS_EXCLUDED(mutex_); + + // Output pipe is initialized by constructor, it remains always valid, until + // it is closed. it represents output part of a local socket. Input part of a + // local socket comes from the peer socket, after connection. + Pipe output_; + mutable absl::Mutex mutex_; + BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. + BluetoothSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. +class BluetoothServerSocket : public api::BluetoothServerSocket { + public: + explicit BluetoothServerSocket(BluetoothAdapter& adapter) + : adapter_(&adapter) {} + ~BluetoothServerSocket() override; + + // Blocks until either: + // - at least one incoming connection request is available, or + // - ServerSocket is closed. + // On success, returns connected socket, ready to exchange data. + // Returns nullptr on error. + // Once error is reported, it is permanent, and ServerSocket has to be closed. + // + // Called by the server side of a connection. + // Returns BluetoothSocket to the server side. + // If not null, returned socket is connected to its remote (client-side) peer. + std::unique_ptr Accept() override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Blocks until either: + // - connection is available, or + // - server socket is closed, or + // - error happens. + // + // Called by the client side of a connection. + // socket is an initialized BluetoothSocket, associated with a client + // BluetoothAdapter. + // Returns true, if socket is successfully connected. + bool Connect(BluetoothSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_); + + // Called by the server side of a connection before passing ownership of + // BluetoothServerSocker to user, to track validity of a pointer to this + // server socket, + void SetCloseNotifier(std::function notifier) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + // Calls close_notifier if it was previously set, and marks socket as closed. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + private: + Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + absl::Mutex mutex_; + absl::CondVar cond_; + BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. + absl::flat_hash_set pending_sockets_ + ABSL_GUARDED_BY(mutex_); + std::function close_notifier_ ABSL_GUARDED_BY(mutex_); + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +// Container of operations that can be performed over the Bluetooth Classic +// medium. +class BluetoothClassicMedium : public api::BluetoothClassicMedium { + public: + explicit BluetoothClassicMedium(api::BluetoothAdapter& adapter); + ~BluetoothClassicMedium() override; + + // NOTE(DiscoveryCallback): + // BluetoothDevice is a proxy object created as a result of BT discovery. + // Its lifetime spans between calls to device_discovered_cb and + // device_lost_cb. + // It is safe to use BluetoothDevice in device_discovered_cb() callback + // and at any time afterwards, until device_lost_cb() is called. + // It is not safe to use BluetoothDevice after returning from + // device_lost_cb() callback. + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery() + // + // Returns true once the process of discovery has been initiated. + bool StartDiscovery(DiscoveryCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery() + // + // Returns true once discovery is well and truly stopped; after this returns, + // there must be no more invocations of the DiscoveryCallback passed in to + // StartDiscovery(). + bool StopDiscovery() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Connects to existing remote BT service. + // + // A combination of + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord + // followed by + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect(). + // + // service_uuid is the canonical textual representation + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a + // type 3 name-based + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) + // UUID. + // + // On success, returns a new BluetoothSocket. + // On error, returns nullptr. + std::unique_ptr ConnectToService( + api::BluetoothDevice& remote_device, + const std::string& service_uuid) override ABSL_LOCKS_EXCLUDED(mutex_); + + BluetoothAdapter& GetAdapter() { return *adapter_; } + + // Creates BT service, and begins listening for remote attempts to connect. + // + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord + // + // service_uuid is the canonical textual representation + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a + // type 3 name-based + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) + // UUID. + // + // Returns nullptr on error. + std::unique_ptr ListenForService( + const std::string& service_name, const std::string& service_uuid) override + ABSL_LOCKS_EXCLUDED(mutex_); + + private: + absl::Mutex mutex_; + BluetoothAdapter* adapter_; // Our device adapter; read-only. + absl::flat_hash_map sockets_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace g3 +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_IMPL_G3_BLUETOOTH_CLASSIC_H_ diff --git a/cpp/platform_v2/impl/g3/platform.cc b/cpp/platform_v2/impl/g3/platform.cc index 73673f77..a77f6695 100644 --- a/cpp/platform_v2/impl/g3/platform.cc +++ b/cpp/platform_v2/impl/g3/platform.cc @@ -21,6 +21,7 @@ #include "platform_v2/impl/g3/atomic_boolean.h" #include "platform_v2/impl/g3/atomic_reference_any.h" #include "platform_v2/impl/g3/bluetooth_adapter.h" +#include "platform_v2/impl/g3/bluetooth_classic.h" #include "platform_v2/impl/g3/condition_variable.h" #include "platform_v2/impl/g3/count_down_latch.h" #include "platform_v2/impl/g3/multi_thread_executor.h" @@ -96,15 +97,18 @@ std::unique_ptr ImplementationPlatform::CreateOutputFile( } std::unique_ptr -ImplementationPlatform::CreateBluetoothClassicMedium() { - return std::unique_ptr(); +ImplementationPlatform::CreateBluetoothClassicMedium( + api::BluetoothAdapter& adapter) { + return absl::make_unique(adapter); } -std::unique_ptr ImplementationPlatform::CreateBleMedium() { +std::unique_ptr ImplementationPlatform::CreateBleMedium( + api::BluetoothAdapter& adapter) { return std::unique_ptr(); } -std::unique_ptr ImplementationPlatform::CreateBleV2Medium() { +std::unique_ptr ImplementationPlatform::CreateBleV2Medium( + api::BluetoothAdapter& adapter) { return std::unique_ptr(); } diff --git a/cpp/platform_v2/impl/g3/webrtc.cc b/cpp/platform_v2/impl/g3/webrtc.cc index 6e70be50..d8f349f4 100644 --- a/cpp/platform_v2/impl/g3/webrtc.cc +++ b/cpp/platform_v2/impl/g3/webrtc.cc @@ -1,6 +1,6 @@ #include "platform_v2/impl/g3/webrtc.h" -#include "webrtc/api/task_queue/default_task_queue_factory.h" +#include "webrtc/files/stable/webrtc/api/task_queue/default_task_queue_factory.h" namespace location { namespace nearby { diff --git a/cpp/platform_v2/impl/g3/webrtc.h b/cpp/platform_v2/impl/g3/webrtc.h index 053a30b8..35a4da10 100644 --- a/cpp/platform_v2/impl/g3/webrtc.h +++ b/cpp/platform_v2/impl/g3/webrtc.h @@ -5,7 +5,7 @@ #include "platform_v2/api/webrtc.h" #include "absl/strings/string_view.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/platform_v2/public/BUILD b/cpp/platform_v2/public/BUILD index 204d713a..0ff90145 100644 --- a/cpp/platform_v2/public/BUILD +++ b/cpp/platform_v2/public/BUILD @@ -28,11 +28,13 @@ cc_library( "//platform_v2/public:__pkg__", ], deps = [ + ":logging", "//platform_v2/api:platform", "//platform_v2/api:types", "//platform_v2/base", "//platform_v2/base:util", "//absl/base:core_headers", + "//absl/container:flat_hash_map", "//absl/time", "//absl/types:any", ], @@ -40,8 +42,12 @@ cc_library( cc_library( name = "comm", + srcs = [ + "bluetooth_classic.cc", + ], hdrs = [ "bluetooth_adapter.h", + "bluetooth_classic.h", "webrtc.h", ], visibility = [ @@ -49,10 +55,14 @@ cc_library( "//platform_v2/public:__pkg__", ], deps = [ + ":logging", + ":types", "//platform_v2/api:comm", "//platform_v2/api:platform", + "//platform_v2/base", + "//absl/container:flat_hash_map", "//absl/strings", - "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) @@ -73,10 +83,12 @@ cc_library( cc_test( name = "public_test", + size = "small", srcs = [ "atomic_boolean_test.cc", "atomic_reference_test.cc", "bluetooth_adapter_test.cc", + "bluetooth_classic_test.cc", "count_down_latch_test.cc", "crypto_test.cc", "future_test.cc", @@ -93,6 +105,7 @@ cc_test( ":logging", ":types", "//platform_v2/base", + "//platform_v2/base:test_util", "//platform_v2/impl/g3", # build_cleaner: keep "//testing/base/public:gunit_main", "//absl/synchronization", diff --git a/cpp/platform_v2/public/bluetooth_adapter.h b/cpp/platform_v2/public/bluetooth_adapter.h index f3b9df4e..beaaf4d3 100644 --- a/cpp/platform_v2/public/bluetooth_adapter.h +++ b/cpp/platform_v2/public/bluetooth_adapter.h @@ -4,55 +4,81 @@ #include #include "platform_v2/api/bluetooth_adapter.h" +#include "platform_v2/api/bluetooth_classic.h" #include "platform_v2/api/platform.h" #include "absl/strings/string_view.h" namespace location { namespace nearby { +// https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. +class BluetoothDevice final { + public: + BluetoothDevice() = default; + BluetoothDevice(const BluetoothDevice&) = default; + BluetoothDevice& operator=(const BluetoothDevice&) = default; + explicit BluetoothDevice(api::BluetoothDevice* device) : impl_(device) {} + ~BluetoothDevice() = default; + + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() + std::string GetName() const { return impl_->GetName(); } + + api::BluetoothDevice& GetImpl() { return *impl_; } + bool IsValid() const { return impl_ != nullptr; } + + private: + api::BluetoothDevice* impl_; +}; + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html -class BluetoothAdapter : public api::BluetoothAdapter { +class BluetoothAdapter final { public: using Status = api::BluetoothAdapter::Status; using ScanMode = api::BluetoothAdapter::ScanMode; BluetoothAdapter() : impl_(api::ImplementationPlatform::CreateBluetoothAdapter()) {} - ~BluetoothAdapter() override = default; + ~BluetoothAdapter() = default; BluetoothAdapter(BluetoothAdapter&&) = default; BluetoothAdapter& operator=(BluetoothAdapter&&) = default; // Synchronously sets the status of the BluetoothAdapter to 'status', and // returns true if the operation was a success. - bool SetStatus(Status status) override { return impl_->SetStatus(status); } + bool SetStatus(Status status) { return impl_->SetStatus(status); } Status GetStatus() const { return IsEnabled() ? Status::kEnabled : Status::kDisabled; } // Returns true if the BluetoothAdapter's current status is // Status::Value::kEnabled. - bool IsEnabled() const override { return impl_->IsEnabled(); } + bool IsEnabled() const { return impl_->IsEnabled(); } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getScanMode() // // Returns ScanMode::kUnknown on error. - ScanMode GetScanMode() const override { return impl_->GetScanMode(); } + ScanMode GetScanMode() const { return impl_->GetScanMode(); } // Synchronously sets the scan mode of the adapter, and returns true if the // operation was a success. - bool SetScanMode(ScanMode scan_mode) override { + bool SetScanMode(ScanMode scan_mode) { return impl_->SetScanMode(scan_mode); } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#getName() // Returns an empty string on error - std::string GetName() const override { return impl_->GetName(); } + std::string GetName() const { return impl_->GetName(); } // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String) - bool SetName(absl::string_view name) override { return impl_->SetName(name); } + bool SetName(absl::string_view name) { return impl_->SetName(name); } bool IsValid() const { return impl_ != nullptr; } + // Returns reference to platform implementation. + // This is used to communicate with platform code, and for debugging purposes. + // Returned reference will remain valid for while BluetoothAdapter object is + // itself valid. It matches Core() object lifetime. + api::BluetoothAdapter& GetImpl() { return *impl_; } + private: std::unique_ptr impl_; }; diff --git a/cpp/platform_v2/public/bluetooth_classic.cc b/cpp/platform_v2/public/bluetooth_classic.cc new file mode 100644 index 00000000..d3997d30 --- /dev/null +++ b/cpp/platform_v2/public/bluetooth_classic.cc @@ -0,0 +1,85 @@ +#include "platform_v2/public/bluetooth_classic.h" + +#include "platform_v2/public/logging.h" +#include "platform_v2/public/mutex_lock.h" + +namespace location { +namespace nearby { + +BluetoothClassicMedium::~BluetoothClassicMedium() { StopDiscovery(); } + +BluetoothSocket BluetoothClassicMedium::ConnectToService( + BluetoothDevice& remote_device, const std::string& service_uuid) { + NEARBY_LOG(INFO, + "BluetoothClassicMedium::ConnectToService: device=%p [impl=%p]", + &remote_device, &remote_device.GetImpl()); + return BluetoothSocket( + impl_->ConnectToService(remote_device.GetImpl(), service_uuid)); +} + +bool BluetoothClassicMedium::StartDiscovery(DiscoveryCallback callback) { + { + MutexLock lock(&mutex_); + if (discovery_enabled_) { + NEARBY_LOG(INFO, "BT Discovery already enabled; impl=%p", &GetImpl()); + return false; + } + discovery_callback_ = std::move(callback); + devices_.clear(); + discovery_enabled_ = true; + NEARBY_LOG(INFO, "BT Discovery enabled; impl=%p", &GetImpl()); + } + return impl_->StartDiscovery({ + .device_discovered_cb = + [this](api::BluetoothDevice& device) { + MutexLock lock(&mutex_); + auto pair = devices_.emplace( + &device, absl::make_unique()); + auto& context = *pair.first->second; + if (!pair.second) { + NEARBY_LOG(INFO, "Adding (again) device=%p, impl=%p", + &context.device, &device); + return; + } + context.device = BluetoothDevice(&device); + NEARBY_LOG(INFO, "Adding device=%p, impl=%p", &context.device, + &device); + if (!discovery_enabled_) return; + discovery_callback_.device_discovered_cb(context.device); + }, + .device_name_changed_cb = + [this](api::BluetoothDevice& device) { + MutexLock lock(&mutex_); + auto& context = *devices_[&device]; + NEARBY_LOG(INFO, "Renaming device=%p, impl=%p", &context.device, + &device); + if (!discovery_enabled_) return; + discovery_callback_.device_name_changed_cb(context.device); + }, + .device_lost_cb = + [this](api::BluetoothDevice& device) { + MutexLock lock(&mutex_); + auto item = devices_.extract(&device); + auto& context = *item.mapped(); + NEARBY_LOG(INFO, "Removing device=%p, impl=%p", &context.device, + &device); + if (!discovery_enabled_) return; + discovery_callback_.device_lost_cb(context.device); + }, + }); +} + +bool BluetoothClassicMedium::StopDiscovery() { + { + MutexLock lock(&mutex_); + if (!discovery_enabled_) return true; + discovery_enabled_ = false; + discovery_callback_ = {}; + devices_.clear(); + NEARBY_LOG(INFO, "BT Discovery disabled: impl=%p", &GetImpl()); + } + return impl_->StopDiscovery(); +} + +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/public/bluetooth_classic.h b/cpp/platform_v2/public/bluetooth_classic.h new file mode 100644 index 00000000..459d74b1 --- /dev/null +++ b/cpp/platform_v2/public/bluetooth_classic.h @@ -0,0 +1,205 @@ +#ifndef PLATFORM_V2_PUBLIC_BLUETOOTH_CLASSIC_H_ +#define PLATFORM_V2_PUBLIC_BLUETOOTH_CLASSIC_H_ + +#include +#include + +#include "platform_v2/api/bluetooth_classic.h" +#include "platform_v2/api/platform.h" +#include "platform_v2/base/byte_array.h" +#include "platform_v2/base/exception.h" +#include "platform_v2/base/input_stream.h" +#include "platform_v2/base/listeners.h" +#include "platform_v2/base/output_stream.h" +#include "platform_v2/public/bluetooth_adapter.h" +#include "platform_v2/public/mutex.h" +#include "absl/container/flat_hash_map.h" + +namespace location { +namespace nearby { + +// https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. +class BluetoothSocket final { + public: + BluetoothSocket() = default; + BluetoothSocket(const BluetoothSocket&) = default; + BluetoothSocket& operator=(const BluetoothSocket&) = default; + explicit BluetoothSocket(std::unique_ptr socket) + : impl_(socket.release()) {} + ~BluetoothSocket() = default; + + // Returns the InputStream of this connected BluetoothSocket. + InputStream& GetInputStream() { return impl_->GetInputStream(); } + + // Returns the OutputStream of this connected BluetoothSocket. + OutputStream& GetOutputStream() { return impl_->GetOutputStream(); } + + // 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. + Exception Close() { return impl_->Close(); } + + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() + BluetoothDevice GetRemoteDevice() { + return BluetoothDevice(impl_->GetRemoteDevice()); + } + + // Returns true if a socket is usable. If this method returns false, + // it is not safe to call any other method. + // NOTE(socket validity): + // Socket created by a default public constructor is not valid, because + // it is missing platform implementation. + // The only way to obtain a valid socket is through connection, such as + // an object returned by either BluetoothClassicMedium::ConnectTotService or + // BluetoothServerSocket::Accept(). + // These methods may also return an invalid socket if connection failed for + // any reason. + bool IsValid() const { return impl_ != nullptr; } + + // Returns reference to platform implementation. + // This is used to communicate with platform code, and for debugging purposes. + // Returned reference will remain valid for while BluetoothSocket object is + // itself valid. Typically BluetoothSocket lifetime matches duration of the + // connection, and is controlled by end user, since they hold the instance. + api::BluetoothSocket& GetImpl() { return *impl_; } + + private: + std::shared_ptr impl_; +}; + +// https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. +class BluetoothServerSocket final { + public: + BluetoothServerSocket() = default; + BluetoothServerSocket(const BluetoothServerSocket&) = default; + BluetoothServerSocket& operator=(const BluetoothServerSocket&) = default; + ~BluetoothServerSocket() = default; + explicit BluetoothServerSocket( + std::unique_ptr socket) + : impl_(std::move(socket)) {} + + // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept() + // + // Blocks until either: + // - at least one incoming connection request is available, or + // - ServerSocket is closed. + // On success, returns connected socket, ready to exchange data. + // Returns nullptr on error. + // Once error is reported, it is permanent, and ServerSocket has to be closed. + BluetoothSocket Accept() { return BluetoothSocket(impl_->Accept()); } + + // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close() + // + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() { return impl_->Close(); } + + bool IsValid() const { return impl_ != nullptr; } + api::BluetoothServerSocket& GetImpl() { return *impl_; } + + private: + std::shared_ptr impl_; +}; + +// Container of operations that can be performed over the Bluetooth Classic +// medium. +class BluetoothClassicMedium final { + public: + using Platform = api::ImplementationPlatform; + struct DiscoveryCallback { + // BluetoothDevice is a proxy object created as a result of BT discovery. + // Its lifetime spans between calls to device_discovered_cb and + // device_lost_cb. + // It is safe to use BluetoothDevice in device_discovered_cb() callback + // and at any time afterwards, until device_lost_cb() is called. + // It is not safe to use BluetoothDevice after returning from + // device_lost_cb() callback. + std::function device_discovered_cb = + DefaultCallback(); + std::function device_name_changed_cb = + DefaultCallback(); + std::function device_lost_cb = + DefaultCallback(); + }; + struct DeviceDiscoveryInfo { + BluetoothDevice device; + }; + + explicit BluetoothClassicMedium(BluetoothAdapter& adapter) + : impl_(Platform::CreateBluetoothClassicMedium(adapter.GetImpl())), + adapter_(adapter) {} + + ~BluetoothClassicMedium(); + + // NOTE(DiscoveryCallback): + // BluetoothDevice is a proxy object created as a result of BT discovery. + // Its lifetime spans between calls to device_discovered_cb and + // device_lost_cb. + // It is safe to use BluetoothDevice in device_discovered_cb() callback + // and at any time afterwards, until device_lost_cb() is called. + // It is not safe to use BluetoothDevice after returning from + // device_lost_cb() callback. + + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery() + // + // Returns true once the process of discovery has been initiated. + bool StartDiscovery(DiscoveryCallback callback); + + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery() + // + // Returns true once discovery is well and truly stopped; after this returns, + // there must be no more invocations of the DiscoveryCallback passed in to + // StartDiscovery(). + bool StopDiscovery(); + + // A combination of + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#createInsecureRfcommSocketToServiceRecord + // followed by + // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#connect(). + // + // service_uuid is the canonical textual representation + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a + // type 3 name-based + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) + // UUID. + // + // Returns a new BluetoothSocket. On Success, BluetoothSocket::IsValid() + // returns true. + BluetoothSocket ConnectToService(BluetoothDevice& remote_device, + const std::string& service_uuid); + + // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord + // + // service_uuid is the canonical textual representation + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of a + // type 3 name-based + // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) + // UUID. + // + // Returns a new BluetoothServerSocket. + // On Success, BluetoothServerSocket::IsValid() returns true. + BluetoothServerSocket ListenForService(const std::string& service_name, + const std::string& service_uuid) { + return BluetoothServerSocket( + impl_->ListenForService(service_name, service_uuid)); + } + + bool IsValid() const { return impl_ != nullptr; } + + api::BluetoothClassicMedium& GetImpl() { return *impl_; } + BluetoothAdapter& GetAdapter() { return adapter_; } + + private: + Mutex mutex_; + std::unique_ptr impl_; + BluetoothAdapter& adapter_; + absl::flat_hash_map> + devices_ ABSL_GUARDED_BY(mutex_); + DiscoveryCallback discovery_callback_ ABSL_GUARDED_BY(mutex_); + bool discovery_enabled_ ABSL_GUARDED_BY(mutex_) = false; +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_PUBLIC_BLUETOOTH_CLASSIC_H_ diff --git a/cpp/platform_v2/public/bluetooth_classic_test.cc b/cpp/platform_v2/public/bluetooth_classic_test.cc new file mode 100644 index 00000000..8bba6c44 --- /dev/null +++ b/cpp/platform_v2/public/bluetooth_classic_test.cc @@ -0,0 +1,197 @@ +#include "platform_v2/public/bluetooth_classic.h" + +#include + +#include "platform_v2/base/medium_environment.h" +#include "platform_v2/public/bluetooth_adapter.h" +#include "platform_v2/public/count_down_latch.h" +#include "platform_v2/public/logging.h" +#include "platform_v2/public/single_thread_executor.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "absl/time/time.h" + +namespace location { +namespace nearby { +namespace { + +class BluetoothClassicMediumTest : public ::testing::Test { + protected: + using DiscoveryCallback = BluetoothClassicMedium::DiscoveryCallback; + BluetoothClassicMediumTest() { + env_.Reset(); + adapter_a_ = std::make_unique(); + adapter_b_ = std::make_unique(); + bt_a_ = std::make_unique(*adapter_a_); + bt_b_ = std::make_unique(*adapter_b_); + adapter_a_->SetName("Device-A"); + adapter_b_->SetName("Device-B"); + adapter_a_->SetStatus(BluetoothAdapter::Status::kEnabled); + adapter_b_->SetStatus(BluetoothAdapter::Status::kEnabled); + env_.Sync(); + } + ~BluetoothClassicMediumTest() override { + env_.Sync(false); + adapter_a_->SetStatus(BluetoothAdapter::Status::kDisabled); + adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); + bt_a_.reset(); + bt_b_.reset(); + env_.Sync(false); + adapter_a_.reset(); + adapter_b_.reset(); + env_.Reset(); + } + + MediumEnvironment& env_{MediumEnvironment::Instance()}; + + std::unique_ptr adapter_a_; + std::unique_ptr adapter_b_; + std::unique_ptr bt_a_; + std::unique_ptr bt_b_; +}; + +TEST_F(BluetoothClassicMediumTest, ConstructorDestructorWorks) { + // Make sure we can create functional adapters. + ASSERT_TRUE(adapter_a_->IsValid()); + ASSERT_TRUE(adapter_b_->IsValid()); + + // Make sure we can create 2 distinct adapters. + // NOTE: multiple adapters are supported on a test platform, but not + // necessarily on every available HW platform. + // Often, HW platform supports only one BT adapter. + EXPECT_NE(&adapter_a_->GetImpl(), &adapter_b_->GetImpl()); + + // Make sure we can create functional mediums. + ASSERT_TRUE(bt_a_->IsValid()); + ASSERT_TRUE(bt_b_->IsValid()); + + // Make sure we can create 2 distinct mediums. + EXPECT_NE(&bt_a_->GetImpl(), &bt_b_->GetImpl()); +} + +TEST_F(BluetoothClassicMediumTest, CanStartDiscovery) { + adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + bt_a_->StartDiscovery(DiscoveryCallback{ + .device_discovered_cb = + [this, &found_latch](BluetoothDevice& device) { + NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); + EXPECT_EQ(device.GetName(), adapter_b_->GetName()); + found_latch.CountDown(); + }, + .device_lost_cb = + [this, &lost_latch](BluetoothDevice& device) { + NEARBY_LOG(INFO, "Device lost: %s", device.GetName().c_str()); + EXPECT_EQ(device.GetName(), adapter_b_->GetName()); + lost_latch.CountDown(); + }, + }); + adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_EQ(adapter_b_->GetScanMode(), + BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); + adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); + EXPECT_FALSE(adapter_b_->IsEnabled()); + EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result()); +} + +TEST_F(BluetoothClassicMediumTest, CanStopDiscovery) { + adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + bt_a_->StartDiscovery(DiscoveryCallback{ + .device_discovered_cb = + [this, &found_latch](BluetoothDevice& device) { + NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); + EXPECT_EQ(device.GetName(), adapter_b_->GetName()); + found_latch.CountDown(); + }, + .device_lost_cb = + [this, &lost_latch](BluetoothDevice& device) { + NEARBY_LOG(INFO, "Device lost: %s", device.GetName().c_str()); + EXPECT_EQ(device.GetName(), adapter_b_->GetName()); + lost_latch.CountDown(); + }, + }); + adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_EQ(adapter_b_->GetScanMode(), + BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); + bt_a_->StopDiscovery(); + adapter_b_->SetStatus(BluetoothAdapter::Status::kDisabled); + EXPECT_FALSE(adapter_b_->IsEnabled()); + EXPECT_FALSE(lost_latch.Await(absl::Milliseconds(1000)).result()); +} + +TEST_F(BluetoothClassicMediumTest, CanListenForService) { + adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); + CountDownLatch found_latch(1); + bt_a_->StartDiscovery(DiscoveryCallback{ + .device_discovered_cb = + [this, &found_latch](BluetoothDevice& device) { + NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); + EXPECT_EQ(device.GetName(), adapter_b_->GetName()); + found_latch.CountDown(); + }, + }); + adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_EQ(adapter_b_->GetScanMode(), + BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); + std::string service_name{"service"}; + std::string service_uuid("service-uuid"); + BluetoothServerSocket server_socket = + bt_b_->ListenForService(service_name, service_uuid); + EXPECT_TRUE(server_socket.IsValid()); + server_socket.Close(); +} + +TEST_F(BluetoothClassicMediumTest, CanConnectToService) { + adapter_a_->SetScanMode(BluetoothAdapter::ScanMode::kConnectable); + CountDownLatch found_latch(1); + BluetoothDevice* discovered_device = nullptr; + bt_a_->StartDiscovery(DiscoveryCallback{ + .device_discovered_cb = + [this, &found_latch, &discovered_device](BluetoothDevice& device) { + NEARBY_LOG(INFO, "Device discovered: %s", device.GetName().c_str()); + EXPECT_EQ(device.GetName(), adapter_b_->GetName()); + discovered_device = &device; + found_latch.CountDown(); + }, + }); + adapter_b_->SetScanMode(BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_EQ(adapter_b_->GetScanMode(), + BluetoothAdapter::ScanMode::kConnectableDiscoverable); + EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); + std::string service_name{"service"}; + std::string service_uuid("service-uuid"); + BluetoothServerSocket server_socket = + bt_b_->ListenForService(service_name, service_uuid); + EXPECT_TRUE(server_socket.IsValid()); + BluetoothSocket socket_a; + BluetoothSocket socket_b; + EXPECT_FALSE(socket_a.IsValid()); + EXPECT_FALSE(socket_b.IsValid()); + { + SingleThreadExecutor server_executor; + SingleThreadExecutor client_executor; + client_executor.Execute( + [this, &socket_a, discovered_device, &service_uuid, &server_socket]() { + socket_a = bt_a_->ConnectToService(*discovered_device, service_uuid); + if (!socket_a.IsValid()) server_socket.Close(); + }); + server_executor.Execute( + [&socket_b, &server_socket]() { + socket_b = server_socket.Accept(); + if (!socket_b.IsValid()) server_socket.Close(); + }); + } + EXPECT_TRUE(socket_a.IsValid()); + EXPECT_TRUE(socket_b.IsValid()); + server_socket.Close(); +} + +} // namespace +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/public/webrtc.h b/cpp/platform_v2/public/webrtc.h index a5bc50de..f0700cef 100644 --- a/cpp/platform_v2/public/webrtc.h +++ b/cpp/platform_v2/public/webrtc.h @@ -5,7 +5,7 @@ #include "platform_v2/api/platform.h" #include "platform_v2/api/webrtc.h" -#include "webrtc/api/peer_connection_interface.h" +#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { From fdd803842864ea0e189f63de4542c3290cbd1bf9 Mon Sep 17 00:00:00 2001 From: Himanshu Jaju Date: Thu, 4 Jun 2020 20:19:58 +0100 Subject: [PATCH 2/2] Fix oss.py script - Adds newline fix from release branch - Adds transformation for MessageLite from google3 to open source Change-Id: Ie6c3f5263417fbbccc67835eb22fbe568a249125 --- script/oss.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/script/oss.py b/script/oss.py index bcc1673d..e05dcc1b 100755 --- a/script/oss.py +++ b/script/oss.py @@ -88,6 +88,7 @@ def post_process_oss_files(path, args): else: top_dirs = ["cpp", "proto"] transforms = ( + ("::google3_proto_compat::MessageLite", "::google::protobuf::MessageLite"), ("third_party/webrtc/files/stable/", ""), ("webrtc/files/stable/", ""), ("third_party/", ""), @@ -154,8 +155,9 @@ def post_process_oss_files(path, args): continue if add_proto_lite_runtime and line.startswith("option "): - lines.append("option optimize_for = LITE_RUNTIME;") - modified = True + if not line.startswith("option optimize_for = LITE_RUNTIME"): + lines.append("option optimize_for = LITE_RUNTIME;\n") + modified = True # LITE_RUNTIME should be added only once per file. add_proto_lite_runtime = False