From c673bf6ac005ca6b362048b56218445e52d33de2 Mon Sep 17 00:00:00 2001 From: Alexey Polyudov Date: Tue, 25 Aug 2020 11:15:38 -0700 Subject: [PATCH] Roll forward to cl/328359974 Change-Id: If2b57ecc852aecf7dea454648f485fd7c08e72a9 --- cpp/core_v2/BUILD | 5 +- cpp/core_v2/core.cc | 3 +- cpp/core_v2/core.h | 3 +- cpp/core_v2/internal/BUILD | 6 + cpp/core_v2/internal/base_endpoint_channel.cc | 3 +- cpp/core_v2/internal/base_pcp_handler.cc | 229 +++--- cpp/core_v2/internal/base_pcp_handler.h | 106 ++- cpp/core_v2/internal/base_pcp_handler_test.cc | 278 +++++-- cpp/core_v2/internal/ble_advertisement.cc | 180 ++--- cpp/core_v2/internal/ble_advertisement.h | 59 +- .../internal/ble_advertisement_test.cc | 356 ++++++--- cpp/core_v2/internal/ble_endpoint_channel.cc | 45 ++ cpp/core_v2/internal/ble_endpoint_channel.h | 29 + cpp/core_v2/internal/bluetooth_device_name.cc | 37 +- cpp/core_v2/internal/bluetooth_device_name.h | 10 +- .../internal/bluetooth_device_name_test.cc | 37 +- cpp/core_v2/internal/client_proxy.cc | 60 +- cpp/core_v2/internal/client_proxy.h | 10 +- cpp/core_v2/internal/client_proxy_test.cc | 24 +- cpp/core_v2/internal/encryption_runner.cc | 24 +- cpp/core_v2/internal/encryption_runner.h | 4 +- .../internal/endpoint_channel_manager.cc | 5 + .../internal/endpoint_channel_manager.h | 9 +- cpp/core_v2/internal/endpoint_manager.cc | 9 +- cpp/core_v2/internal/endpoint_manager.h | 8 +- cpp/core_v2/internal/endpoint_manager_test.cc | 19 +- cpp/core_v2/internal/mediums/BUILD | 5 + cpp/core_v2/internal/mediums/ble.cc | 269 +++++++ cpp/core_v2/internal/mediums/ble.h | 162 ++++ cpp/core_v2/internal/mediums/ble_test.cc | 162 ++++ .../internal/mediums/bluetooth_classic.cc | 6 + .../internal/mediums/bluetooth_classic.h | 3 + cpp/core_v2/internal/mediums/mediums.cc | 2 + cpp/core_v2/internal/mediums/mediums.h | 5 + cpp/core_v2/internal/mediums/wifi_lan.cc | 14 +- cpp/core_v2/internal/mediums/wifi_lan.h | 5 +- cpp/core_v2/internal/mediums/wifi_lan_test.cc | 21 +- .../internal/mock_service_controller.h | 3 +- cpp/core_v2/internal/offline_frames.cc | 133 +++- cpp/core_v2/internal/offline_frames.h | 38 +- cpp/core_v2/internal/offline_frames_test.cc | 64 +- .../internal/offline_service_controller.cc | 8 +- .../internal/offline_service_controller.h | 7 +- .../offline_service_controller_test.cc | 119 +-- .../internal/offline_simulation_user.cc | 27 +- .../internal/offline_simulation_user.h | 35 +- .../internal/p2p_cluster_pcp_handler.cc | 753 ++++++++++++------ .../internal/p2p_cluster_pcp_handler.h | 73 +- .../internal/p2p_cluster_pcp_handler_test.cc | 93 ++- .../p2p_point_to_point_pcp_handler.cc | 3 +- .../internal/p2p_point_to_point_pcp_handler.h | 6 +- cpp/core_v2/internal/p2p_star_pcp_handler.cc | 4 +- cpp/core_v2/internal/p2p_star_pcp_handler.h | 6 +- cpp/core_v2/internal/payload_manager_test.cc | 67 +- cpp/core_v2/internal/pcp_handler.h | 5 +- cpp/core_v2/internal/pcp_manager.cc | 7 +- cpp/core_v2/internal/pcp_manager.h | 17 +- cpp/core_v2/internal/pcp_manager_test.cc | 67 +- cpp/core_v2/internal/service_controller.h | 6 +- .../internal/service_controller_router.cc | 34 +- .../internal/service_controller_router.h | 1 + .../service_controller_router_test.cc | 12 +- cpp/core_v2/internal/simulation_user.cc | 22 +- cpp/core_v2/internal/simulation_user.h | 26 +- cpp/core_v2/internal/wifi_lan_service_info.cc | 41 +- cpp/core_v2/internal/wifi_lan_service_info.h | 10 +- .../internal/wifi_lan_service_info_test.cc | 30 +- cpp/core_v2/listeners.h | 9 +- cpp/core_v2/options.h | 65 +- cpp/core_v2/params.h | 11 +- cpp/core_v2/status.h | 1 + cpp/platform/impl/g3/BUILD | 1 - cpp/platform/impl/sample/BUILD | 6 +- cpp/platform/impl/shared/sample/BUILD | 1 - cpp/platform_v2/api/ble.h | 77 +- cpp/platform_v2/api/bluetooth_adapter.h | 3 + cpp/platform_v2/api/bluetooth_classic.h | 5 + cpp/platform_v2/api/wifi_lan.h | 10 +- cpp/platform_v2/base/BUILD | 4 + cpp/platform_v2/base/bluetooth_utils.cc | 61 ++ cpp/platform_v2/base/bluetooth_utils.h | 32 + cpp/platform_v2/base/bluetooth_utils_test.cc | 75 ++ cpp/platform_v2/base/byte_array.h | 2 +- cpp/platform_v2/base/medium_environment.cc | 188 +++++ cpp/platform_v2/base/medium_environment.h | 73 +- cpp/platform_v2/impl/g3/BUILD | 7 +- cpp/platform_v2/impl/g3/ble.cc | 341 ++++++++ cpp/platform_v2/impl/g3/ble.h | 213 +++++ cpp/platform_v2/impl/g3/bluetooth_adapter.cc | 40 +- cpp/platform_v2/impl/g3/bluetooth_adapter.h | 47 +- cpp/platform_v2/impl/g3/bluetooth_classic.cc | 17 +- cpp/platform_v2/impl/g3/bluetooth_classic.h | 5 +- cpp/platform_v2/impl/g3/platform.cc | 4 +- cpp/platform_v2/impl/g3/wifi_lan.cc | 51 +- cpp/platform_v2/impl/g3/wifi_lan.h | 19 +- cpp/platform_v2/impl/shared/BUILD | 4 +- cpp/platform_v2/public/BUILD | 4 + cpp/platform_v2/public/ble.cc | 127 +++ cpp/platform_v2/public/ble.h | 146 ++++ cpp/platform_v2/public/ble_test.cc | 189 +++++ cpp/platform_v2/public/bluetooth_adapter.h | 23 + cpp/platform_v2/public/bluetooth_classic.h | 3 + cpp/platform_v2/public/wifi_lan.cc | 40 +- cpp/platform_v2/public/wifi_lan.h | 2 + cpp/platform_v2/public/wifi_lan_test.cc | 28 +- proto/connections/offline_wire_formats.proto | 6 + proto/connections_enums.proto | 9 +- proto/error_code_enums.proto | 103 ++- proto/magic_pair_enums.proto | 3 + proto/sharing_enums.proto | 19 + 110 files changed, 4758 insertions(+), 1245 deletions(-) create mode 100644 cpp/core_v2/internal/ble_endpoint_channel.cc create mode 100644 cpp/core_v2/internal/ble_endpoint_channel.h create mode 100644 cpp/core_v2/internal/mediums/ble.cc create mode 100644 cpp/core_v2/internal/mediums/ble.h create mode 100644 cpp/core_v2/internal/mediums/ble_test.cc create mode 100644 cpp/platform_v2/base/bluetooth_utils.cc create mode 100644 cpp/platform_v2/base/bluetooth_utils.h create mode 100644 cpp/platform_v2/base/bluetooth_utils_test.cc create mode 100644 cpp/platform_v2/impl/g3/ble.cc create mode 100644 cpp/platform_v2/impl/g3/ble.h create mode 100644 cpp/platform_v2/public/ble.cc create mode 100644 cpp/platform_v2/public/ble.h create mode 100644 cpp/platform_v2/public/ble_test.cc diff --git a/cpp/core_v2/BUILD b/cpp/core_v2/BUILD index eed3c011..993686cc 100644 --- a/cpp/core_v2/BUILD +++ b/cpp/core_v2/BUILD @@ -6,9 +6,7 @@ cc_library( hdrs = [ "core.h", ], - visibility = [ - "//core_v2:__subpackages__", - ], + visibility = ["//visibility:private"], deps = [ ":core_types", "//core_v2/internal", @@ -42,6 +40,7 @@ cc_library( "//platform_v2/public:comm", "//platform_v2/public:logging", "//platform_v2/public:types", + "//proto:connections_enums_portable_proto", "//absl/strings", "//absl/types:variant", ], diff --git a/cpp/core_v2/core.cc b/cpp/core_v2/core.cc index 412f987c..3b410f48 100644 --- a/cpp/core_v2/core.cc +++ b/cpp/core_v2/core.cc @@ -54,10 +54,11 @@ void Core::StopDiscovery(ResultCallback callback) { void Core::RequestConnection(absl::string_view endpoint_id, ConnectionRequestInfo info, + ConnectionOptions options, ResultCallback callback) { assert(!endpoint_id.empty()); - router_.RequestConnection(&client_, endpoint_id, info, callback); + router_.RequestConnection(&client_, endpoint_id, info, options, callback); } void Core::AcceptConnection(absl::string_view endpoint_id, diff --git a/cpp/core_v2/core.h b/cpp/core_v2/core.h index 3d4cd6a1..37509763 100644 --- a/cpp/core_v2/core.h +++ b/cpp/core_v2/core.h @@ -104,7 +104,8 @@ class Core { // issue with Bluetooth/WiFi. // Status::STATUS_ERROR if we failed to connect for any other reason. void RequestConnection(absl::string_view endpoint_id, - ConnectionRequestInfo info, ResultCallback callback); + ConnectionRequestInfo info, ConnectionOptions options, + ResultCallback callback); // Accepts a connection to a remote endpoint. This method must be called // before Payloads can be exchanged with the remote endpoint. diff --git a/cpp/core_v2/internal/BUILD b/cpp/core_v2/internal/BUILD index 8315217b..e68a6c28 100644 --- a/cpp/core_v2/internal/BUILD +++ b/cpp/core_v2/internal/BUILD @@ -4,6 +4,7 @@ cc_library( "base_endpoint_channel.cc", "base_pcp_handler.cc", "ble_advertisement.cc", + "ble_endpoint_channel.cc", "bluetooth_device_name.cc", "bluetooth_endpoint_channel.cc", "client_proxy.cc", @@ -28,6 +29,7 @@ cc_library( "base_endpoint_channel.h", "base_pcp_handler.h", "ble_advertisement.h", + "ble_endpoint_channel.h", "bluetooth_device_name.h", "bluetooth_endpoint_channel.h", "client_proxy.h", @@ -69,8 +71,10 @@ cc_library( "//proto:connections_enums_portable_proto", "//securegcm:ukey2", "//absl/base:core_headers", + "//absl/container:btree", "//absl/container:flat_hash_map", "//absl/container:flat_hash_set", + "//absl/functional:bind_front", "//absl/memory", "//absl/strings", "//absl/time", @@ -96,6 +100,7 @@ cc_library( deps = [ ":internal", "//core_v2:core_types", + "//platform_v2/base", "//platform_v2/base:test_util", "//platform_v2/public:types", "//testing/base/public:gunit", @@ -107,6 +112,7 @@ cc_library( cc_test( name = "core_v2_internal_test", size = "small", + timeout = "moderate", srcs = [ "base_endpoint_channel_test.cc", "base_pcp_handler_test.cc", diff --git a/cpp/core_v2/internal/base_endpoint_channel.cc b/cpp/core_v2/internal/base_endpoint_channel.cc index 569135f5..b90c19cf 100644 --- a/cpp/core_v2/internal/base_endpoint_channel.cc +++ b/cpp/core_v2/internal/base_endpoint_channel.cc @@ -108,8 +108,7 @@ ExceptionOr BaseEndpointChannel::Read() { // If encryption is enabled, decode the message. std::string input(std::move(result)); std::unique_ptr decrypted_data = - crypto_context_->DecodeMessageFromPeer( - std::string(std::move(result))); + crypto_context_->DecodeMessageFromPeer(input); if (decrypted_data) { result = ByteArray(std::move(*decrypted_data)); } else { diff --git a/cpp/core_v2/internal/base_pcp_handler.cc b/cpp/core_v2/internal/base_pcp_handler.cc index 34d95e72..44f6779d 100644 --- a/cpp/core_v2/internal/base_pcp_handler.cc +++ b/cpp/core_v2/internal/base_pcp_handler.cc @@ -8,11 +8,13 @@ #include "core_v2/internal/offline_frames.h" #include "core_v2/internal/pcp_handler.h" +#include "core_v2/options.h" #include "platform_v2/public/logging.h" #include "platform_v2/public/system_clock.h" #include "securegcm/d2d_connection_context_v1.h" #include "securegcm/ukey2_handshake.h" #include "absl/container/flat_hash_set.h" +#include "absl/strings/escaping.h" #include "absl/types/span.h" namespace location { @@ -25,9 +27,11 @@ using ::securegcm::UKey2Handshake; constexpr absl::Duration BasePcpHandler::kConnectionRequestReadTimeout; constexpr absl::Duration BasePcpHandler::kRejectedConnectionCloseDelay; -BasePcpHandler::BasePcpHandler(EndpointManager* endpoint_manager, +BasePcpHandler::BasePcpHandler(Mediums* mediums, + EndpointManager* endpoint_manager, EndpointChannelManager* channel_manager, Pcp pcp) - : endpoint_manager_(endpoint_manager), + : mediums_(mediums), + endpoint_manager_(endpoint_manager), channel_manager_(channel_manager), pcp_(pcp) {} @@ -58,25 +62,27 @@ Status BasePcpHandler::StartAdvertising(ClientProxy* client, const ConnectionOptions& options, const ConnectionRequestInfo& info) { Future response; + ConnectionOptions advertising_options = options.CompatibleOptions(); RunOnPcpHandlerThread( - [this, client, &service_id, &info, &options, &response]() { - auto result = StartAdvertisingImpl(client, service_id, - client->GenerateLocalEndpointId(), - info.name, options); + [this, client, &service_id, &info, &advertising_options, &response]() { + auto result = StartAdvertisingImpl( + client, service_id, client->GetLocalEndpointId(), + info.endpoint_info, advertising_options); if (!result.status.Ok()) { response.Set(result.status); return; } // Now that we've succeeded, mark the client as advertising. - advertising_options_ = options; + advertising_options_ = advertising_options; advertising_listener_ = info.listener; client->StartedAdvertising(service_id, GetStrategy(), info.listener, absl::MakeSpan(result.mediums)); response.Set({Status::kSuccess}); }); - return WaitForResult(absl::StrCat("StartAdvertising(", info.name, ")"), - client->GetClientId(), &response); + return WaitForResult( + absl::StrCat("StartAdvertising(", std::string(info.endpoint_info), ")"), + client->GetClientId(), &response); } void BasePcpHandler::StopAdvertising(ClientProxy* client) { @@ -95,10 +101,11 @@ Status BasePcpHandler::StartDiscovery(ClientProxy* client, const ConnectionOptions& options, const DiscoveryListener& listener) { Future response; + ConnectionOptions discovery_options = options.CompatibleOptions(); RunOnPcpHandlerThread( - [this, client, service_id, options, &listener, &response]() { + [this, client, service_id, discovery_options, &listener, &response]() { // Ask the implementation to attempt to start discovery. - auto result = StartDiscoveryImpl(client, service_id, options); + auto result = StartDiscoveryImpl(client, service_id, discovery_options); if (!result.status.Ok()) { response.Set(result.status); return; @@ -106,7 +113,7 @@ Status BasePcpHandler::StartDiscovery(ClientProxy* client, // Now that we've succeeded, mark the client as discovering and clear // out any old endpoints we had discovered. - discovery_options_ = options; + discovery_options_ = discovery_options; discovered_endpoints_.clear(); client->StartedDiscovery(service_id, GetStrategy(), listener, absl::MakeSpan(result.mediums)); @@ -125,7 +132,7 @@ void BasePcpHandler::StopDiscovery(ClientProxy* client) { latch.CountDown(); }); - WaitForLatch("stopDiscovery", &latch); + WaitForLatch("StopDiscovery", &latch); } void BasePcpHandler::WaitForLatch(const std::string& method_name, @@ -148,10 +155,12 @@ Status BasePcpHandler::WaitForResult(const std::string& method_name, NEARBY_LOG(INFO, "waiting for future to complete"); ExceptionOr result = future->Get(); if (!result.ok()) { - NEARBY_LOG(INFO, "Future completed with exception: %d", result.exception()); + NEARBY_LOG(INFO, "Future:[%s] completed with exception: %d", + method_name.c_str(), result.exception()); return {Status::kError}; } - NEARBY_LOG(INFO, "Future completed with status: %d", result.result().value); + NEARBY_LOG(INFO, "Future:[%s] completed with status: %d", method_name.c_str(), + result.result().value); return result.result(); } @@ -218,11 +227,12 @@ void BasePcpHandler::OnEncryptionSuccessRunnable( endpoint_manager_->RegisterEndpoint( connection_info.client, endpoint_id, { - .remote_endpoint_name = connection_info.remote_endpoint_name, + .remote_endpoint_info = connection_info.remote_endpoint_info, .authentication_token = auth_token, .raw_authentication_token = raw_auth_token, .is_incoming_connection = connection_info.is_incoming, }, + connection_info.options, std::move(connection_info.channel), connection_info.listener); if (connection_info.result != nullptr) { @@ -265,9 +275,10 @@ void BasePcpHandler::OnEncryptionFailureRunnable( Status BasePcpHandler::RequestConnection(ClientProxy* client, const std::string& endpoint_id, - const ConnectionRequestInfo& info) { + const ConnectionRequestInfo& info, + const ConnectionOptions& options) { Future result; - RunOnPcpHandlerThread([this, client, &info, endpoint_id, &result]() { + RunOnPcpHandlerThread([this, client, &info, options, endpoint_id, &result]() { absl::Time start_time = SystemClock::ElapsedRealtime(); // If we already have a pending connection, then we shouldn't allow any more @@ -288,8 +299,7 @@ Status BasePcpHandler::RequestConnection(ClientProxy* client, return; } - std::vector endpoints; - auto endpoint = GetDiscoveredEndpoint(endpoint_id); + DiscoveredEndpoint* endpoint = GetDiscoveredEndpoint(endpoint_id); if (endpoint == nullptr) { NEARBY_LOG(INFO, "Discovered endpoint not found: id=%s", endpoint_id.c_str()); @@ -297,24 +307,24 @@ Status BasePcpHandler::RequestConnection(ClientProxy* client, return; } - auto webrtc_endpoint = absl::make_unique( - DiscoveredEndpoint{endpoint->endpoint_id, endpoint->endpoint_name, - endpoint->service_id, - proto::connections::Medium::WEB_RTC}, - CreatePeerIdFromAdvertisement(endpoint->service_id, - endpoint->endpoint_id, - endpoint->endpoint_name)); - endpoints.push_back(endpoint); - endpoints.push_back(webrtc_endpoint.get()); - - std::sort(endpoints.begin(), endpoints.end(), - [this](DiscoveredEndpoint* a, DiscoveredEndpoint* b) -> bool { - return IsPreferred(*a, *b); - }); + if (discovery_options_.allowed.web_rtc) { + auto webrtc_endpoint = std::make_shared( + DiscoveredEndpoint{endpoint->endpoint_id, endpoint->endpoint_info, + endpoint->service_id, + proto::connections::Medium::WEB_RTC}, + CreatePeerIdFromAdvertisement(endpoint->service_id, + endpoint->endpoint_id, + endpoint->endpoint_info)); + OnEndpointFound(client, webrtc_endpoint); + } + auto endpoints = GetDiscoveredEndpoints(endpoint_id); std::unique_ptr channel; ConnectImplResult connect_impl_result; + // TODO(b/156634369): add GetRemoteBluetoothMacAddressEndpoint here for + // valid remote mac address. + for (auto connect_endpoint : endpoints) { connect_impl_result = ConnectImpl(client, connect_endpoint); if (connect_impl_result.status.Ok()) { @@ -338,7 +348,7 @@ Status BasePcpHandler::RequestConnection(ClientProxy* client, // The first message we have to send, after connecting, is to tell the // endpoint about ourselves. Exception write_exception = WriteConnectionRequestFrame( - channel.get(), client->GenerateLocalEndpointId(), info.name, nonce, + channel.get(), client->GetLocalEndpointId(), info.endpoint_info, nonce, GetConnectionMediumsByPriority()); if (!write_exception.Ok()) { NEARBY_LOG(INFO, "Failed to send connection request: id=%s", @@ -354,18 +364,19 @@ Status BasePcpHandler::RequestConnection(ClientProxy* client, // We've successfully connected to the device, and are now about to jump on // to the EncryptionRunner thread to start running our encryption protocol. // We'll mark ourselves as pending in case we get another call to - // requestConnection or OnIncomingConnection, so that we can cancel the + // RequestConnection or OnIncomingConnection, so that we can cancel the // connection if needed. EndpointChannel* endpoint_channel = pending_connections_ .emplace(endpoint_id, PendingConnectionInfo{ .client = client, - .remote_endpoint_name = endpoint->endpoint_name, + .remote_endpoint_info = endpoint->endpoint_info, .nonce = nonce, .is_incoming = false, .start_time = start_time, .listener = info.listener, + .options = options, .result = MakeSwapper(&result), .channel = std::move(channel), }) @@ -374,20 +385,21 @@ Status BasePcpHandler::RequestConnection(ClientProxy* client, NEARBY_LOG(INFO, "Initiating secure connection: id=%s", endpoint_id.c_str()); // Next, we'll set up encryption. When it's done, our future will return and - // requestConnection() will finish. + // RequestConnection() will finish. encryption_runner_.StartClient(client, endpoint_id, endpoint_channel, GetResultListener()); }); NEARBY_LOG(INFO, "Waiting for connection to complete: id=%s", endpoint_id.c_str()); auto status = - WaitForResult(absl::StrCat("requestConnection(", endpoint_id, ")"), + WaitForResult(absl::StrCat("RequestConnection(", endpoint_id, ")"), client->GetClientId(), &result); NEARBY_LOG(INFO, "Wait is complete: id=%s; status=%d", endpoint_id.c_str(), status.value); return status; } +// Get any single discovered endpoint for a given endpoint_id. BasePcpHandler::DiscoveredEndpoint* BasePcpHandler::GetDiscoveredEndpoint( const std::string& endpoint_id) { auto it = discovered_endpoints_.find(endpoint_id); @@ -397,6 +409,20 @@ BasePcpHandler::DiscoveredEndpoint* BasePcpHandler::GetDiscoveredEndpoint( return it->second.get(); } +std::vector +BasePcpHandler::GetDiscoveredEndpoints(const std::string& endpoint_id) { + std::vector result; + auto it = discovered_endpoints_.equal_range(endpoint_id); + for (auto item = it.first; item != it.second; item++) { + result.push_back(item->second.get()); + } + std::sort(result.begin(), result.end(), + [this](DiscoveredEndpoint* a, DiscoveredEndpoint* b) -> bool { + return IsPreferred(*a, *b); + }); + return result; +} + void BasePcpHandler::PendingConnectionInfo::SetCryptoContext( std::unique_ptr ukey2) { this->ukey2 = std::move(ukey2); @@ -432,10 +458,10 @@ bool BasePcpHandler::CanReceiveIncomingConnection(ClientProxy* client) const { Exception BasePcpHandler::WriteConnectionRequestFrame( EndpointChannel* endpoint_channel, const std::string& local_endpoint_id, - const std::string& local_endpoint_name, std::int32_t nonce, + const ByteArray& local_endpoint_info, std::int32_t nonce, const std::vector& supported_mediums) { return endpoint_channel->Write(parser::ForConnectionRequest( - local_endpoint_id, local_endpoint_name, nonce, supported_mediums)); + local_endpoint_id, local_endpoint_info, nonce, supported_mediums)); } void BasePcpHandler::ProcessPreConnectionInitiationFailure( @@ -529,7 +555,7 @@ Status BasePcpHandler::AcceptConnection( response.Set({Status::kSuccess}); }); - return WaitForResult(absl::StrCat("acceptConnection(", endpoint_id, ")"), + return WaitForResult(absl::StrCat("AcceptConnection(", endpoint_id, ")"), client->GetClientId(), &response); } @@ -581,7 +607,7 @@ Status BasePcpHandler::RejectConnection(ClientProxy* client, response.Set({Status::kSuccess}); }); - return WaitForResult(absl::StrCat("rejectConnection(", endpoint_id, ")"), + return WaitForResult(absl::StrCat("RejectConnection(", endpoint_id, ")"), client->GetClientId(), &response); } @@ -648,44 +674,52 @@ ConnectionOptions BasePcpHandler::GetConnectionOptions() const { return advertising_options_; } +ConnectionOptions BasePcpHandler::GetDiscoveryOptions() const { + return discovery_options_; +} + void BasePcpHandler::OnEndpointFound( - ClientProxy* client, - std::shared_ptr endpoint) { + ClientProxy* client, std::shared_ptr endpoint) { // Check if we've seen this endpoint ID before. std::string& endpoint_id = endpoint->endpoint_id; - BasePcpHandler::DiscoveredEndpoint* previously_discovered_endpoint = - GetDiscoveredEndpoint(endpoint_id); - NEARBY_LOG(INFO, "OnEndpointFound: id='%s' [enter]", endpoint_id.c_str()); - if (previously_discovered_endpoint == nullptr) { - // If this is the first medium we've discovered this endpoint over, then add - // it to the map. - const auto& owned_endpoint = - discovered_endpoints_.emplace(endpoint_id, std::move(endpoint)) - .first->second; + auto range = discovered_endpoints_.equal_range(endpoint->endpoint_id); + + DiscoveredEndpoint* owned_endpoint = nullptr; + for (auto& item = range.first; item != range.second; ++item) { + auto& discovered_endpoint = item->second; + if (discovered_endpoint->medium != endpoint->medium) continue; + // Check if there was a info change. If there was, report the previous + // endpoint as lost. + if (discovered_endpoint->endpoint_info != endpoint->endpoint_info) { + OnEndpointLost(client, *discovered_endpoint); + discovered_endpoint = endpoint; // Replace endpoint. + OnEndpointFound(client, std::move(endpoint)); + return; + } else { + owned_endpoint = endpoint.get(); + break; + } + } + + if (!owned_endpoint) { + owned_endpoint = + discovered_endpoints_.emplace(endpoint_id, std::move(endpoint)) + ->second.get(); + } + + // Range is empty: this is the first endpoint we discovered so far. + // Report this endpoint_id to client. + if (range.first == range.second) { NEARBY_LOG(INFO, "Adding new endpoint: id=%s", endpoint_id.c_str()); // And, as it's the first time, report it to the client. client->OnEndpointFound( owned_endpoint->service_id, owned_endpoint->endpoint_id, - owned_endpoint->endpoint_name, owned_endpoint->medium); - } else if (previously_discovered_endpoint->endpoint_name != - endpoint->endpoint_name) { - // If we've already seen this endpoint before, check if there was a name - // change. If there was, report the previous endpoint as lost. - NEARBY_LOG(INFO, "Switch to new endpoint: id=%s", endpoint_id.c_str()); - - OnEndpointLost(client, *previously_discovered_endpoint); - OnEndpointFound(client, std::move(endpoint)); + owned_endpoint->endpoint_info, owned_endpoint->medium); } else { - // Otherwise, we need to see if the medium we discovered the endpoint over - // this time is better than the medium we originally discovered the endpoint - // over. - NEARBY_LOG(INFO, "Rediscovered endpoint on new media: id=%s", - endpoint_id.c_str()); - if (IsPreferred(*endpoint, *previously_discovered_endpoint)) { - discovered_endpoints_.insert_or_assign(endpoint_id, std::move(endpoint)); - } + NEARBY_LOGS(INFO) << "Adding new medium for endpoint: id=" << endpoint_id + << "; medium=" << owned_endpoint->medium; } } @@ -699,19 +733,22 @@ void BasePcpHandler::OnEndpointLost( return; } - // Validate that the cached endpoint has the same name as the one reported as - // onLost. If the name differs, then no-op. This likely means that the remote - // device changed their name. We reported onFound for the new name and are - // just now figuring out that we lost the old name. - if (discovered_endpoint->endpoint_name != endpoint.endpoint_name) { + // Validate that the cached endpoint has the same info as the one reported as + // onLost. If the info differs, then no-op. This likely means that the remote + // device changed their info. We reported onFound for the new info and are + // just now figuring out that we lost the old info. + if (discovered_endpoint->endpoint_info != endpoint.endpoint_info) { NEARBY_LOG(INFO, "Previous endpoint name mismatch; passed=%s; expected=%s", - endpoint.endpoint_name.c_str(), - discovered_endpoint->endpoint_name.c_str()); + absl::BytesToHexString(endpoint.endpoint_info.data()).c_str(), + absl::BytesToHexString(discovered_endpoint->endpoint_info.data()) + .c_str()); return; } auto item = discovered_endpoints_.extract(endpoint.endpoint_id); - client->OnEndpointLost(endpoint.service_id, endpoint.endpoint_id); + if (!discovered_endpoints_.count(endpoint.endpoint_id)) { + client->OnEndpointLost(endpoint.service_id, endpoint.endpoint_id); + } } bool BasePcpHandler::IsPreferred( @@ -732,17 +769,24 @@ bool BasePcpHandler::IsPreferred( return false; } } - NEARBY_LOG(FATAL, "Failed to determine preferred medium; bailing out"); + std::string medium_string; + for (const auto& medium : mediums) { + absl::StrAppend(&medium_string, medium, "; "); + } + NEARBY_LOG(FATAL, + "Failed to determine preferred medium; bailing out; mediums=%s; " + "new=%d; old=%d", + medium_string.c_str(), new_endpoint.medium, old_endpoint.medium); return false; } Exception BasePcpHandler::OnIncomingConnection( - ClientProxy* client, const std::string& remote_device_name, + ClientProxy* client, const ByteArray& remote_endpoint_info, std::unique_ptr channel, proto::connections::Medium medium) { absl::Time start_time = SystemClock::ElapsedRealtime(); - // Fixes an NPE in ClientProxy.OnConnectionResult. The crash happened when + // Fixes an NPE in ClientProxy.OnConnectionAccepted. The crash happened when // the client stopped advertising and we nulled out state, followed by an // incoming connection where we attempted to check that state. if (!client->IsAdvertising()) { @@ -763,7 +807,8 @@ Exception BasePcpHandler::OnIncomingConnection( ERROR, "Failed to parse incoming connection request; client_id=0x%" PRIX64 "; device=%s", - client->GetClientId(), remote_device_name.c_str()); + client->GetClientId(), + absl::BytesToHexString(remote_endpoint_info.data()).c_str()); ProcessPreConnectionInitiationFailure("", channel.get(), {Status::kError}, nullptr); return {Exception::kSuccess}; @@ -777,7 +822,8 @@ Exception BasePcpHandler::OnIncomingConnection( NEARBY_LOG(INFO, "Incoming connection request; client_id=0x%" PRIX64 "; device=%s; id=%s", - client->GetClientId(), remote_device_name.c_str(), + client->GetClientId(), + absl::BytesToHexString(remote_endpoint_info.data()).c_str(), connection_request.endpoint_id().c_str()); if (client->IsConnectedToEndpoint(connection_request.endpoint_id())) { return {Exception::kIo}; @@ -801,20 +847,20 @@ Exception BasePcpHandler::OnIncomingConnection( // EndpointInfo. The legacy field stores it as a string while the newer field // stores it as a byte array. We'll attempt to grab from the newer field, but // will accept the older string if it's all that exists. - const std::string endpoint_name = connection_request.has_endpoint_info() - ? connection_request.endpoint_info() - : connection_request.endpoint_name(); + const ByteArray endpoint_info{connection_request.has_endpoint_info() + ? connection_request.endpoint_info() + : connection_request.endpoint_name()}; // We've successfully connected to the device, and are now about to jump on to // the EncryptionRunner thread to start running our encryption protocol. We'll - // mark ourselves as pending in case we get another call to requestConnection + // mark ourselves as pending in case we get another call to RequestConnection // or OnIncomingConnection, so that we can cancel the connection if needed. auto* owned_channel = pending_connections_ .emplace(connection_request.endpoint_id(), PendingConnectionInfo{ .client = client, - .remote_endpoint_name = endpoint_name, + .remote_endpoint_info = endpoint_info, .nonce = connection_request.nonce(), .is_incoming = true, .start_time = start_time, @@ -1079,8 +1125,9 @@ void BasePcpHandler::PendingConnectionInfo::LocalEndpointRejectedConnection( mediums::PeerId BasePcpHandler::CreatePeerIdFromAdvertisement( const std::string& service_id, const std::string& endpoint_id, - const std::string& endpoint_name) { - std::string seed = absl::StrCat(service_id, endpoint_id, endpoint_name); + const ByteArray& endpoint_info) { + std::string seed = + absl::StrCat(service_id, endpoint_id, std::string(endpoint_info)); return mediums::PeerId::FromSeed(ByteArray(std::move(seed))); } diff --git a/cpp/core_v2/internal/base_pcp_handler.h b/cpp/core_v2/internal/base_pcp_handler.h index 533ec388..d9ee3f92 100644 --- a/cpp/core_v2/internal/base_pcp_handler.h +++ b/cpp/core_v2/internal/base_pcp_handler.h @@ -10,6 +10,7 @@ #include "core_v2/internal/encryption_runner.h" #include "core_v2/internal/endpoint_channel_manager.h" #include "core_v2/internal/endpoint_manager.h" +#include "core_v2/internal/mediums/mediums.h" #include "core_v2/internal/mediums/webrtc.h" #include "core_v2/internal/pcp.h" #include "core_v2/internal/pcp_handler.h" @@ -17,6 +18,7 @@ #include "core_v2/options.h" #include "core_v2/status.h" #include "proto/connections/offline_wire_formats.pb.h" +#include "platform_v2/base/byte_array.h" #include "platform_v2/base/prng.h" #include "platform_v2/public/atomic_boolean.h" #include "platform_v2/public/atomic_reference.h" @@ -29,6 +31,7 @@ #include "proto/connections_enums.pb.h" #include "securegcm/d2d_connection_context_v1.h" #include "securegcm/ukey2_handshake.h" +#include "absl/container/btree_map.h" #include "absl/container/flat_hash_map.h" #include "absl/time/time.h" @@ -77,7 +80,7 @@ class BasePcpHandler : public PcpHandler, using FrameProcessor = EndpointManager::FrameProcessor; // TODO(apolyudov): Add SecureRandom. - BasePcpHandler(EndpointManager* endpoint_manager, + BasePcpHandler(Mediums* mediums, EndpointManager* endpoint_manager, EndpointChannelManager* channel_manager, Pcp pcp); ~BasePcpHandler() override; BasePcpHandler(BasePcpHandler&&) = delete; @@ -87,44 +90,45 @@ class BasePcpHandler : public PcpHandler, // Notifies ConnectionListener (info.listener) in case of any event. // See // https://source.corp.google.com/piper///depot/google3/core_v2/listeners.h;l=78 - Status StartAdvertising(ClientProxy* client_proxy, + Status StartAdvertising(ClientProxy* client, const std::string& service_id, const ConnectionOptions& options, const ConnectionRequestInfo& info) override; // Stops Advertising is active, and changes CLientProxy state, // otherwise does nothing. - void StopAdvertising(ClientProxy* client_proxy) override; + void StopAdvertising(ClientProxy* client) override; // Starts discovery of endpoints that may be advertising. // Updates ClientProxy state once discovery started. // DiscoveryListener will get called in case of any event. - Status StartDiscovery(ClientProxy* client_proxy, + Status StartDiscovery(ClientProxy* client, const std::string& service_id, const ConnectionOptions& options, const DiscoveryListener& listener) override; // Stops Discovery if it is active, and changes CLientProxy state, // otherwise does nothing. - void StopDiscovery(ClientProxy* client_proxy) override; + void StopDiscovery(ClientProxy* client) override; // Requests a newly discovered remote endpoint it to form a connection. // Updates state on ClientProxy. - Status RequestConnection(ClientProxy* client_proxy, + Status RequestConnection(ClientProxy* client, const std::string& endpoint_id, - const ConnectionRequestInfo& info) override; + const ConnectionRequestInfo& info, + const ConnectionOptions& options) override; // Called by either party to accept connection on their part. // Until both parties call it, connection will not reach a data phase. // Updates state in ClientProxy. - Status AcceptConnection(ClientProxy* client_proxy, + Status AcceptConnection(ClientProxy* client, const std::string& endpoint_id, const PayloadListener& payload_listener) override; // Called by either party to reject connection on their part. // If either party does call it, connection will terminate. // Updates state in ClientProxy. - Status RejectConnection(ClientProxy* client_proxy, + Status RejectConnection(ClientProxy* client, const std::string& endpoint_id) override; // @EndpointManagerReaderThread @@ -135,7 +139,7 @@ class BasePcpHandler : public PcpHandler, // Called when an endpoint disconnects while we're waiting for both sides to // approve/reject the connection. // @EndpointManagerThread - void OnEndpointDisconnect(ClientProxy* client_proxy, + void OnEndpointDisconnect(ClientProxy* client, const std::string& endpoint_id, CountDownLatch* barrier) override; @@ -167,21 +171,37 @@ class BasePcpHandler : public PcpHandler, // instance (but it can if implementation desires to do so). // BasePcpHandler will hold on to the shared_ptr. struct DiscoveredEndpoint { - DiscoveredEndpoint(std::string endpoint_id, std::string endpoint_name, + DiscoveredEndpoint(std::string endpoint_id, ByteArray endpoint_info, std::string service_id, proto::connections::Medium medium) : endpoint_id(std::move(endpoint_id)), - endpoint_name(std::move(endpoint_name)), + endpoint_info(std::move(endpoint_info)), service_id(std::move(service_id)), medium(medium) {} virtual ~DiscoveredEndpoint() = default; std::string endpoint_id; - std::string endpoint_name; + ByteArray endpoint_info; std::string service_id; proto::connections::Medium medium; }; + struct BluetoothEndpoint : public DiscoveredEndpoint { + BluetoothEndpoint(DiscoveredEndpoint endpoint, BluetoothDevice device) + : DiscoveredEndpoint(std::move(endpoint)), + bluetooth_device(std::move(device)) {} + + BluetoothDevice bluetooth_device; + }; + + struct WifiLanEndpoint : public DiscoveredEndpoint { + WifiLanEndpoint(DiscoveredEndpoint endpoint, WifiLanService service) + : DiscoveredEndpoint(std::move(endpoint)), + wifi_lan_service(std::move(service)) {} + + WifiLanService wifi_lan_service; + }; + struct WebRtcEndpoint : public DiscoveredEndpoint { WebRtcEndpoint(DiscoveredEndpoint endpoint, mediums::PeerId peer_id) : DiscoveredEndpoint(std::move(endpoint)), @@ -200,54 +220,64 @@ class BasePcpHandler : public PcpHandler, void RunOnPcpHandlerThread(Runnable runnable); ConnectionOptions GetConnectionOptions() const; + ConnectionOptions GetDiscoveryOptions() const; // @PcpHandlerThread - void OnEndpointFound(ClientProxy* client_proxy, + void OnEndpointFound(ClientProxy* client, std::shared_ptr endpoint); // @PcpHandlerThread - void OnEndpointLost(ClientProxy* client_proxy, + void OnEndpointLost(ClientProxy* client, const DiscoveredEndpoint& endpoint); Exception OnIncomingConnection( - ClientProxy* client_proxy, const std::string& remote_device_name, + ClientProxy* client, const ByteArray& remote_endpoint_info, std::unique_ptr endpoint_channel, proto::connections::Medium medium); // throws Exception::IO - virtual bool HasOutgoingConnections(ClientProxy* client_proxy) const; - virtual bool HasIncomingConnections(ClientProxy* client_proxy) const; + virtual bool HasOutgoingConnections(ClientProxy* client) const; + virtual bool HasIncomingConnections(ClientProxy* client) const; - virtual bool CanSendOutgoingConnection(ClientProxy* client_proxy) const; - virtual bool CanReceiveIncomingConnection(ClientProxy* client_proxy) const; + virtual bool CanSendOutgoingConnection(ClientProxy* client) const; + virtual bool CanReceiveIncomingConnection(ClientProxy* client) const; // @PcpHandlerThread virtual StartOperationResult StartAdvertisingImpl( - ClientProxy* client_proxy, const std::string& service_id, + ClientProxy* client, const std::string& service_id, const std::string& local_endpoint_id, - const std::string& local_endpoint_name, + const ByteArray& local_endpoint_info, const ConnectionOptions& options) = 0; // @PcpHandlerThread - virtual Status StopAdvertisingImpl(ClientProxy* client_proxy) = 0; + virtual Status StopAdvertisingImpl(ClientProxy* client) = 0; // @PcpHandlerThread virtual StartOperationResult StartDiscoveryImpl( - ClientProxy* client_proxy, const std::string& service_id, + ClientProxy* client, const std::string& service_id, const ConnectionOptions& options) = 0; // @PcpHandlerThread - virtual Status StopDiscoveryImpl(ClientProxy* client_proxy) = 0; + virtual Status StopDiscoveryImpl(ClientProxy* client) = 0; // @PcpHandlerThread - virtual ConnectImplResult ConnectImpl(ClientProxy* client_proxy, + virtual ConnectImplResult ConnectImpl(ClientProxy* client, DiscoveredEndpoint* endpoint) = 0; virtual std::vector GetConnectionMediumsByPriority() = 0; virtual proto::connections::Medium GetDefaultUpgradeMedium() = 0; + // Returns the first discovered endpoint for the given endpoint_id. + DiscoveredEndpoint* GetDiscoveredEndpoint(const std::string& endpoint_id); + + // Returns a vector of discovered endpoints, sorted in order of decreasing + // preference. + std::vector + GetDiscoveredEndpoints(const std::string& endpoint_id); + mediums::PeerId CreatePeerIdFromAdvertisement(const string& service_id, const string& endpoint_id, - const string& endpoint_name); + const ByteArray& endpoint_info); + Mediums* mediums_; EndpointManager* endpoint_manager_; EndpointChannelManager* channel_manager_; @@ -272,13 +302,14 @@ class BasePcpHandler : public PcpHandler, // Client state tracker to report events to. Never changes. Always valid. ClientProxy* client = nullptr; - // Peer endpoint name, or empty, if not discovered yet. May change. - std::string remote_endpoint_name; + // Peer endpoint info, or empty, if not discovered yet. May change. + ByteArray remote_endpoint_info; std::int32_t nonce = 0; bool is_incoming = false; absl::Time start_time{absl::InfinitePast()}; // Client callbacks. Always valid. ConnectionListener listener; + ConnectionOptions options; // Only set for outgoing connections. If set, we must call // result->Set() when connection is established, or rejected. @@ -322,7 +353,7 @@ class BasePcpHandler : public PcpHandler, static Exception WriteConnectionRequestFrame( EndpointChannel* endpoint_channel, const std::string& local_endpoint_id, - const std::string& local_endpoint_name, std::int32_t nonce, + const ByteArray& local_endpoint_info, std::int32_t nonce, const std::vector& supported_mediums); static constexpr absl::Duration kConnectionRequestReadTimeout = @@ -330,8 +361,7 @@ class BasePcpHandler : public PcpHandler, static constexpr absl::Duration kRejectedConnectionCloseDelay = absl::Seconds(2); - void OnConnectionResponse(ClientProxy* client_proxy, - const std::string& endpoint_id, + void OnConnectionResponse(ClientProxy* client, const std::string& endpoint_id, const OfflineFrame& frame); // Returns true if the new endpoint is preferred over the old endpoint. @@ -353,8 +383,7 @@ class BasePcpHandler : public PcpHandler, // We're not sure how far our outgoing connection has gotten. We may (or may // not) have called ClientProxy::OnConnectionInitiated. Therefore, we'll // call both preInit and preResult failures. - void ProcessTieBreakLoss(ClientProxy* client_proxy, - const std::string& endpoint_id, + void ProcessTieBreakLoss(ClientProxy* client, const std::string& endpoint_id, PendingConnectionInfo* info); // Called when an incoming connection has been accepted by both sides. @@ -366,7 +395,7 @@ class BasePcpHandler : public PcpHandler, // for outgoing connections and older devices that don't report their // supported mediums. void InitiateBandwidthUpgrade( - ClientProxy* client_proxy, const std::string& endpoint_id, + ClientProxy* client, const std::string& endpoint_id, const std::vector& supported_mediums); // Returns the optimal medium supported by both devices. @@ -377,9 +406,8 @@ class BasePcpHandler : public PcpHandler, EndpointChannel* channel, Status status, Future* result); - void ProcessPreConnectionResultFailure(ClientProxy* client_proxy, + void ProcessPreConnectionResultFailure(ClientProxy* client, const std::string& endpoint_id); - DiscoveredEndpoint* GetDiscoveredEndpoint(const std::string& endpoint_id); // Called when either side accepts/rejects the connection, but only takes // effect after both have accepted or one side has rejected. @@ -390,7 +418,7 @@ class BasePcpHandler : public PcpHandler, // onResult(DISCONNECTED) instead of onResult(REJECTED)), we delay our // close. If the other side behaves properly, we shouldn't even see the // delay (because they will also close the connection). - void EvaluateConnectionResult(ClientProxy* client_proxy, + void EvaluateConnectionResult(ClientProxy* client, const std::string& endpoint_id, bool can_close_immediately); @@ -413,7 +441,7 @@ class BasePcpHandler : public PcpHandler, // removed from this map. absl::flat_hash_map pending_connections_; // A map of endpoint id -> DiscoveredEndpoint. - absl::flat_hash_map> + absl::btree_multimap> discovered_endpoints_; // A map of endpoint id -> alarm. These alarms delay closing the // EndpointChannel to give the other side enough time to read the rejection diff --git a/cpp/core_v2/internal/base_pcp_handler_test.cc b/cpp/core_v2/internal/base_pcp_handler_test.cc index e18ff69c..1a580067 100644 --- a/cpp/core_v2/internal/base_pcp_handler_test.cc +++ b/cpp/core_v2/internal/base_pcp_handler_test.cc @@ -8,11 +8,13 @@ #include "core_v2/internal/encryption_runner.h" #include "core_v2/internal/offline_frames.h" #include "core_v2/listeners.h" +#include "core_v2/options.h" #include "core_v2/params.h" #include "proto/connections/offline_wire_formats.pb.h" #include "platform_v2/base/byte_array.h" #include "platform_v2/public/count_down_latch.h" #include "platform_v2/public/pipe.h" +#include "proto/connections_enums.pb.h" #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/time/time.h" @@ -30,6 +32,20 @@ using ::testing::MockFunction; using ::testing::Return; using ::testing::StrictMock; +constexpr BooleanMediumSelector kTestCases[] = { + BooleanMediumSelector{}, + BooleanMediumSelector{ + .bluetooth = true, + }, + BooleanMediumSelector{ + .wifi_lan = true, + }, + BooleanMediumSelector{ + .bluetooth = true, + .wifi_lan = true, + }, +}; + class MockEndpointChannel : public BaseEndpointChannel { public: explicit MockEndpointChannel(Pipe* reader, Pipe* writer) @@ -58,8 +74,10 @@ class MockEndpointChannel : public BaseEndpointChannel { class MockPcpHandler : public BasePcpHandler { public: - MockPcpHandler(EndpointManager* em, EndpointChannelManager* ecm) - : BasePcpHandler(em, ecm, Pcp::kP2pCluster) {} + using DiscoveredEndpoint = BasePcpHandler::DiscoveredEndpoint; + + MockPcpHandler(Mediums* m, EndpointManager* em, EndpointChannelManager* ecm) + : BasePcpHandler(m, em, ecm, Pcp::kP2pCluster) {} // Expose protected inner types of a base type for mocking. using BasePcpHandler::ConnectImplResult; @@ -80,9 +98,9 @@ class MockPcpHandler : public BasePcpHandler { (const, override)); MOCK_METHOD(StartOperationResult, StartAdvertisingImpl, - (ClientProxy * client, const string& service_id, - const string& local_endpoint_id, - const string& local_endpoint_name, + (ClientProxy * client, const std::string& service_id, + const std::string& local_endpoint_id, + const ByteArray& local_endpoint_info, const ConnectionOptions& options), (override)); MOCK_METHOD(Status, StopAdvertisingImpl, (ClientProxy * client), (override)); @@ -98,8 +116,7 @@ class MockPcpHandler : public BasePcpHandler { std::vector GetConnectionMediumsByPriority() override { - return {proto::connections::Medium::BLE, - proto::connections::Medium::WEB_RTC}; + return GetDiscoveryMediums(); } // Mock adapters for protected non-virtual methods of a base class. @@ -110,22 +127,37 @@ class MockPcpHandler : public BasePcpHandler { void OnEndpointLost(ClientProxy* client, const DiscoveredEndpoint& endpoint) { BasePcpHandler::OnEndpointLost(client, endpoint); } + std::vector GetDiscoveredEndpoints( + const std::string& endpoint_id) { + return BasePcpHandler::GetDiscoveredEndpoints(endpoint_id); + } + + std::vector GetDiscoveryMediums() { + auto allowed = + BasePcpHandler::GetDiscoveryOptions().CompatibleOptions().allowed; + return GetMediumsFromSelector(allowed); + } + + std::vector GetMediumsFromSelector( + BooleanMediumSelector allowed) { + return allowed.GetMediums(true); + } }; class MockContext { public: - explicit MockContext(std::atomic_bool* destroyed = nullptr) { + explicit MockContext(std::atomic_int* destroyed = nullptr) { destroyed_ = destroyed; } MockContext(MockContext&&) = default; MockContext& operator=(MockContext&&) = default; ~MockContext() { - if (destroyed_) *destroyed_ = true; + if (destroyed_) (*destroyed_)++; } private: - Swapper destroyed_{nullptr}; + Swapper destroyed_{nullptr}; }; struct MockDiscoveredEndpoint : public MockPcpHandler::DiscoveredEndpoint { @@ -135,7 +167,8 @@ struct MockDiscoveredEndpoint : public MockPcpHandler::DiscoveredEndpoint { MockContext context; }; -class BasePcpHandlerTest : public ::testing::Test { +class BasePcpHandlerTest + : public ::testing::TestWithParam { protected: struct MockConnectionListener { StrictMock> endpoint_found_cb; StrictMock> @@ -163,39 +196,43 @@ class BasePcpHandlerTest : public ::testing::Test { endpoint_distance_changed_cb; }; - void StartAdvertising(ClientProxy* client, MockPcpHandler* pcp_handler) { + void StartAdvertising(ClientProxy* client, MockPcpHandler* pcp_handler, + BooleanMediumSelector allowed = GetParam()) { std::string service_id{"service"}; ConnectionOptions options{ .strategy = Strategy::kP2pCluster, + .allowed = allowed, .auto_upgrade_bandwidth = true, .enforce_topology_constraints = true, }; ConnectionRequestInfo info{ - .name = "remote_endpoint_name", + .endpoint_info = ByteArray{"remote_endpoint_name"}, .listener = connection_listener_, }; - EXPECT_CALL(*pcp_handler, - StartAdvertisingImpl(client, service_id, _, info.name, _)) + EXPECT_CALL(*pcp_handler, StartAdvertisingImpl(client, service_id, _, + info.endpoint_info, _)) .WillOnce(Return(MockPcpHandler::StartOperationResult{ .status = {Status::kSuccess}, - .mediums = {Medium::BLE}, + .mediums = pcp_handler->GetMediumsFromSelector(allowed), })); EXPECT_EQ(pcp_handler->StartAdvertising(client, service_id, options, info), Status{Status::kSuccess}); EXPECT_TRUE(client->IsAdvertising()); } - void StartDiscovery(ClientProxy* client, MockPcpHandler* pcp_handler) { + void StartDiscovery(ClientProxy* client, MockPcpHandler* pcp_handler, + BooleanMediumSelector allowed = GetParam()) { std::string service_id{"service"}; ConnectionOptions options{ .strategy = Strategy::kP2pCluster, + .allowed = allowed, .auto_upgrade_bandwidth = true, .enforce_topology_constraints = true, }; EXPECT_CALL(*pcp_handler, StartDiscoveryImpl(client, service_id, _)) .WillOnce(Return(MockPcpHandler::StartOperationResult{ .status = {Status::kSuccess}, - .mediums = {Medium::BLE}, + .mediums = pcp_handler->GetMediumsFromSelector(allowed), })); EXPECT_EQ(pcp_handler->StartDiscovery(client, service_id, options, discovery_listener_), @@ -205,7 +242,8 @@ class BasePcpHandlerTest : public ::testing::Test { std::pair, std::unique_ptr> - SetupConnection(Pipe& pipe_a, Pipe& pipe_b) { // NOLINT + SetupConnection(Pipe& pipe_a, Pipe& pipe_b, + proto::connections::Medium medium) { // NOLINT auto channel_a = std::make_unique(&pipe_b, &pipe_a); auto channel_b = std::make_unique(&pipe_a, &pipe_b); // On initiator (A) side, we drop the first write, since this is a @@ -221,7 +259,7 @@ class BasePcpHandlerTest : public ::testing::Test { Invoke([channel = channel_a.get()](const ByteArray& data) { return channel->DoWrite(data); })); - EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(Medium::BLE)); + EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(medium)); EXPECT_CALL(*channel_a, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); EXPECT_CALL(*channel_a, IsPaused).WillRepeatedly(Return(false)); @@ -233,7 +271,7 @@ class BasePcpHandlerTest : public ::testing::Test { Invoke([channel = channel_b.get()](const ByteArray& data) { return channel->DoWrite(data); })); - EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(Medium::BLE)); + EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(medium)); EXPECT_CALL(*channel_b, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); EXPECT_CALL(*channel_b, IsPaused).WillRepeatedly(Return(false)); @@ -244,39 +282,50 @@ class BasePcpHandlerTest : public ::testing::Test { std::unique_ptr channel_a, MockEndpointChannel* channel_b, ClientProxy* client, MockPcpHandler* pcp_handler, - std::atomic_bool* flag = nullptr) { + proto::connections::Medium connect_medium, + std::atomic_int* flag = nullptr) { ConnectionRequestInfo info{ - .name = "ABCD", + .endpoint_info = ByteArray{"ABCD"}, .listener = connection_listener_, }; + ConnectionOptions options{ + .remote_bluetooth_mac_address = + ByteArray{std::string("\x12\x34\x56\x78\x9a\xbc")}, + }; EXPECT_CALL(mock_discovery_listener_.endpoint_found_cb, Call); EXPECT_CALL(*pcp_handler, CanSendOutgoingConnection) .WillRepeatedly(Return(true)); EXPECT_CALL(*pcp_handler, GetStrategy) .WillRepeatedly(Return(Strategy::kP2pCluster)); EXPECT_CALL(mock_connection_listener_.initiated_cb, Call).Times(1); - EXPECT_CALL(*pcp_handler, ConnectImpl) - .WillOnce( - Invoke([&channel_a](ClientProxy* client, - MockPcpHandler::DiscoveredEndpoint* endpoint) { - return MockPcpHandler::ConnectImplResult{ - .medium = Medium::BLE, - .status = {Status::kSuccess}, - .endpoint_channel = std::move(channel_a), - }; - })); // Simulate successful discovery. auto encryption_runner = std::make_unique(); - pcp_handler->OnEndpointFound( - client, std::make_shared(MockDiscoveredEndpoint{ - { - endpoint_id, - info.name, - "service", - Medium::BLE, - }, - MockContext{flag}, - })); + auto allowed_mediums = pcp_handler->GetDiscoveryMediums(); + + EXPECT_CALL(*pcp_handler, ConnectImpl) + .WillOnce(Invoke([&channel_a, connect_medium]( + ClientProxy* client, + MockPcpHandler::DiscoveredEndpoint* endpoint) { + return MockPcpHandler::ConnectImplResult{ + .medium = connect_medium, + .status = {Status::kSuccess}, + .endpoint_channel = std::move(channel_a), + }; + })); + + for (const auto& discovered_medium : allowed_mediums) { + pcp_handler->OnEndpointFound( + client, + std::make_shared(MockDiscoveredEndpoint{ + { + endpoint_id, + info.endpoint_info, + "service", + discovered_medium, + }, + MockContext{flag}, + })); + } auto other_client = std::make_unique(); // Run peer crypto in advance, if channel_b is provided. @@ -285,8 +334,9 @@ class BasePcpHandlerTest : public ::testing::Test { encryption_runner->StartServer(other_client.get(), endpoint_id, channel_b, {}); } - EXPECT_EQ(pcp_handler->RequestConnection(client, endpoint_id, info), - Status{Status::kSuccess}); + EXPECT_EQ( + pcp_handler->RequestConnection(client, endpoint_id, info, options), + Status{Status::kSuccess}); NEARBY_LOG(INFO, "Stopping Encryption Runner"); } @@ -313,26 +363,29 @@ class BasePcpHandlerTest : public ::testing::Test { }; }; -TEST_F(BasePcpHandlerTest, ConstructorDestructorWorks) { +TEST_P(BasePcpHandlerTest, ConstructorDestructorWorks) { + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); SUCCEED(); } -TEST_F(BasePcpHandlerTest, StartAdvertisingChangesState) { +TEST_P(BasePcpHandlerTest, StartAdvertisingChangesState) { ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartAdvertising(&client, &pcp_handler); } -TEST_F(BasePcpHandlerTest, StopAdvertisingChangesState) { +TEST_P(BasePcpHandlerTest, StopAdvertisingChangesState) { ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartAdvertising(&client, &pcp_handler); EXPECT_CALL(pcp_handler, StopAdvertisingImpl(&client)).Times(1); EXPECT_TRUE(client.IsAdvertising()); @@ -340,19 +393,21 @@ TEST_F(BasePcpHandlerTest, StopAdvertisingChangesState) { EXPECT_FALSE(client.IsAdvertising()); } -TEST_F(BasePcpHandlerTest, StartDiscoveryChangesState) { +TEST_P(BasePcpHandlerTest, StartDiscoveryChangesState) { ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); } -TEST_F(BasePcpHandlerTest, StopDiscoveryChangesState) { +TEST_P(BasePcpHandlerTest, StopDiscoveryChangesState) { ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); EXPECT_CALL(pcp_handler, StopDiscoveryImpl(&client)).Times(1); EXPECT_TRUE(client.IsDiscovering()); @@ -360,40 +415,46 @@ TEST_F(BasePcpHandlerTest, StopDiscoveryChangesState) { EXPECT_FALSE(client.IsDiscovering()); } -TEST_F(BasePcpHandlerTest, RequestConnectionChangesState) { +TEST_P(BasePcpHandlerTest, RequestConnectionChangesState) { std::string endpoint_id{"1234"}; ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto mediums = pcp_handler.GetDiscoveryMediums(); + auto connect_medium = mediums[mediums.size() - 1]; + auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), &client, - &pcp_handler); + &pcp_handler, connect_medium); NEARBY_LOG(INFO, "RequestConnection complete"); channel_b->Close(); pcp_handler.DisconnectFromEndpointManager(); } -TEST_F(BasePcpHandlerTest, AcceptConnectionChangesState) { +TEST_P(BasePcpHandlerTest, AcceptConnectionChangesState) { std::string endpoint_id{"1234"}; ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto mediums = pcp_handler.GetDiscoveryMediums(); + auto connect_medium = mediums[mediums.size() - 1]; + auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), &client, - &pcp_handler); + &pcp_handler, connect_medium); NEARBY_LOG(INFO, "Attempting to accept connection: id=%s", endpoint_id.c_str()); EXPECT_EQ(pcp_handler.AcceptConnection(&client, endpoint_id, {}), @@ -404,18 +465,21 @@ TEST_F(BasePcpHandlerTest, AcceptConnectionChangesState) { pcp_handler.DisconnectFromEndpointManager(); } -TEST_F(BasePcpHandlerTest, RejectConnectionChangesState) { +TEST_P(BasePcpHandlerTest, RejectConnectionChangesState) { std::string endpoint_id{"1234"}; ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto mediums = pcp_handler.GetDiscoveryMediums(); + auto connect_medium = mediums[mediums.size() - 1]; + auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); auto& channel_b = channel_pair.second; EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(1); RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b.get(), - &client, &pcp_handler); + &client, &pcp_handler, connect_medium); NEARBY_LOGS(INFO) << "Attempting to reject connection: id=" << endpoint_id; EXPECT_EQ(pcp_handler.RejectConnection(&client, endpoint_id), Status{Status::kSuccess}); @@ -424,20 +488,23 @@ TEST_F(BasePcpHandlerTest, RejectConnectionChangesState) { pcp_handler.DisconnectFromEndpointManager(); } -TEST_F(BasePcpHandlerTest, OnIncomingFrameChangesState) { +TEST_P(BasePcpHandlerTest, OnIncomingFrameChangesState) { std::string endpoint_id{"1234"}; ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto mediums = pcp_handler.GetDiscoveryMediums(); + auto connect_medium = mediums[mediums.size() - 1]; + auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), &client, - &pcp_handler); + &pcp_handler, connect_medium); NEARBY_LOGS(INFO) << "Attempting to accept connection: id=" << endpoint_id; EXPECT_CALL(mock_connection_listener_.accepted_cb, Call).Times(1); EXPECT_CALL(mock_connection_listener_.disconnected_cb, Call) @@ -448,28 +515,33 @@ TEST_F(BasePcpHandlerTest, OnIncomingFrameChangesState) { auto frame = parser::FromBytes(parser::ForConnectionResponse(Status::kSuccess)); pcp_handler.OnIncomingFrame(frame.result(), endpoint_id, &client, - Medium::BLE); + connect_medium); NEARBY_LOGS(INFO) << "Closing connection: id=" << endpoint_id; channel_b->Close(); pcp_handler.DisconnectFromEndpointManager(); } -TEST_F(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) { - std::atomic_bool destroyed_flag = false; +TEST_P(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) { + std::atomic_int destroyed_flag = 0; + int mediums_count = 0; { std::string endpoint_id{"1234"}; ClientProxy client; + Mediums m; EndpointChannelManager ecm; EndpointManager em(&ecm); - MockPcpHandler pcp_handler(&em, &ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); StartDiscovery(&client, &pcp_handler); - auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto mediums = pcp_handler.GetDiscoveryMediums(); + auto connect_medium = mediums[mediums.size() - 1]; + auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); auto& channel_a = channel_pair.first; auto& channel_b = channel_pair.second; EXPECT_CALL(*channel_a, CloseImpl).Times(1); EXPECT_CALL(*channel_b, CloseImpl).Times(1); RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), - &client, &pcp_handler, &destroyed_flag); + &client, &pcp_handler, connect_medium, &destroyed_flag); + mediums_count = mediums.size(); NEARBY_LOG(INFO, "Attempting to accept connection: id=%s", endpoint_id.c_str()); EXPECT_EQ(pcp_handler.AcceptConnection(&client, endpoint_id, {}), @@ -479,9 +551,57 @@ TEST_F(BasePcpHandlerTest, DestructorIsCalledOnProtocolEndpoint) { channel_b->Close(); pcp_handler.DisconnectFromEndpointManager(); } - EXPECT_TRUE(destroyed_flag.load()); + EXPECT_EQ(destroyed_flag.load(), mediums_count); } +TEST_P(BasePcpHandlerTest, MultipleMediumsProduceSingleEndpointLostEvent) { + BooleanMediumSelector allowed = GetParam(); + if (allowed.Count(true) < 2) { + // Ignore single-medium test cases, and implicit "all mediums" case. + SUCCEED(); + return; + } + std::atomic_int destroyed_flag = 0; + int mediums_count = 0; + { + std::string endpoint_id{"1234"}; + ClientProxy client; + Mediums m; + EndpointChannelManager ecm; + EndpointManager em(&ecm); + MockPcpHandler pcp_handler(&m, &em, &ecm); + StartDiscovery(&client, &pcp_handler); + auto mediums = pcp_handler.GetDiscoveryMediums(); + auto connect_medium = mediums[mediums.size() - 1]; + auto channel_pair = SetupConnection(pipe_a_, pipe_b_, connect_medium); + auto& channel_a = channel_pair.first; + auto& channel_b = channel_pair.second; + EXPECT_CALL(*channel_a, CloseImpl).Times(1); + EXPECT_CALL(*channel_b, CloseImpl).Times(1); + EXPECT_CALL(mock_discovery_listener_.endpoint_lost_cb, Call).Times(1); + RequestConnection(endpoint_id, std::move(channel_a), channel_b.get(), + &client, &pcp_handler, connect_medium, &destroyed_flag); + auto allowed_mediums = pcp_handler.GetDiscoveryMediums(); + mediums_count = allowed_mediums.size(); + NEARBY_LOG(INFO, "Attempting to accept connection: id=%s", + endpoint_id.c_str()); + EXPECT_EQ(pcp_handler.AcceptConnection(&client, endpoint_id, {}), + Status{Status::kSuccess}); + EXPECT_CALL(mock_connection_listener_.rejected_cb, Call).Times(AtLeast(0)); + for (const auto* endpoint : + pcp_handler.GetDiscoveredEndpoints(endpoint_id)) { + pcp_handler.OnEndpointLost(&client, *endpoint); + } + NEARBY_LOG(INFO, "Closing connection: id=%s", endpoint_id.c_str()); + channel_b->Close(); + pcp_handler.DisconnectFromEndpointManager(); + } + EXPECT_EQ(destroyed_flag.load(), mediums_count); +} + +INSTANTIATE_TEST_SUITE_P(ParameterizedBasePcpHandlerTest, BasePcpHandlerTest, + ::testing::ValuesIn(kTestCases)); + } // namespace } // namespace connections } // namespace nearby diff --git a/cpp/core_v2/internal/ble_advertisement.cc b/cpp/core_v2/internal/ble_advertisement.cc index 0443a03f..1ad5df12 100644 --- a/cpp/core_v2/internal/ble_advertisement.cc +++ b/cpp/core_v2/internal/ble_advertisement.cc @@ -13,12 +13,33 @@ namespace connections { BleAdvertisement::BleAdvertisement(Version version, Pcp pcp, const ByteArray& service_id_hash, const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& bluetooth_mac_address) { - if (version != Version::kV1 || - service_id_hash.size() != kServiceIdHashLength || endpoint_id.empty() || + DoInitialize(/*fast_advertisement=*/false, version, pcp, service_id_hash, + endpoint_id, endpoint_info, bluetooth_mac_address); +} + +BleAdvertisement::BleAdvertisement(Version version, Pcp pcp, + const std::string& endpoint_id, + const ByteArray& endpoint_info) { + DoInitialize(/*fast_advertisement=*/true, version, pcp, {}, endpoint_id, + endpoint_info, {}); +} + +void BleAdvertisement::DoInitialize(bool fast_advertisement, Version version, + Pcp pcp, const ByteArray& service_id_hash, + const std::string& endpoint_id, + const ByteArray& endpoint_info, + const std::string& bluetooth_mac_address) { + fast_advertisement_ = fast_advertisement; + if (!fast_advertisement_) { + if (service_id_hash.size() != kServiceIdHashLength) return; + } + int max_endpoint_info_length = + fast_advertisement_ ? kMaxFastEndpointInfoLength : kMaxEndpointInfoLength; + if (version != Version::kV1 || endpoint_id.empty() || endpoint_id.length() != kEndpointIdLength || - endpoint_name.length() > kMaxEndpointNameLength) { + endpoint_info.size() > max_endpoint_info_length) { return; } @@ -35,20 +56,29 @@ BleAdvertisement::BleAdvertisement(Version version, Pcp pcp, pcp_ = pcp; service_id_hash_ = service_id_hash; endpoint_id_ = endpoint_id; - endpoint_name_ = endpoint_name; - if (!BluetoothMacAddressHexStringToBytes(bluetooth_mac_address).Empty()) { - bluetooth_mac_address_ = bluetooth_mac_address; + endpoint_info_ = endpoint_info; + if (!fast_advertisement_) { + if (!BluetoothUtils::FromString(bluetooth_mac_address).Empty()) { + bluetooth_mac_address_ = bluetooth_mac_address; + } } } -BleAdvertisement::BleAdvertisement(const ByteArray& ble_advertisement_bytes) { +BleAdvertisement::BleAdvertisement(bool fast_advertisement, + const ByteArray& ble_advertisement_bytes) { + fast_advertisement_ = fast_advertisement; + if (ble_advertisement_bytes.Empty()) { NEARBY_LOG(ERROR, "Cannot deserialize BleAdvertisement: null bytes passed in."); return; } - if (ble_advertisement_bytes.size() < kMinAdvertisementLength) { + int min_advertisement_length = fast_advertisement_ + ? kMinFastAdvertisementLength + : kMinAdvertisementLength; + + if (ble_advertisement_bytes.size() < min_advertisement_length) { NEARBY_LOG(ERROR, "Cannot deserialize BleAdvertisement: expecting min %d raw " "bytes, got %" PRIu64, @@ -82,43 +112,44 @@ BleAdvertisement::BleAdvertisement(const ByteArray& ble_advertisement_bytes) { pcp_); } - // The next 3 bytes are supposed to be the service_id_hash. - service_id_hash_ = base_input_stream.ReadBytes(kServiceIdHashLength); + // The next 3 bytes are supposed to be the service_id_hash if not fast + // advertisment. + if (!fast_advertisement_) + service_id_hash_ = base_input_stream.ReadBytes(kServiceIdHashLength); // The next 4 bytes are supposed to be the endpoint_id. endpoint_id_ = std::string{base_input_stream.ReadBytes(kEndpointIdLength)}; - // The next 1 byte are supposed to be the length of the endpoint_name. - std::uint32_t expected_endpoint_name_length = base_input_stream.ReadUint8(); + // The next 1 byte are supposed to be the length of the endpoint_info. + std::uint32_t expected_endpoint_info_length = base_input_stream.ReadUint8(); - // The next x bytes are the endpoint name. (Max length is 131 bytes). - // Check that the stated endpoint_name_length is the same as what we - // received. - auto endpoint_name_bytes = - base_input_stream.ReadBytes(expected_endpoint_name_length); - if (endpoint_name_bytes.Empty() || - endpoint_name_bytes.size() != expected_endpoint_name_length) { + // The next x bytes are the endpoint info. (Max length is 131 bytes or 17 + // bytes as fast_advertisement being true). + endpoint_info_ = base_input_stream.ReadBytes(expected_endpoint_info_length); + const int max_endpoint_info_length = + fast_advertisement_ ? kMaxFastEndpointInfoLength : kMaxEndpointInfoLength; + if (endpoint_info_.Empty() || + endpoint_info_.size() != expected_endpoint_info_length || + endpoint_info_.size() > max_endpoint_info_length) { NEARBY_LOG(INFO, - "Cannot deserialize BleAdvertisement: expected " - "endpointName to be %d bytes, got %" PRIu64, - expected_endpoint_name_length, endpoint_name_bytes.size()); + "Cannot deserialize BleAdvertisement(fast advertisement=%d): " + "expected endpointInfo to be %d bytes, got %" PRIu64, + fast_advertisement_, expected_endpoint_info_length, + endpoint_info_.size()); // Clear enpoint_id for validadity. endpoint_id_.clear(); return; } - endpoint_name_ = std::string{endpoint_name_bytes}; - // The next 6 bytes are the bluetooth mac address. - auto bluetooth_mac_address_bytes = - base_input_stream.ReadBytes(kBluetoothMacAddressLength); - // If the Bluetooth MAC Address bytes are unset or invalid, leave the - // string empty. Otherwise, convert it to the proper colon delimited - // format. - if (!IsBluetoothMacAddressUnset(bluetooth_mac_address_bytes)) { + // The next 6 bytes are the bluetooth mac address if not fast advertisment. + if (!fast_advertisement_) { + auto bluetooth_mac_address_bytes = + base_input_stream.ReadBytes(BluetoothUtils::kBluetoothMacAddressLength); bluetooth_mac_address_ = - HexBytesToColonDelimitedString(bluetooth_mac_address_bytes); + BluetoothUtils::ToString(bluetooth_mac_address_bytes); } + base_input_stream.Close(); } @@ -133,74 +164,35 @@ BleAdvertisement::operator ByteArray() const { // The next 5 bits are the Pcp. version_and_pcp_byte |= static_cast(pcp_) & kPcpBitmask; - // clang-format off - std::string out = absl::StrCat(std::string(1, version_and_pcp_byte), - std::string(service_id_hash_), - endpoint_id_, - std::string(1, endpoint_name_.size()), - endpoint_name_); - // clang-format on + std::string out; + if (fast_advertisement_) { + // clang-format off + out = absl::StrCat(std::string(1, version_and_pcp_byte), + endpoint_id_, + std::string(1, endpoint_info_.size()), + std::string(endpoint_info_)); + // clang-format on + } else { + // clang-format off + out = absl::StrCat(std::string(1, version_and_pcp_byte), + std::string(service_id_hash_), + endpoint_id_, + std::string(1, endpoint_info_.size()), + std::string(endpoint_info_)); + // clang-format on - // The next 6 bytes are the bluetooth mac address. If bluetooth_mac_address is - // invalid or empty, we get back a null byte array. - auto bluetooth_mac_address_bytes( - BluetoothMacAddressHexStringToBytes(bluetooth_mac_address_)); - if (!bluetooth_mac_address_bytes.Empty()) { - absl::StrAppend(&out, std::string(bluetooth_mac_address_bytes)); + // The next 6 bytes are the bluetooth mac address. If bluetooth_mac_address + // is invalid or empty, we get back a null byte array. + auto bluetooth_mac_address_bytes{ + BluetoothUtils::FromString(bluetooth_mac_address_)}; + if (!bluetooth_mac_address_bytes.Empty()) { + absl::StrAppend(&out, std::string(bluetooth_mac_address_bytes)); + } } return ByteArray(std::move(out)); } -ByteArray BleAdvertisement::BluetoothMacAddressHexStringToBytes( - const std::string& bluetooth_mac_address) const { - std::string bt_mac_address(bluetooth_mac_address); - - // Remove the colon delimiters. - bt_mac_address.erase( - std::remove(bt_mac_address.begin(), bt_mac_address.end(), ':'), - bt_mac_address.end()); - - // If the bluetooth mac address is invalid (wrong size), return a null byte - // array. - if (bt_mac_address.length() != kBluetoothMacAddressLength * 2) { - return ByteArray(); - } - - // Convert to bytes. If MAC Address bytes are unset, return a null byte array. - auto bt_mac_address_string(absl::HexStringToBytes(bt_mac_address)); - auto bt_mac_address_bytes = - ByteArray(bt_mac_address_string.data(), bt_mac_address_string.size()); - if (IsBluetoothMacAddressUnset(bt_mac_address_bytes)) { - return ByteArray(); - } - return bt_mac_address_bytes; -} - -std::string BleAdvertisement::HexBytesToColonDelimitedString( - const ByteArray& hex_bytes) const { - // Convert the hex bytes to a string. - std::string colon_delimited_string( - absl::BytesToHexString(std::string(hex_bytes.data(), hex_bytes.size()))); - absl::AsciiStrToUpper(&colon_delimited_string); - - // Insert the colons. - for (int i = colon_delimited_string.length() - 2; i > 0; i -= 2) { - colon_delimited_string.insert(i, ":"); - } - return colon_delimited_string; -} - -bool BleAdvertisement::IsBluetoothMacAddressUnset( - const ByteArray& bluetooth_mac_address_bytes) const { - for (int i = 0; i < bluetooth_mac_address_bytes.size(); i++) { - if (bluetooth_mac_address_bytes.data()[i] != 0) { - return false; - } - } - return true; -} - } // namespace connections } // namespace nearby } // namespace location diff --git a/cpp/core_v2/internal/ble_advertisement.h b/cpp/core_v2/internal/ble_advertisement.h index 5523e17d..3f1d04f3 100644 --- a/cpp/core_v2/internal/ble_advertisement.h +++ b/cpp/core_v2/internal/ble_advertisement.h @@ -2,6 +2,7 @@ #define CORE_V2_INTERNAL_BLE_ADVERTISEMENT_H_ #include "core_v2/internal/pcp.h" +#include "platform_v2/base/bluetooth_utils.h" #include "platform_v2/base/byte_array.h" namespace location { @@ -11,8 +12,11 @@ namespace connections { // Represents the format of the Connections Ble Advertisement used in // Advertising + Discovery. // -//

[VERSION][PCP][SERVICE_ID_HASH][ENDPOINT_ID][ENDPOINT_NAME_SIZE] -// [ENDPOINT_NAME][BLUETOOTH_MAC] +//

[VERSION][PCP][SERVICE_ID_HASH][ENDPOINT_ID][ENDPOINT_INFO_SIZE] +// [ENDPOINT_INFO][BLUETOOTH_MAC] +// +//

The fast version of this advertisement simply omits SERVICE_ID_HASH and +// the Bluetooth MAC address. // //

See go/connections-ble-advertisement for more information. class BleAdvertisement { @@ -25,28 +29,35 @@ class BleAdvertisement { // can never go beyond V7. }; - static constexpr int kServiceIdHashLength = 3; static constexpr int kVersionAndPcpLength = 1; - // Should be defined as EndpointManager::kEndpointIdLength, but that - // involves making BleAdvertisement templatized on Platform just for - // that one little thing, so forget it (at least for now). - static constexpr int kEndpointIdLength = 4; - static constexpr int kEndpointNameSizeLength = 1; - static constexpr int kBluetoothMacAddressLength = 6; - static constexpr int kMinAdvertisementLength = - kVersionAndPcpLength + kServiceIdHashLength + kEndpointIdLength + - kEndpointNameSizeLength + kBluetoothMacAddressLength; - static constexpr int kMaxEndpointNameLength = 131; static constexpr int kVersionBitmask = 0x0E0; static constexpr int kPcpBitmask = 0x01F; - static constexpr int kEndpointNameLengthBitmask = 0x0FF; + static constexpr int kServiceIdHashLength = 3; + static constexpr int kEndpointIdLength = 4; + static constexpr int kEndpointInfoSizeLength = 1; + static constexpr int kEndpointInfoLengthBitmask = 0x0FF; + static constexpr int kMinAdvertisementLength = + kVersionAndPcpLength + kServiceIdHashLength + kEndpointIdLength + + kEndpointInfoSizeLength + BluetoothUtils::kBluetoothMacAddressLength; + + // The difference between normal and fast advertisements is that the fast one + // omits the SERVICE_ID_HASH and Bluetooth MAC address. This is done to save + // space. + static constexpr int kMinFastAdvertisementLength = + kMinAdvertisementLength - kServiceIdHashLength - + BluetoothUtils::kBluetoothMacAddressLength; + static constexpr int kMaxEndpointInfoLength = 131; + static constexpr int kMaxFastEndpointInfoLength = 17; BleAdvertisement() = default; + BleAdvertisement(Version version, Pcp pcp, const std::string& endpoint_id, + const ByteArray& endpoint_info); BleAdvertisement(Version version, Pcp pcp, const ByteArray& service_id_hash, const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& bluetooth_mac_address); - explicit BleAdvertisement(const ByteArray& ble_advertisement_bytes); + BleAdvertisement(bool fast_advertisement, + const ByteArray& ble_advertisement_bytes); BleAdvertisement(const BleAdvertisement&) = default; BleAdvertisement& operator=(const BleAdvertisement&) = default; BleAdvertisement(BleAdvertisement&&) = default; @@ -56,25 +67,27 @@ class BleAdvertisement { explicit operator ByteArray() const; bool IsValid() const { return !endpoint_id_.empty(); } + bool IsFastAdvertisement() const { return fast_advertisement_; } Version GetVersion() const { return version_; } Pcp GetPcp() const { return pcp_; } ByteArray GetServiceIdHash() const { return service_id_hash_; } std::string GetEndpointId() const { return endpoint_id_; } - std::string GetEndpointName() const { return endpoint_name_; } + ByteArray GetEndpointInfo() const { return endpoint_info_; } std::string GetBluetoothMacAddress() const { return bluetooth_mac_address_; } private: - ByteArray BluetoothMacAddressHexStringToBytes( - const std::string& bluetooth_mac_address) const; - std::string HexBytesToColonDelimitedString(const ByteArray& hex_bytes) const; - bool IsBluetoothMacAddressUnset( - const ByteArray& bluetooth_mac_address_bytes) const; + void DoInitialize(bool fast_advertisement, Version version, Pcp pcp, + const ByteArray& service_id_hash, + const std::string& endpoint_id, + const ByteArray& endpoint_info, + const std::string& bluetooth_mac_address); + bool fast_advertisement_ = false; Version version_ = Version::kUndefined; Pcp pcp_ = Pcp::kUnknown; ByteArray service_id_hash_; std::string endpoint_id_; - std::string endpoint_name_; + ByteArray endpoint_info_; std::string bluetooth_mac_address_; }; diff --git a/cpp/core_v2/internal/ble_advertisement_test.cc b/cpp/core_v2/internal/ble_advertisement_test.cc index b0621d68..7ad1d374 100644 --- a/cpp/core_v2/internal/ble_advertisement_test.cc +++ b/cpp/core_v2/internal/ble_advertisement_test.cc @@ -9,81 +9,138 @@ namespace { constexpr BleAdvertisement::Version kVersion = BleAdvertisement::Version::kV1; constexpr Pcp kPcp = Pcp::kP2pCluster; -constexpr absl::string_view kServiceIDHashBytes{"\x0a\x0b\x0c"}; -constexpr absl::string_view kEndPointID{"AB12"}; +constexpr absl::string_view kServiceIdHashBytes{"\x0a\x0b\x0c"}; +constexpr absl::string_view kEndpointId{"AB12"}; constexpr absl::string_view kEndpointName{ "How much wood can a woodchuck chuck if a wood chuck would chuck wood?"}; +constexpr absl::string_view kFastAdvertisementEndpointName{"Fast Advertise"}; constexpr absl::string_view kBluetoothMacAddress{"00:00:E6:88:64:13"}; TEST(BleAdvertisementTest, ConstructionWorks) { - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_FALSE(ble_advertisement.IsFastAdvertisement()); EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash()); - EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId()); - EXPECT_EQ(kEndpointName, ble_advertisement.GetEndpointName()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(endpoint_info, ble_advertisement.GetEndpointInfo()); EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress()); } -TEST(BleAdvertisementTest, ConstructionWorksWithEmptyEndpointName) { - std::string empty_endpoint_name; +TEST(BleAdvertisementTest, ConstructionWorksForFastAdvertisement) { + ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)}; + BleAdvertisement ble_advertisement{kVersion, kPcp, std::string(kEndpointId), + fast_endpoint_info}; - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_TRUE(ble_advertisement.IsFastAdvertisement()); + EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); + EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(fast_endpoint_info, ble_advertisement.GetEndpointInfo()); +} + +TEST(BleAdvertisementTest, ConstructionWorksWithEmptyEndpointInfo) { + ByteArray empty_endpoint_info; + + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, - std::string(kEndPointID), - empty_endpoint_name, + std::string(kEndpointId), + empty_endpoint_info, std::string(kBluetoothMacAddress)}; EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_FALSE(ble_advertisement.IsFastAdvertisement()); EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash()); - EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId()); - EXPECT_EQ(empty_endpoint_name, ble_advertisement.GetEndpointName()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(empty_endpoint_info, ble_advertisement.GetEndpointInfo()); EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress()); } -TEST(BleAdvertisementTest, ConstructionWorksWithEmojiEndpointName) { - std::string emoji_endpoint_name{"\u0001F450 \u0001F450"}; +TEST(BleAdvertisementTest, + ConstructionWorksWithEmptyEndpointInfoForFastAdvertisement) { + ByteArray empty_endpoint_info; - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + BleAdvertisement ble_advertisement{kVersion, kPcp, std::string(kEndpointId), + empty_endpoint_info}; + + EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_TRUE(ble_advertisement.IsFastAdvertisement()); + EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); + EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(empty_endpoint_info, ble_advertisement.GetEndpointInfo()); +} + +TEST(BleAdvertisementTest, ConstructionWorksWithEmojiEndpointInfo) { + ByteArray emoji_endpoint_info{std::string("\u0001F450 \u0001F450")}; + + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, - std::string(kEndPointID), - emoji_endpoint_name, + std::string(kEndpointId), + emoji_endpoint_info, std::string(kBluetoothMacAddress)}; EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_FALSE(ble_advertisement.IsFastAdvertisement()); EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash()); - EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId()); - EXPECT_EQ(emoji_endpoint_name, ble_advertisement.GetEndpointName()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(emoji_endpoint_info, ble_advertisement.GetEndpointInfo()); EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress()); } -TEST(BleAdvertisementTest, ConstructionFailsWithLongEndpointName) { - std::string long_endpoint_name(BleAdvertisement::kMaxEndpointNameLength + 1, +TEST(BleAdvertisementTest, + ConstructionWorksWithEmojiEndpointInfoForFastAdvertisement) { + ByteArray emoji_endpoint_info{std::string("\u0001F450 \u0001F450")}; + + BleAdvertisement ble_advertisement{kVersion, kPcp, std::string(kEndpointId), + emoji_endpoint_info}; + + EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_TRUE(ble_advertisement.IsFastAdvertisement()); + EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); + EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(emoji_endpoint_info, ble_advertisement.GetEndpointInfo()); +} + +TEST(BleAdvertisementTest, ConstructionFailsWithLongEndpointInfo) { + std::string long_endpoint_name(BleAdvertisement::kMaxEndpointInfoLength + 1, 'x'); + ByteArray long_endpoint_info{long_endpoint_name}; - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - long_endpoint_name, - std::string(kBluetoothMacAddress)}; + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + long_endpoint_info, std::string(kBluetoothMacAddress)}; + + EXPECT_FALSE(ble_advertisement.IsValid()); +} + +TEST(BleAdvertisementTest, + ConstructionFailsWithLongEndpointInfoForFastAdvertisement) { + std::string long_endpoint_name( + BleAdvertisement::kMaxFastEndpointInfoLength + 1, 'x'); + ByteArray long_endpoint_info{long_endpoint_name}; + + BleAdvertisement ble_advertisement{kVersion, kPcp, std::string(kEndpointId), + long_endpoint_info}; EXPECT_FALSE(ble_advertisement.IsValid()); } @@ -91,13 +148,23 @@ TEST(BleAdvertisementTest, ConstructionFailsWithLongEndpointName) { TEST(BleAdvertisementTest, ConstructionFailsWithBadVersion) { auto bad_version = static_cast(666); - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{bad_version, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + bad_version, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; + + EXPECT_FALSE(ble_advertisement.IsValid()); +} + +TEST(BleAdvertisementTest, + ConstructionFailsWithBadVersionForFastAdvertisement) { + auto bad_version = static_cast(666); + + ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)}; + BleAdvertisement ble_advertisement{ + bad_version, kPcp, std::string(kEndpointId), fast_endpoint_info}; EXPECT_FALSE(ble_advertisement.IsValid()); } @@ -105,13 +172,22 @@ TEST(BleAdvertisementTest, ConstructionFailsWithBadVersion) { TEST(BleAdvertisementTest, ConstructionFailsWithBadPCP) { auto bad_pcp = static_cast(666); - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - bad_pcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, bad_pcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; + + EXPECT_FALSE(ble_advertisement.IsValid()); +} + +TEST(BleAdvertisementTest, ConstructionFailsWithBadPCPForFastAdvertisement) { + auto bad_pcp = static_cast(666); + + ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, bad_pcp, std::string(kEndpointId), fast_endpoint_info}; EXPECT_FALSE(ble_advertisement.IsValid()); } @@ -119,13 +195,12 @@ TEST(BleAdvertisementTest, ConstructionFailsWithBadPCP) { TEST(BleAdvertisementTest, ConstructionSucceedsWithEmptyBluetoothMacAddress) { std::string empty_bluetooth_mac_address = ""; - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - empty_bluetooth_mac_address}; + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, empty_bluetooth_mac_address}; EXPECT_TRUE(ble_advertisement.IsValid()); } @@ -133,125 +208,180 @@ TEST(BleAdvertisementTest, ConstructionSucceedsWithEmptyBluetoothMacAddress) { TEST(BleAdvertisementTest, ConstructionSucceedsWithInvalidBluetoothMacAddress) { std::string bad_bluetooth_mac_address = "022:00"; - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - bad_bluetooth_mac_address}; + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, bad_bluetooth_mac_address}; EXPECT_TRUE(ble_advertisement.IsValid()); EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash()); - EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId()); - EXPECT_EQ(kEndpointName, ble_advertisement.GetEndpointName()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(endpoint_info, ble_advertisement.GetEndpointInfo()); EXPECT_TRUE(ble_advertisement.GetBluetoothMacAddress().empty()); } TEST(BleAdvertisementTest, ConstructionFromBytesWorks) { // Serialize good data into a good Ble Advertisement. - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement org_ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; - auto ble_advertisement_bytes = ByteArray(org_ble_advertisement); + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement org_ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; + ByteArray ble_advertisement_bytes(org_ble_advertisement); - BleAdvertisement ble_advertisement{ble_advertisement_bytes}; + BleAdvertisement ble_advertisement{false, ble_advertisement_bytes}; EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_FALSE(ble_advertisement.IsFastAdvertisement()); EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); EXPECT_EQ(service_id_hash, ble_advertisement.GetServiceIdHash()); - EXPECT_EQ(kEndPointID, ble_advertisement.GetEndpointId()); - EXPECT_EQ(kEndpointName, ble_advertisement.GetEndpointName()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(endpoint_info, ble_advertisement.GetEndpointInfo()); EXPECT_EQ(kBluetoothMacAddress, ble_advertisement.GetBluetoothMacAddress()); } +TEST(BleAdvertisementTest, ConstructionFromBytesWorksForFastAdvertisement) { + // Serialize good data into a good Ble Advertisement. + ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)}; + BleAdvertisement org_ble_advertisement{ + kVersion, kPcp, std::string(kEndpointId), fast_endpoint_info}; + ByteArray ble_advertisement_bytes(org_ble_advertisement); + + BleAdvertisement ble_advertisement{true, ble_advertisement_bytes}; + + EXPECT_TRUE(ble_advertisement.IsValid()); + EXPECT_TRUE(ble_advertisement.IsFastAdvertisement()); + EXPECT_EQ(kVersion, ble_advertisement.GetVersion()); + EXPECT_EQ(kPcp, ble_advertisement.GetPcp()); + EXPECT_EQ(kEndpointId, ble_advertisement.GetEndpointId()); + EXPECT_EQ(fast_endpoint_info, ble_advertisement.GetEndpointInfo()); +} + // Bytes at the end should be ignored so that they can be used as reserve bytes // in the future. TEST(BleAdvertisementTest, ConstructionFromLongLengthBytesWorks) { // Serialize good data into a good Ble Advertisement. - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; - auto ble_advertisement_bytes = ByteArray(ble_advertisement); + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; + ByteArray ble_advertisement_bytes(ble_advertisement); // Add bytes to the end of the valid Ble advertisement. - auto long_ble_advertisement_bytes = - ByteArray(BleAdvertisement::kMinAdvertisementLength + 1000); + ByteArray long_ble_advertisement_bytes( + BleAdvertisement::kMinAdvertisementLength + 1000); ASSERT_LE(ble_advertisement_bytes.size(), long_ble_advertisement_bytes.size()); - memcpy(long_ble_advertisement_bytes.data(), - ble_advertisement_bytes.data(), + memcpy(long_ble_advertisement_bytes.data(), ble_advertisement_bytes.data(), ble_advertisement_bytes.size()); - BleAdvertisement long_ble_advertisement{long_ble_advertisement_bytes}; + BleAdvertisement long_ble_advertisement{false, long_ble_advertisement_bytes}; EXPECT_TRUE(long_ble_advertisement.IsValid()); EXPECT_EQ(kVersion, long_ble_advertisement.GetVersion()); EXPECT_EQ(kPcp, long_ble_advertisement.GetPcp()); EXPECT_EQ(service_id_hash, long_ble_advertisement.GetServiceIdHash()); - EXPECT_EQ(kEndPointID, long_ble_advertisement.GetEndpointId()); - EXPECT_EQ(kEndpointName, long_ble_advertisement.GetEndpointName()); + EXPECT_EQ(kEndpointId, long_ble_advertisement.GetEndpointId()); + EXPECT_EQ(endpoint_info, long_ble_advertisement.GetEndpointInfo()); EXPECT_EQ(kBluetoothMacAddress, long_ble_advertisement.GetBluetoothMacAddress()); } TEST(BleAdvertisementTest, ConstructionFromNullBytesFails) { - BleAdvertisement ble_advertisement{ByteArray{}}; + BleAdvertisement ble_advertisement{false, ByteArray{}}; + + EXPECT_FALSE(ble_advertisement.IsValid()); +} + +TEST(BleAdvertisementTest, ConstructionFromNullBytesFailsForFastAdvertisement) { + BleAdvertisement ble_advertisement{true, ByteArray{}}; EXPECT_FALSE(ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionFromShortLengthBytesFails) { // Serialize good data into a good Ble Advertisement. - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; - auto ble_advertisement_bytes = ByteArray(ble_advertisement); + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; + ByteArray ble_advertisement_bytes(ble_advertisement); // Shorten the valid Ble Advertisement. ByteArray short_ble_advertisement_bytes{ ble_advertisement_bytes.data(), BleAdvertisement::kMinAdvertisementLength - 1}; - BleAdvertisement short_ble_advertisement{short_ble_advertisement_bytes}; + BleAdvertisement short_ble_advertisement{false, + short_ble_advertisement_bytes}; + + EXPECT_FALSE(short_ble_advertisement.IsValid()); +} +TEST(BleAdvertisementTest, + ConstructionFromShortLengthBytesFailsForFastAdvertisement) { + // Serialize good data into a good Ble Advertisement. + ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)}; + BleAdvertisement ble_advertisement{kVersion, kPcp, std::string(kEndpointId), + fast_endpoint_info}; + ByteArray ble_advertisement_bytes(ble_advertisement); + + // Shorten the valid Ble Advertisement. + ByteArray short_ble_advertisement_bytes{ + ble_advertisement_bytes.data(), + BleAdvertisement::kMinAdvertisementLength - 1}; + + BleAdvertisement short_ble_advertisement{true, short_ble_advertisement_bytes}; EXPECT_FALSE(short_ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, - ConstructionFromByesWithWrongEndpointNameLengthFails) { + ConstructionFromByesWithWrongEndpointInfoLengthFails) { // Serialize good data into a good Ble Advertisement. - ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - BleAdvertisement ble_advertisement{kVersion, - kPcp, - service_id_hash, - std::string(kEndPointID), - std::string(kEndpointName), - std::string(kBluetoothMacAddress)}; - auto ble_advertisement_bytes = ByteArray(ble_advertisement); + ByteArray service_id_hash{std::string(kServiceIdHashBytes)}; + ByteArray endpoint_info{std::string(kEndpointName)}; + BleAdvertisement ble_advertisement{ + kVersion, kPcp, + service_id_hash, std::string(kEndpointId), + endpoint_info, std::string(kBluetoothMacAddress)}; + ByteArray ble_advertisement_bytes(ble_advertisement); // Corrupt the EndpointNameLength bits. - auto corrupt_ble_advertisement_string = std::string(ble_advertisement_bytes); + std::string corrupt_ble_advertisement_string(ble_advertisement_bytes); corrupt_ble_advertisement_string[8] ^= 0x0FF; - auto corrupt_ble_advertisement_bytes = - ByteArray(corrupt_ble_advertisement_string); + ByteArray corrupt_ble_advertisement_bytes(corrupt_ble_advertisement_string); - BleAdvertisement corrupt_ble_advertisement{corrupt_ble_advertisement_bytes}; + BleAdvertisement corrupt_ble_advertisement{false, + corrupt_ble_advertisement_bytes}; + + EXPECT_FALSE(corrupt_ble_advertisement.IsValid()); +} + +TEST(BleAdvertisementTest, + ConstructionFromByesWithWrongEndpointInfoLengthFailsForFastAdvertisement) { + // Serialize good data into a good Ble Advertisement. + ByteArray fast_endpoint_info{std::string(kFastAdvertisementEndpointName)}; + BleAdvertisement ble_advertisement{kVersion, kPcp, std::string(kEndpointId), + fast_endpoint_info}; + ByteArray ble_advertisement_bytes = ByteArray(ble_advertisement); + + // Corrupt the EndpointInfoLength bits. + std::string corrupt_ble_advertisement_string(ble_advertisement_bytes); + corrupt_ble_advertisement_string[5] ^= 0x0FF; + ByteArray corrupt_ble_advertisement_bytes(corrupt_ble_advertisement_string); + + BleAdvertisement corrupt_ble_advertisement{true, + corrupt_ble_advertisement_bytes}; EXPECT_FALSE(corrupt_ble_advertisement.IsValid()); } diff --git a/cpp/core_v2/internal/ble_endpoint_channel.cc b/cpp/core_v2/internal/ble_endpoint_channel.cc new file mode 100644 index 00000000..aba332d0 --- /dev/null +++ b/cpp/core_v2/internal/ble_endpoint_channel.cc @@ -0,0 +1,45 @@ +#include "core_v2/internal/ble_endpoint_channel.h" + +#include + +#include "platform_v2/public/ble.h" +#include "platform_v2/public/logging.h" + +namespace location { +namespace nearby { +namespace connections { + +namespace { + +OutputStream* GetOutputStreamOrNull(BleSocket& socket) { + if (socket.GetRemotePeripheral().IsValid()) return &socket.GetOutputStream(); + return nullptr; +} + +InputStream* GetInputStreamOrNull(BleSocket& socket) { + if (socket.GetRemotePeripheral().IsValid()) return &socket.GetInputStream(); + return nullptr; +} + +} // namespace + +BleEndpointChannel::BleEndpointChannel(const std::string& channel_name, + BleSocket socket) + : BaseEndpointChannel(channel_name, GetInputStreamOrNull(socket), + GetOutputStreamOrNull(socket)), + ble_socket_(std::move(socket)) {} + +proto::connections::Medium BleEndpointChannel::GetMedium() const { + return proto::connections::Medium::BLE; +} + +void BleEndpointChannel::CloseImpl() { + auto status = ble_socket_.Close(); + if (!status.Ok()) { + NEARBY_LOG(INFO, "Failed to close Ble socket: exception=%d", status.value); + } +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/ble_endpoint_channel.h b/cpp/core_v2/internal/ble_endpoint_channel.h new file mode 100644 index 00000000..74d68993 --- /dev/null +++ b/cpp/core_v2/internal/ble_endpoint_channel.h @@ -0,0 +1,29 @@ +#ifndef CORE_V2_INTERNAL_BLE_ENDPOINT_CHANNEL_H_ +#define CORE_V2_INTERNAL_BLE_ENDPOINT_CHANNEL_H_ + +#include "core_v2/internal/base_endpoint_channel.h" +#include "platform_v2/public/ble.h" +#include "proto/connections_enums.pb.h" + +namespace location { +namespace nearby { +namespace connections { + +class BleEndpointChannel final : public BaseEndpointChannel { + public: + // Creates both outgoing and incoming Ble channels. + BleEndpointChannel(const std::string& channel_name, BleSocket socket); + + proto::connections::Medium GetMedium() const override; + + private: + void CloseImpl() override; + + BleSocket ble_socket_; +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_BLE_ENDPOINT_CHANNEL_H_ diff --git a/cpp/core_v2/internal/bluetooth_device_name.cc b/cpp/core_v2/internal/bluetooth_device_name.cc index 8afd1737..48897dc9 100644 --- a/cpp/core_v2/internal/bluetooth_device_name.cc +++ b/cpp/core_v2/internal/bluetooth_device_name.cc @@ -8,6 +8,7 @@ #include "platform_v2/base/base64_utils.h" #include "platform_v2/base/base_input_stream.h" #include "platform_v2/public/logging.h" +#include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" namespace location { @@ -17,7 +18,7 @@ namespace connections { BluetoothDeviceName::BluetoothDeviceName(Version version, Pcp pcp, absl::string_view endpoint_id, const ByteArray& service_id_hash, - absl::string_view endpoint_name) { + const ByteArray& endpoint_info) { if (version != Version::kV1 || endpoint_id.empty() || endpoint_id.length() != kEndpointIdLength || service_id_hash.size() != kServiceIdHashLength) { @@ -36,7 +37,7 @@ BluetoothDeviceName::BluetoothDeviceName(Version version, Pcp pcp, pcp_ = pcp; endpoint_id_ = std::string(endpoint_id); service_id_hash_ = service_id_hash; - endpoint_name_ = std::string(endpoint_name); + endpoint_info_ = endpoint_info; } BluetoothDeviceName::BluetoothDeviceName( @@ -106,24 +107,22 @@ BluetoothDeviceName::BluetoothDeviceName( // untouched. base_input_stream.ReadBytes(kReservedLength); - // The next 1 byte are supposed to be the length of the endpoint_name. - std::uint32_t expected_endpoint_name_length = base_input_stream.ReadUint8(); + // The next 1 byte are supposed to be the length of the endpoint_info. + std::uint32_t expected_endpoint_info_length = base_input_stream.ReadUint8(); - // The rest bytes are supposed to be the endpoint_name - auto endpoint_name_bytes = - base_input_stream.ReadBytes(expected_endpoint_name_length); - if (endpoint_name_bytes.Empty() || - endpoint_name_bytes.size() != expected_endpoint_name_length) { + // The rest bytes are supposed to be the endpoint_info + endpoint_info_ = base_input_stream.ReadBytes(expected_endpoint_info_length); + if (endpoint_info_.Empty() || + endpoint_info_.size() != expected_endpoint_info_length) { NEARBY_LOG(INFO, "Cannot deserialize BluetoothDeviceName: expected " - "endpointName to be %d bytes, got %" PRIu64, - expected_endpoint_name_length, endpoint_name_bytes.size()); + "endpoint info to be %d bytes, got %" PRIu64, + expected_endpoint_info_length, endpoint_info_.size()); // Clear enpoint_id for validadity. endpoint_id_.clear(); return; } - endpoint_name_ = std::string{endpoint_name_bytes}; } BluetoothDeviceName::operator std::string() const { @@ -140,14 +139,14 @@ BluetoothDeviceName::operator std::string() const { ByteArray reserved_bytes{kReservedLength}; - std::string usable_endpoint_name(endpoint_name_); - if (endpoint_name_.size() > kMaxEndpointNameLength) { + ByteArray usable_endpoint_info(endpoint_info_); + if (endpoint_info_.size() > kMaxEndpointInfoLength) { NEARBY_LOG(INFO, "While serializing Advertisement, truncating Endpoint Name %s " "(%lu bytes) down to %d bytes", - endpoint_name_.c_str(), endpoint_name_.size(), - kMaxEndpointNameLength); - usable_endpoint_name.erase(kMaxEndpointNameLength); + absl::BytesToHexString(endpoint_info_.data()).c_str(), + endpoint_info_.size(), kMaxEndpointInfoLength); + usable_endpoint_info.SetData(endpoint_info_.data(), kMaxEndpointInfoLength); } // clang-format off @@ -155,8 +154,8 @@ BluetoothDeviceName::operator std::string() const { endpoint_id_, std::string(service_id_hash_), std::string(reserved_bytes), - std::string(1, usable_endpoint_name.size()), - usable_endpoint_name); + std::string(1, usable_endpoint_info.size()), + std::string(usable_endpoint_info)); // clang-format on return Base64Utils::Encode(ByteArray{std::move(out)}); diff --git a/cpp/core_v2/internal/bluetooth_device_name.h b/cpp/core_v2/internal/bluetooth_device_name.h index b92d433a..c5c3f652 100644 --- a/cpp/core_v2/internal/bluetooth_device_name.h +++ b/cpp/core_v2/internal/bluetooth_device_name.h @@ -30,7 +30,7 @@ class BluetoothDeviceName { BluetoothDeviceName() = default; BluetoothDeviceName(Version version, Pcp pcp, absl::string_view endpoint_id, const ByteArray& service_id_hash, - absl::string_view endpoint_name); + const ByteArray& endpoint_info); explicit BluetoothDeviceName(absl::string_view bluetooth_device_name_string); BluetoothDeviceName(const BluetoothDeviceName&) = default; BluetoothDeviceName& operator=(const BluetoothDeviceName&) = default; @@ -45,15 +45,15 @@ class BluetoothDeviceName { Pcp GetPcp() const { return pcp_; } std::string GetEndpointId() const { return endpoint_id_; } ByteArray GetServiceIdHash() const { return service_id_hash_; } - std::string GetEndpointName() const { return endpoint_name_; } + ByteArray GetEndpointInfo() const { return endpoint_info_; } private: static constexpr int kMaxBluetoothDeviceNameLength = 147; static constexpr int kEndpointIdLength = 4; static constexpr int kReservedLength = 7; - static constexpr int kMaxEndpointNameLength = 131; + static constexpr int kMaxEndpointInfoLength = 131; static constexpr int kMinBluetoothDeviceNameLength = - kMaxBluetoothDeviceNameLength - kMaxEndpointNameLength; + kMaxBluetoothDeviceNameLength - kMaxEndpointInfoLength; static constexpr int kVersionBitmask = 0x0E0; static constexpr int kPcpBitmask = 0x01F; @@ -63,7 +63,7 @@ class BluetoothDeviceName { Pcp pcp_{Pcp::kUnknown}; std::string endpoint_id_; ByteArray service_id_hash_; - std::string endpoint_name_; + ByteArray endpoint_info_; }; } // namespace connections diff --git a/cpp/core_v2/internal/bluetooth_device_name_test.cc b/cpp/core_v2/internal/bluetooth_device_name_test.cc index f92c5468..d957bb63 100644 --- a/cpp/core_v2/internal/bluetooth_device_name_test.cc +++ b/cpp/core_v2/internal/bluetooth_device_name_test.cc @@ -20,38 +20,40 @@ constexpr absl::string_view kEndPointName{"RAWK + ROWL!"}; TEST(BluetoothDeviceNameTest, ConstructionWorks) { ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_TRUE(bluetooth_device_name.IsValid()); EXPECT_EQ(kVersion, bluetooth_device_name.GetVersion()); EXPECT_EQ(kPcp, bluetooth_device_name.GetPcp()); EXPECT_EQ(kEndPointID, bluetooth_device_name.GetEndpointId()); EXPECT_EQ(service_id_hash, bluetooth_device_name.GetServiceIdHash()); - EXPECT_EQ(kEndPointName, bluetooth_device_name.GetEndpointName()); + EXPECT_EQ(endpoint_info, bluetooth_device_name.GetEndpointInfo()); } TEST(BluetoothDeviceNameTest, ConstructionWorksWithEmptyEndpointName) { - std::string empty_endpoint_name; + ByteArray empty_endpoint_info; ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; BluetoothDeviceName bluetooth_device_name{ - kVersion, kPcp, kEndPointID, service_id_hash, empty_endpoint_name}; + kVersion, kPcp, kEndPointID, service_id_hash, empty_endpoint_info}; EXPECT_TRUE(bluetooth_device_name.IsValid()); EXPECT_EQ(kVersion, bluetooth_device_name.GetVersion()); EXPECT_EQ(kPcp, bluetooth_device_name.GetPcp()); EXPECT_EQ(kEndPointID, bluetooth_device_name.GetEndpointId()); EXPECT_EQ(service_id_hash, bluetooth_device_name.GetServiceIdHash()); - EXPECT_EQ(empty_endpoint_name, bluetooth_device_name.GetEndpointName()); + EXPECT_EQ(empty_endpoint_info, bluetooth_device_name.GetEndpointInfo()); } TEST(BluetoothDeviceNameTest, ConstructionFailsWithBadVersion) { auto bad_version = static_cast(666); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{bad_version, kPcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(bluetooth_device_name.IsValid()); } @@ -60,8 +62,9 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithBadPcp) { auto bad_pcp = static_cast(666); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{kVersion, bad_pcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(bluetooth_device_name.IsValid()); } @@ -70,8 +73,9 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortEndpointId) { std::string short_endpoint_id("AB1"); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, short_endpoint_id, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(bluetooth_device_name.IsValid()); } @@ -80,8 +84,9 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithLongEndpointId) { std::string long_endpoint_id("AB12X"); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, long_endpoint_id, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(bluetooth_device_name.IsValid()); } @@ -90,8 +95,9 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortServiceIdHash) { char short_service_id_hash_bytes[] = "\x0a\x0b"; ByteArray short_service_id_hash{short_service_id_hash_bytes}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{ - kVersion, kPcp, kEndPointID, short_service_id_hash, kEndPointName}; + kVersion, kPcp, kEndPointID, short_service_id_hash, endpoint_info}; EXPECT_FALSE(bluetooth_device_name.IsValid()); } @@ -100,8 +106,9 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithLongServiceIdHash) { char long_service_id_hash_bytes[] = "\x0a\x0b\x0c\x0d"; ByteArray long_service_id_hash{long_service_id_hash_bytes}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{ - kVersion, kPcp, kEndPointID, long_service_id_hash, kEndPointName}; + kVersion, kPcp, kEndPointID, long_service_id_hash, endpoint_info}; EXPECT_FALSE(bluetooth_device_name.IsValid()); } @@ -119,8 +126,9 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortStringLength) { TEST(BluetoothDeviceNameTest, ConstructionFailsWithWrongEndpointNameLength) { // Serialize good data into a good Bluetooth Device Name. ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; auto bluetooth_device_name_string = std::string(bluetooth_device_name); // Base64-decode the good Bluetooth Device Name. @@ -145,9 +153,10 @@ TEST(BluetoothDeviceNameTest, ConstructionFailsWithWrongEndpointNameLength) { TEST(BluetoothDeviceNameTest, CanParseGeneratedName) { ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; // Build name1 from scratch. BluetoothDeviceName name1{kVersion, kPcp, kEndPointID, service_id_hash, - kEndPointName}; + endpoint_info}; // Build name2 from string composed from name1. BluetoothDeviceName name2{std::string(name1)}; EXPECT_TRUE(name1.IsValid()); @@ -156,7 +165,7 @@ TEST(BluetoothDeviceNameTest, CanParseGeneratedName) { EXPECT_EQ(name1.GetPcp(), name2.GetPcp()); EXPECT_EQ(name1.GetEndpointId(), name2.GetEndpointId()); EXPECT_EQ(name1.GetServiceIdHash(), name2.GetServiceIdHash()); - EXPECT_EQ(name1.GetEndpointName(), name2.GetEndpointName()); + EXPECT_EQ(name1.GetEndpointInfo(), name2.GetEndpointInfo()); } } // namespace diff --git a/cpp/core_v2/internal/client_proxy.cc b/cpp/core_v2/internal/client_proxy.cc index 3ee2d6c3..bd5a36a8 100644 --- a/cpp/core_v2/internal/client_proxy.cc +++ b/cpp/core_v2/internal/client_proxy.cc @@ -12,6 +12,7 @@ #include "proto/connections_enums.pb.h" #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/strings/escaping.h" #include "absl/strings/str_cat.h" namespace location { @@ -24,21 +25,22 @@ ClientProxy::~ClientProxy() { Reset(); } std::int64_t ClientProxy::GetClientId() const { return client_id_; } -std::string ClientProxy::GenerateLocalEndpointId() { - // 1) Concatenate the Random 64-bit value with "client" string. - // 2) Compute a hash of that concatenation. - // 3) Base64-encode that hash, to make it human-readable. - // 4) Use only the first kEndpointIdLength bytes to make ID. - ByteArray id_hash = Crypto::Sha256( - absl::StrCat("client", prng_.NextInt64())); - - std::string id = Base64Utils::Encode(id_hash).substr(0, kEndpointIdLength); - - NEARBY_LOG( - INFO, "ClientProxy [Local Endpoint Generated]: client=%p; endpoint_id=%s", - this, id.c_str()); - - return id; +std::string ClientProxy::GetLocalEndpointId() { + if (local_endpoint_id_.empty()) { + // 1) Concatenate the Random 64-bit value with "client" string. + // 2) Compute a hash of that concatenation. + // 3) Base64-encode that hash, to make it human-readable. + // 4) Use only the first kEndpointIdLength bytes to make ID. + ByteArray id_hash = + Crypto::Sha256(absl::StrCat("client", prng_.NextInt64())); + std::string id = Base64Utils::Encode(id_hash).substr(0, kEndpointIdLength); + NEARBY_LOG( + INFO, + "ClientProxy [Local Endpoint Generated]: client=%p; endpoint_id=%s", + this, id.c_str()); + local_endpoint_id_ = id; + } + return local_endpoint_id_; } void ClientProxy::Reset() { @@ -55,6 +57,7 @@ void ClientProxy::StartedAdvertising( absl::Span mediums) { MutexLock lock(&mutex_); + if (connections_.empty()) local_endpoint_id_.clear(); advertising_info_ = {service_id, listener}; } @@ -64,6 +67,7 @@ void ClientProxy::StoppedAdvertising() { if (IsAdvertising()) { advertising_info_.Clear(); } + if (connections_.empty()) local_endpoint_id_.clear(); } bool ClientProxy::IsAdvertising() const { @@ -83,6 +87,7 @@ void ClientProxy::StartedDiscovery( absl::Span mediums) { MutexLock lock(&mutex_); + if (connections_.empty()) local_endpoint_id_.clear(); discovery_info_ = DiscoveryInfo{service_id, listener}; } @@ -93,6 +98,7 @@ void ClientProxy::StoppedDiscovery() { discovered_endpoint_ids_.clear(); discovery_info_.Clear(); } + if (connections_.empty()) local_endpoint_id_.clear(); } bool ClientProxy::IsDiscoveringServiceId(const std::string& service_id) const { @@ -115,13 +121,14 @@ std::string ClientProxy::GetDiscoveryServiceId() const { void ClientProxy::OnEndpointFound(const std::string& service_id, const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, proto::connections::Medium medium) { MutexLock lock(&mutex_); NEARBY_LOG(INFO, - "ClientProxy [Endpoint Found]: [enter] id=%s; service=%s; name=%s", - endpoint_id.c_str(), service_id.c_str(), endpoint_name.c_str()); + "ClientProxy [Endpoint Found]: [enter] id=%s; service=%s; info=%s", + endpoint_id.c_str(), service_id.c_str(), + absl::BytesToHexString(endpoint_info.data()).c_str()); if (!IsDiscoveringServiceId(service_id)) { NEARBY_LOG(INFO, "ClientProxy [Endpoint Found]: [no discovery] id=%s", endpoint_id.c_str()); @@ -133,7 +140,7 @@ void ClientProxy::OnEndpointFound(const std::string& service_id, return; } discovered_endpoint_ids_.insert(endpoint_id); - discovery_info_.listener.endpoint_found_cb(endpoint_id, endpoint_name, + discovery_info_.listener.endpoint_found_cb(endpoint_id, endpoint_info, service_id); } @@ -150,6 +157,7 @@ void ClientProxy::OnEndpointLost(const std::string& service_id, void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id, const ConnectionResponseInfo& info, + const ConnectionOptions& options, const ConnectionListener& listener) { MutexLock lock(&mutex_); @@ -160,6 +168,7 @@ void ClientProxy::OnConnectionInitiated(const std::string& endpoint_id, endpoint_id, Connection{ .is_incoming = info.is_incoming_connection, .connection_listener = listener, + .connection_options = options, }); // Instead of using structured binding which is nice, but banned // (can not use c++17 features, until chromium does) we unpack manually. @@ -234,6 +243,7 @@ void ClientProxy::OnDisconnected(const std::string& endpoint_id, bool notify) { item->connection_listener.disconnected_cb({endpoint_id}); } connections_.erase(endpoint_id); + if (connections_.empty()) local_endpoint_id_.clear(); } } @@ -248,6 +258,17 @@ bool ClientProxy::ConnectionStatusMatches(const std::string& endpoint_id, return false; } +BooleanMediumSelector ClientProxy::GetUpgradeMediums( + const std::string& endpoint_id) const { + MutexLock lock(&mutex_); + + const Connection* item = LookupConnection(endpoint_id); + if (item != nullptr) { + return item->connection_options.allowed; + } + return {}; +} + bool ClientProxy::IsConnectedToEndpoint(const std::string& endpoint_id) const { return ConnectionStatusMatches(endpoint_id, Connection::kConnected); } @@ -469,6 +490,7 @@ void ClientProxy::RemoveAllEndpoints() { // endpoint, in the case when this is called from stopAllEndpoints(). For now, // just remove without notifying. connections_.clear(); + local_endpoint_id_.clear(); } bool ClientProxy::ConnectionStatusesContains( diff --git a/cpp/core_v2/internal/client_proxy.h b/cpp/core_v2/internal/client_proxy.h index 67ada3ef..3185f785 100644 --- a/cpp/core_v2/internal/client_proxy.h +++ b/cpp/core_v2/internal/client_proxy.h @@ -6,6 +6,7 @@ #include #include "core_v2/listeners.h" +#include "core_v2/options.h" #include "core_v2/status.h" #include "core_v2/strategy.h" #include "platform_v2/base/byte_array.h" @@ -35,7 +36,7 @@ class ClientProxy final { std::int64_t GetClientId() const; - std::string GenerateLocalEndpointId(); + std::string GetLocalEndpointId(); // Clears all the runtime state of this client. void Reset(); @@ -64,7 +65,7 @@ class ClientProxy final { // Proxies to the client's DiscoveryListener::OnEndpointFound() callback. void OnEndpointFound(const std::string& service_id, const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, proto::connections::Medium medium); // Proxies to the client's DiscoveryListener::OnEndpointLost() callback. void OnEndpointLost(const std::string& service_id, @@ -73,6 +74,7 @@ class ClientProxy final { // Proxies to the client's ConnectionListener::OnInitiated() callback. void OnConnectionInitiated(const std::string& endpoint_id, const ConnectionResponseInfo& info, + const ConnectionOptions& options, const ConnectionListener& listener); // Proxies to the client's ConnectionListener::OnAccepted() callback. @@ -88,6 +90,8 @@ class ClientProxy final { // ConnectionListener.disconnected_cb() callback. void OnDisconnected(const std::string& endpoint_id, bool notify); + // Returns all mediums eligible for upgrade. + BooleanMediumSelector GetUpgradeMediums(const std::string& endpoint_id) const; // Returns true if it's safe to send payloads to this endpoint. bool IsConnectedToEndpoint(const std::string& endpoint_id) const; // Returns all endpoints that can safely be sent payloads. @@ -157,6 +161,7 @@ class ClientProxy final { Status status{kPending}; ConnectionListener connection_listener; PayloadListener payload_listener; + ConnectionOptions connection_options; }; struct AdvertisingInfo { @@ -188,6 +193,7 @@ class ClientProxy final { mutable RecursiveMutex mutex_; std::int64_t client_id_; + std::string local_endpoint_id_; Prng prng_; // If not empty, we are currently advertising and accepting connection diff --git a/cpp/core_v2/internal/client_proxy_test.cc b/cpp/core_v2/internal/client_proxy_test.cc index 88a3e93e..5c091852 100644 --- a/cpp/core_v2/internal/client_proxy_test.cc +++ b/cpp/core_v2/internal/client_proxy_test.cc @@ -3,6 +3,7 @@ #include #include "core_v2/listeners.h" +#include "core_v2/options.h" #include "core_v2/strategy.h" #include "platform_v2/base/byte_array.h" #include "gmock/gmock.h" @@ -22,7 +23,7 @@ class ClientProxyTest : public testing::Test { protected: struct MockDiscoveryListener { StrictMock> endpoint_found_cb; StrictMock> @@ -52,14 +53,14 @@ class ClientProxyTest : public testing::Test { }; struct Endpoint { - std::string name; + ByteArray info; std::string id; }; Endpoint StartAdvertising(ClientProxy* client, ConnectionListener listener) { Endpoint endpoint{ - .name = "advertising endpoint name", - .id = client->GenerateLocalEndpointId(), + .info = ByteArray{"advertising endpoint name"}, + .id = client->GetLocalEndpointId(), }; client->StartedAdvertising(service_id_, strategy_, listener, absl::MakeSpan(mediums_)); @@ -68,8 +69,8 @@ class ClientProxyTest : public testing::Test { Endpoint StartDiscovery(ClientProxy* client, DiscoveryListener listener) { Endpoint endpoint{ - .name = "discovery endpoint name", - .id = client->GenerateLocalEndpointId(), + .info = ByteArray{"discovery endpoint name"}, + .id = client->GetLocalEndpointId(), }; client->StartedDiscovery(service_id_, strategy_, listener, absl::MakeSpan(mediums_)); @@ -78,7 +79,8 @@ class ClientProxyTest : public testing::Test { void OnDiscoveryEndpointFound(ClientProxy* client, const Endpoint& endpoint) { EXPECT_CALL(mock_discovery_.endpoint_found_cb, Call).Times(1); - client->OnEndpointFound(service_id_, endpoint.id, endpoint.name, medium_); + client->OnEndpointFound(service_id_, endpoint.id, endpoint.info, + medium_); } void OnDiscoveryEndpointLost(ClientProxy* client, const Endpoint& endpoint) { @@ -91,8 +93,9 @@ class ClientProxyTest : public testing::Test { EXPECT_CALL(mock_discovery_connection_.initiated_cb, Call).Times(1); const std::string auth_token{"auth_token"}; const ByteArray raw_auth_token{auth_token}; - advertising_connection_info_.remote_endpoint_name = endpoint.name; + advertising_connection_info_.remote_endpoint_info = endpoint.info; client->OnConnectionInitiated(endpoint.id, advertising_connection_info_, + connection_options_, discovery_connection_listener_); EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint.id)); } @@ -208,6 +211,7 @@ class ClientProxyTest : public testing::Test { .payload_progress_cb = mock_discovery_payload_.payload_progress_cb.AsStdFunction(), }; + ConnectionOptions connection_options_; }; TEST_F(ClientProxyTest, ConstructorDestructorWorks) { SUCCEED(); } @@ -217,8 +221,8 @@ TEST_F(ClientProxyTest, ClientIdIsUnique) { } TEST_F(ClientProxyTest, GeneratedEndpointIdIsUnique) { - EXPECT_NE(client1_.GenerateLocalEndpointId(), - client2_.GenerateLocalEndpointId()); + EXPECT_NE(client1_.GetLocalEndpointId(), + client2_.GetLocalEndpointId()); } TEST_F(ClientProxyTest, ResetClearsState) { diff --git a/cpp/core_v2/internal/encryption_runner.cc b/cpp/core_v2/internal/encryption_runner.cc index 226c0695..ddddd887 100644 --- a/cpp/core_v2/internal/encryption_runner.cc +++ b/cpp/core_v2/internal/encryption_runner.cc @@ -52,13 +52,13 @@ bool HandleEncryptionSuccess(const std::string& endpoint_id, return true; } -void CancelableAlarmRunnable(ClientProxy* client_proxy, +void CancelableAlarmRunnable(ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel) { NEARBY_LOG(INFO, "Timing out encryption for client %" PRId64 " to endpoint %s after %" PRId64 " ms", - client_proxy->GetClientId(), endpoint_id.c_str(), + client->GetClientId(), endpoint_id.c_str(), static_cast(absl::ToInt64Milliseconds(kTimeout))); endpoint_channel->Close(); } @@ -76,7 +76,7 @@ class ServerRunnable final { void operator()() const { CancelableAlarm timeout_alarm( - "EncryptionRunner.startServer() timeout", + "EncryptionRunner.StartServer() timeout", [this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); }, kTimeout, alarm_executor_); @@ -109,7 +109,7 @@ class ServerRunnable final { return; } - NEARBY_LOG(INFO, "In startServer(), read UKEY2 Message 1 from endpoint %s", + NEARBY_LOG(INFO, "In StartServer(), read UKEY2 Message 1 from endpoint %s", endpoint_id_.c_str()); // Message 2 (Server Init) @@ -131,7 +131,7 @@ class ServerRunnable final { return; } - NEARBY_LOG(INFO, "In startServer(), wrote UKEY2 Message 2 to endpoint %s", + NEARBY_LOG(INFO, "In StartServer(), wrote UKEY2 Message 2 to endpoint %s", endpoint_id_.c_str()); // Message 3 (Client Finish) @@ -156,7 +156,7 @@ class ServerRunnable final { return; } - NEARBY_LOG(INFO, "In startServer(), read UKEY2 Message 3 from endpoint %s", + NEARBY_LOG(INFO, "In StartServer(), read UKEY2 Message 3 from endpoint %s", endpoint_id_.c_str()); timeout_alarm.Cancel(); @@ -170,7 +170,7 @@ class ServerRunnable final { private: void LogException() const { - NEARBY_LOG(ERROR, "In startServer(), UKEY2 failed with endpoint %s", + NEARBY_LOG(ERROR, "In StartServer(), UKEY2 failed with endpoint %s", endpoint_id_.c_str()); } @@ -185,7 +185,7 @@ class ServerRunnable final { channel_->Write(ByteArray(*parse_result.alert_to_send)); if (!write_exception.Ok()) { NEARBY_LOG(WARNING, - "In startServer(), client %" PRId64 + "In StartServer(), client %" PRId64 " failed to pass the alert error message to endpoint %s", client_->GetClientId(), endpoint_id_.c_str()); } @@ -342,22 +342,22 @@ EncryptionRunner::~EncryptionRunner() { } void EncryptionRunner::StartServer( - ClientProxy* client_proxy, const std::string& endpoint_id, + ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel, EncryptionRunner::ResultListener&& listener) { server_executor_.Execute( - [runnable{ServerRunnable(client_proxy, &alarm_executor_, endpoint_id, + [runnable{ServerRunnable(client, &alarm_executor_, endpoint_id, endpoint_channel, std::move(listener))}]() { runnable(); }); } void EncryptionRunner::StartClient( - ClientProxy* client_proxy, const std::string& endpoint_id, + ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel, EncryptionRunner::ResultListener&& listener) { client_executor_.Execute( - [runnable{ClientRunnable(client_proxy, &alarm_executor_, endpoint_id, + [runnable{ClientRunnable(client, &alarm_executor_, endpoint_id, endpoint_channel, std::move(listener))}]() { runnable(); }); diff --git a/cpp/core_v2/internal/encryption_runner.h b/cpp/core_v2/internal/encryption_runner.h index 399fb0b5..a3cd73ea 100644 --- a/cpp/core_v2/internal/encryption_runner.h +++ b/cpp/core_v2/internal/encryption_runner.h @@ -51,11 +51,11 @@ class EncryptionRunner { }; // @AnyThread - void StartServer(ClientProxy* client_proxy, const std::string& endpoint_id, + void StartServer(ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel, ResultListener&& result_listener); // @AnyThread - void StartClient(ClientProxy* client_proxy, const std::string& endpoint_id, + void StartClient(ClientProxy* client, const std::string& endpoint_id, EndpointChannel* endpoint_channel, ResultListener&& result_listener); diff --git a/cpp/core_v2/internal/endpoint_channel_manager.cc b/cpp/core_v2/internal/endpoint_channel_manager.cc index f214c845..d3d0354c 100644 --- a/cpp/core_v2/internal/endpoint_channel_manager.cc +++ b/cpp/core_v2/internal/endpoint_channel_manager.cc @@ -74,6 +74,11 @@ void EndpointChannelManager::SetActiveEndpointChannel( if (endpoint->IsEncrypted()) channel_state_.EncryptChannel(endpoint); } +int EndpointChannelManager::GetConnectedEndpointsCount() const { + MutexLock lock(&mutex_); + return channel_state_.GetConnectedEndpointsCount(); +} + ///////////////////////////////// ChannelState ///////////////////////////////// // endpoint - channel endpoint to encrypt diff --git a/cpp/core_v2/internal/endpoint_channel_manager.h b/cpp/core_v2/internal/endpoint_channel_manager.h index 14f8e718..b9f82dc3 100644 --- a/cpp/core_v2/internal/endpoint_channel_manager.h +++ b/cpp/core_v2/internal/endpoint_channel_manager.h @@ -80,6 +80,8 @@ class EndpointChannelManager final { bool UnregisterChannelForEndpoint(const std::string& endpoint_id) ABSL_LOCKS_EXCLUDED(mutex_); + int GetConnectedEndpointsCount() const ABSL_LOCKS_EXCLUDED(mutex_); + private: // Tracks channel state for all endpoints. This includes what EndpointChannel // the endpoint is currently using and whether or not the EndpointChannel has @@ -97,9 +99,7 @@ class EndpointChannelManager final { } // True if we have a 'context' for the endpoint. - bool IsEncrypted() const { - return context != nullptr; - } + bool IsEncrypted() const { return context != nullptr; } std::shared_ptr channel; std::shared_ptr context; @@ -134,6 +134,7 @@ class EndpointChannelManager final { proto::connections::DisconnectionReason reason); bool EncryptChannel(EndpointData* endpoint); + int GetConnectedEndpointsCount() const { return endpoints_.size(); } private: // Endpoint ID -> EndpointData. Contains everything we know about the @@ -146,7 +147,7 @@ class EndpointChannelManager final { std::unique_ptr channel) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - Mutex mutex_; + mutable Mutex mutex_; ChannelState channel_state_ ABSL_GUARDED_BY(mutex_); }; diff --git a/cpp/core_v2/internal/endpoint_manager.cc b/cpp/core_v2/internal/endpoint_manager.cc index 615dd49a..4e3e5d7a 100644 --- a/cpp/core_v2/internal/endpoint_manager.cc +++ b/cpp/core_v2/internal/endpoint_manager.cc @@ -227,8 +227,7 @@ EndpointManager::~EndpointManager() { NEARBY_LOG(INFO, "EndpointManager is down"); } -EndpointManager::FrameProcessor::Handle -EndpointManager::RegisterFrameProcessor( +EndpointManager::FrameProcessor::Handle EndpointManager::RegisterFrameProcessor( V1Frame::FrameType frame_type, EndpointManager::FrameProcessor* processor) { const FrameProcessor::Handle handle = processor; CountDownLatch latch(1); @@ -318,6 +317,7 @@ void EndpointManager::EnsureWorkersTerminated(const std::string& endpoint_id) { void EndpointManager::RegisterEndpoint(ClientProxy* client, const std::string& endpoint_id, const ConnectionResponseInfo& info, + const ConnectionOptions& options, std::unique_ptr channel, const ConnectionListener& listener) { CountDownLatch latch(1); @@ -329,7 +329,8 @@ void EndpointManager::RegisterEndpoint(ClientProxy* client, // We ignore the risk of job not scheduled (and an associated risk of memory // leak), because this may only happen during service shutdown. RunOnEndpointManagerThread([this, client, channel = channel.release(), - &endpoint_id, &info, &listener, &latch]() { + &endpoint_id, &info, &options, &listener, + &latch]() { // Pass ownership of channel to EndpointChannelManager NEARBY_LOG(INFO, "Registering endpoint with channel manager: id=%s", endpoint_id.c_str()); @@ -382,7 +383,7 @@ void EndpointManager::RegisterEndpoint(ClientProxy* client, // It's now time to let the client know of this new connection so that // they can accept or reject it. - client->OnConnectionInitiated(endpoint_id, info, listener); + client->OnConnectionInitiated(endpoint_id, info, options, listener); latch.CountDown(); }); latch.Await(); diff --git a/cpp/core_v2/internal/endpoint_manager.h b/cpp/core_v2/internal/endpoint_manager.h index b5ea8194..898a6e86 100644 --- a/cpp/core_v2/internal/endpoint_manager.h +++ b/cpp/core_v2/internal/endpoint_manager.h @@ -81,8 +81,8 @@ class EndpointManager { // FrameProcessor* instances are of dynamic duration and survive all sessions. // returns unique handle to be used for unregistering. // Blocks until registration is complete. - FrameProcessor::Handle RegisterFrameProcessor( - V1Frame::FrameType frame_type, FrameProcessor* processor); + FrameProcessor::Handle RegisterFrameProcessor(V1Frame::FrameType frame_type, + FrameProcessor* processor); void UnregisterFrameProcessor(V1Frame::FrameType frame_type, const void* handle, bool sync = false); @@ -91,6 +91,7 @@ class EndpointManager { // Blocks until registration is complete. void RegisterEndpoint(ClientProxy* client, const std::string& endpoint_id, const ConnectionResponseInfo& info, + const ConnectionOptions& options, std::unique_ptr channel, const ConnectionListener& listener); // Called when a client explicitly asks to disconnect from this endpoint. In @@ -201,8 +202,7 @@ class EndpointManager { EndpointChannelManager* channel_manager_; - absl::flat_hash_map - frame_processors_; + absl::flat_hash_map frame_processors_; // We keep track of all registered channel endpoints here. absl::flat_hash_map endpoints_; diff --git a/cpp/core_v2/internal/endpoint_manager_test.cc b/cpp/core_v2/internal/endpoint_manager_test.cc index fa9b485a..7842454e 100644 --- a/cpp/core_v2/internal/endpoint_manager_test.cc +++ b/cpp/core_v2/internal/endpoint_manager_test.cc @@ -6,6 +6,7 @@ #include "core_v2/internal/client_proxy.h" #include "core_v2/internal/endpoint_channel_manager.h" #include "core_v2/internal/offline_frames.h" +#include "core_v2/options.h" #include "platform_v2/base/byte_array.h" #include "platform_v2/base/exception.h" #include "platform_v2/public/count_down_latch.h" @@ -40,8 +41,7 @@ class MockEndpointChannel : public EndpointChannel { MOCK_METHOD(std::string, GetName, (), (const override)); MOCK_METHOD(Medium, GetMedium, (), (const override)); MOCK_METHOD(void, EnableEncryption, - (std::shared_ptr context), - (override)); + (std::shared_ptr context), (override)); MOCK_METHOD(bool, IsPaused, (), (const override)); MOCK_METHOD(void, Pause, (), (override)); MOCK_METHOD(void, Resume, (), (override)); @@ -89,22 +89,23 @@ class EndpointManagerTest : public ::testing::Test { EXPECT_CALL(*channel, GetLastReadTimestamp()) .WillRepeatedly(Return(start_time_)); EXPECT_CALL(mock_listener_.initiated_cb, Call).Times(1); - em_.RegisterEndpoint(&client_, endpoint_id_, info_, std::move(channel), - listener_); + em_.RegisterEndpoint(&client_, endpoint_id_, info_, options_, + std::move(channel), listener_); if (should_close) { EXPECT_TRUE(done.Await(absl::Milliseconds(1000)).result()); } } ClientProxy client_; + ConnectionOptions options_; std::vector> processors_; EndpointChannelManager ecm_; EndpointManager em_{&ecm_}; std::string endpoint_id_ = "endpoint_id"; ConnectionResponseInfo info_ = { - .remote_endpoint_name = "name", + .remote_endpoint_info = ByteArray{"info"}, .authentication_token = "auth_token", - .raw_authentication_token = ByteArray("auth_token"), + .raw_authentication_token = ByteArray{"auth_token"}, .is_incoming_connection = true, }; struct MockConnectionListener { @@ -158,8 +159,10 @@ TEST_F(EndpointManagerTest, UnregisterEndpointCallsOnDisconnected) { TEST_F(EndpointManagerTest, RegisterFrameProcessorWorks) { auto endpoint_channel = std::make_unique(); auto connect_request = std::make_unique(); - auto read_data = parser::ForConnectionRequest("endpoint_id", "endpoint_name", - 1234, std::vector{Medium::BLE}); + ByteArray endpoint_info{"endpoint_name"}; + auto read_data = + parser::ForConnectionRequest("endpoint_id", endpoint_info, + 1234, std::vector{Medium::BLE}); EXPECT_CALL(*connect_request, OnIncomingFrame); EXPECT_CALL(*connect_request, OnEndpointDisconnect); EXPECT_CALL(*endpoint_channel, Read()) diff --git a/cpp/core_v2/internal/mediums/BUILD b/cpp/core_v2/internal/mediums/BUILD index 1681a94f..02190bcc 100644 --- a/cpp/core_v2/internal/mediums/BUILD +++ b/cpp/core_v2/internal/mediums/BUILD @@ -2,6 +2,7 @@ cc_library( name = "mediums", srcs = [ "advertisement_read_result.cc", + "ble.cc", "ble_advertisement.cc", "ble_advertisement_header.cc", "ble_packet.cc", @@ -15,6 +16,7 @@ cc_library( ], hdrs = [ "advertisement_read_result.h", + "ble.h", "ble_advertisement.h", "ble_advertisement_header.h", "ble_packet.h", @@ -56,6 +58,7 @@ cc_library( srcs = ["utils.cc"], hdrs = ["utils.h"], visibility = [ + "//core_v2/internal:__pkg__", "//core_v2/internal/mediums/webrtc:__pkg__", ], deps = [ @@ -74,6 +77,7 @@ cc_test( "ble_advertisement_test.cc", "ble_packet_test.cc", "ble_peripheral_test.cc", + "ble_test.cc", "bloom_filter_test.cc", "bluetooth_classic_test.cc", "bluetooth_radio_test.cc", @@ -93,6 +97,7 @@ cc_test( "//platform_v2/public:logging", "//platform_v2/public:types", "//testing/base/public:gunit_main", + "//absl/strings", "//absl/time", ], ) diff --git a/cpp/core_v2/internal/mediums/ble.cc b/cpp/core_v2/internal/mediums/ble.cc new file mode 100644 index 00000000..ae1efae9 --- /dev/null +++ b/cpp/core_v2/internal/mediums/ble.cc @@ -0,0 +1,269 @@ +#include "core_v2/internal/mediums/ble.h" + +#include +#include +#include + +#include "platform_v2/public/logging.h" +#include "platform_v2/public/mutex_lock.h" + +namespace location { +namespace nearby { +namespace connections { + +Ble::Ble(BluetoothRadio& radio) : radio_(radio) {} + +bool Ble::IsAvailable() const { + MutexLock lock(&mutex_); + + return IsAvailableLocked(); +} + +bool Ble::IsAvailableLocked() const { return medium_.IsValid(); } + +bool Ble::StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes) { + MutexLock lock(&mutex_); + + if (advertisement_bytes.Empty()) { + NEARBY_LOGS(INFO) + << "Refusing to turn on BLE advertising. Empty advertisement data."; + return false; + } + + if (advertisement_bytes.size() > kMaxAdvertisementLength) { + NEARBY_LOG(INFO, + "Refusing to start BLE advertising because the advertisement " + "was too long. Expected at most %d bytes but received %d.", + kMaxAdvertisementLength, advertisement_bytes.size()); + return false; + } + + if (IsAdvertisingLocked(service_id)) { + NEARBY_LOGS(INFO) + << "Failed to BLE advertise because we're already advertising."; + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) + << "Can't start BLE scanning because Bluetooth was never turned on"; + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) << "Can't turn on BLE advertising. BLE is not available."; + return false; + } + + NEARBY_LOGS(INFO) << "Turning on BLE advertising with advertisement bytes=" + << advertisement_bytes.data() << "(" + << advertisement_bytes.size() << ")" + << ", service id=" << service_id; + if (!medium_.StartAdvertising(service_id, advertisement_bytes)) { + NEARBY_LOGS(INFO) + << "Failed to turn on BLE advertising with advertisement bytes=" + << advertisement_bytes.data() << "(" << advertisement_bytes.size() + << ")"; + return false; + } + + advertising_info_.Add(service_id); + return true; +} + +bool Ble::StopAdvertising(const std::string& service_id) { + MutexLock lock(&mutex_); + + if (!IsAdvertisingLocked(service_id)) { + NEARBY_LOGS(INFO) << "Can't turn off BLE advertising; it is already off"; + return false; + } + + NEARBY_LOGS(INFO) << "Turned off BLE advertising with service id=" + << service_id; + bool ret = medium_.StopAdvertising(service_id); + // Reset our bundle of advertising state to mark that we're no longer + // advertising. + advertising_info_.Remove(service_id); + return ret; +} + +bool Ble::IsAdvertising(const std::string& service_id) { + MutexLock lock(&mutex_); + + return IsAdvertisingLocked(service_id); +} + +bool Ble::IsAdvertisingLocked(const std::string& service_id) { + return advertising_info_.Existed(service_id); +} + +bool Ble::StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback) { + MutexLock lock(&mutex_); + + if (service_id.empty()) { + NEARBY_LOGS(INFO) + << "Refusing to start BLE scanning with empty service id."; + return false; + } + + if (IsScanningLocked(service_id)) { + NEARBY_LOGS(INFO) << "Refusing to start scan of BLE peripherals because " + "another scanning is already in-progress."; + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) + << "Can't start BLE scanning because Bluetooth was never turned on"; + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) + << "Can't scan BLE peripherals because BLE isn't available."; + return false; + } + + if (!medium_.StartScanning(service_id, callback)) { + NEARBY_LOGS(INFO) << "Failed to start scan of BLE services."; + return false; + } + + NEARBY_LOGS(INFO) << "Turned on BLE scanning with service id=" << service_id; + // Mark the fact that we're currently performing a BLE discovering. + scanning_info_.Add(service_id); + return true; +} + +bool Ble::StopScanning(const std::string& service_id) { + MutexLock lock(&mutex_); + + if (!IsScanningLocked(service_id)) { + NEARBY_LOGS(INFO) << "Can't turn off BLE sacanning because we never " + "started scanning."; + return false; + } + + NEARBY_LOG(INFO, "Turned off BLE scanning with service id=%s", + service_id.c_str()); + bool ret = medium_.StopScanning(service_id); + scanning_info_.Clear(); + return ret; +} + +bool Ble::IsScanning(const std::string& service_id) { + MutexLock lock(&mutex_); + + return IsScanningLocked(service_id); +} + +bool Ble::IsScanningLocked(const std::string& service_id) { + return scanning_info_.Existed(service_id); +} + +bool Ble::StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) { + MutexLock lock(&mutex_); + + if (service_id.empty()) { + NEARBY_LOGS(INFO) + << "Refusing to start accepting BLE connections with empty service id."; + return false; + } + + if (IsAcceptingConnectionsLocked(service_id)) { + NEARBY_LOGS(INFO) + << "Refusing to start accepting BLE connections for " + << service_id + << " because another BLE peripheral socket is already in-progress."; + return false; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) << "Can't start accepting BLE connections for " + << service_id + << " because Bluetooth isn't enabled."; + return false; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) << "Can't start accepting BLE connections for " + << service_id << " because BLE isn't available."; + return false; + } + + if (!medium_.StartAcceptingConnections(service_id, callback)) { + NEARBY_LOGS(INFO) << "Failed to accept connections callback for " + << service_id << " ."; + return false; + } + + accepting_connections_info_.Add(service_id); + return true; +} + +bool Ble::StopAcceptingConnections(const std::string& service_id) { + MutexLock lock(&mutex_); + + if (!IsAcceptingConnectionsLocked(service_id)) { + NEARBY_LOGS(INFO) + << "Can't stop accepting BLE connections because it was never started."; + return false; + } + + bool ret = medium_.StopAcceptingConnections(service_id); + // Reset our bundle of accepting connections state to mark that we're no + // longer accepting connections. + accepting_connections_info_.Remove(service_id); + return ret; +} + +bool Ble::IsAcceptingConnections(const std::string& service_id) { + MutexLock lock(&mutex_); + + return IsAcceptingConnectionsLocked(service_id); +} + +bool Ble::IsAcceptingConnectionsLocked(const std::string& service_id) { + return accepting_connections_info_.Existed(service_id); +} + +BleSocket Ble::Connect(BlePeripheral& peripheral, + const std::string& service_id) { + MutexLock lock(&mutex_); + NEARBY_LOGS(INFO) << "BLE::Connect: service=" << &peripheral; + // Socket to return. To allow for NRVO to work, it has to be a single object. + BleSocket socket; + + if (service_id.empty()) { + NEARBY_LOGS(INFO) << "Refusing to create BLE socket with empty service_id."; + return socket; + } + + if (!radio_.IsEnabled()) { + NEARBY_LOGS(INFO) << "Can't create client BLE socket to " + << &peripheral << " because Bluetooth isn't enabled."; + return socket; + } + + if (!IsAvailableLocked()) { + NEARBY_LOGS(INFO) << "Can't create client BLE socket [service_id=" + << service_id << "]; BLE isn't available."; + return socket; + } + + socket = medium_.Connect(peripheral, service_id); + if (!socket.IsValid()) { + NEARBY_LOGS(INFO) << "Failed to Connect via BLE [service=" << service_id + << "]"; + } + + return socket; +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/ble.h b/cpp/core_v2/internal/mediums/ble.h new file mode 100644 index 00000000..7880f837 --- /dev/null +++ b/cpp/core_v2/internal/mediums/ble.h @@ -0,0 +1,162 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_BLE_H_ +#define CORE_V2_INTERNAL_MEDIUMS_BLE_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/ble.h" +#include "platform_v2/public/multi_thread_executor.h" +#include "platform_v2/public/mutex.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" + +namespace location { +namespace nearby { +namespace connections { + +class Ble { + public: + using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback; + using AcceptedConnectionCallback = BleMedium::AcceptedConnectionCallback; + + explicit Ble(BluetoothRadio& bluetooth_radio); + ~Ble() = default; + + // Returns true, if Ble communications are supported by a platform. + bool IsAvailable() const ABSL_LOCKS_EXCLUDED(mutex_); + + // Sets custom advertisement data, and then enables Ble advertising. + // Returns true, if data is successfully set, and false otherwise. + bool StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables Ble advertising. + bool StopAdvertising(const std::string& service_id) + ABSL_LOCKS_EXCLUDED(mutex_); + + bool IsAdvertising(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + + // Enables Ble scanning mode. Will report any discoverable peripherals in + // range through a callback. Returns true, if scanning mode was enabled, + // false otherwise. + bool StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Disables Ble discovery mode. + bool StopScanning(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + + bool IsScanning(const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + + // Starts a worker thread, creates a Ble socket, associates it with a + // service id. + bool StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) + ABSL_LOCKS_EXCLUDED(mutex_); + + // Closes socket corresponding to a service id. + bool StopAcceptingConnections(const std::string& service_id) + ABSL_LOCKS_EXCLUDED(mutex_); + + bool IsAcceptingConnections(const std::string& service_id) + 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 Ble peripheral that was might be started on + // another peripheral with StartAcceptingConnections() using the same + // service_id. Blocks until connection is established, or server-side is + // terminated. Returns socket instance. On success, BleSocket.IsValid() return + // true. + BleSocket Connect(BlePeripheral& peripheral, const std::string& service_id) + ABSL_LOCKS_EXCLUDED(mutex_); + + private: + static constexpr int kMaxAdvertisementLength = 512; + + struct AdvertisingInfo { + bool Empty() const { return service_ids.empty(); } + void Clear() { service_ids.clear(); } + void Add(const std::string& service_id) { service_ids.emplace(service_id); } + void Remove(const std::string& service_id) { + service_ids.erase(service_id); + } + bool Existed(const std::string& service_id) const { + return service_ids.contains(service_id); + } + + absl::flat_hash_set service_ids; + }; + + struct ScanningInfo { + bool Empty() const { return service_ids.empty(); } + void Clear() { service_ids.clear(); } + void Add(const std::string& service_id) { service_ids.emplace(service_id); } + void Remove(const std::string& service_id) { + service_ids.erase(service_id); + } + bool Existed(const std::string& service_id) const { + return service_ids.contains(service_id); + } + + absl::flat_hash_set service_ids; + }; + + struct AcceptingConnectionsInfo { + bool Empty() const { return service_ids.empty(); } + void Clear() { service_ids.clear(); } + void Add(const std::string& service_id) { service_ids.emplace(service_id); } + void Remove(const std::string& service_id) { + service_ids.erase(service_id); + } + bool Existed(const std::string& service_id) const { + return service_ids.contains(service_id); + } + + absl::flat_hash_set service_ids; + }; + + // Same as IsAvailable(), but must be called with mutex_ held. + bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Same as IsAdvertising(), but must be called with mutex_ held. + bool IsAdvertisingLocked(const std::string& service_id) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Same as IsDiscovering(), but must be called with mutex_ held. + bool IsScanningLocked(const std::string& service_id) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // Same as IsAcceptingConnections(), but must be called with mutex_ held. + bool IsAcceptingConnectionsLocked(const std::string& service_id) + ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + mutable Mutex mutex_; + BluetoothRadio& radio_ ABSL_GUARDED_BY(mutex_); + BluetoothAdapter& adapter_ ABSL_GUARDED_BY(mutex_){ + radio_.GetBluetoothAdapter()}; + BleMedium medium_ ABSL_GUARDED_BY(mutex_){adapter_}; + AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_); + ScanningInfo scanning_info_ ABSL_GUARDED_BY(mutex_); + AcceptingConnectionsInfo accepting_connections_info_ ABSL_GUARDED_BY(mutex_); +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_BLE_H_ diff --git a/cpp/core_v2/internal/mediums/ble_test.cc b/cpp/core_v2/internal/mediums/ble_test.cc new file mode 100644 index 00000000..5ce85562 --- /dev/null +++ b/cpp/core_v2/internal/mediums/ble_test.cc @@ -0,0 +1,162 @@ +#include "core_v2/internal/mediums/ble.h" + +#include + +#include "core_v2/internal/mediums/bluetooth_radio.h" +#include "platform_v2/base/medium_environment.h" +#include "platform_v2/public/ble.h" +#include "platform_v2/public/count_down_latch.h" +#include "platform_v2/public/logging.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace location { +namespace nearby { +namespace connections { +namespace { + +constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); +constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; +constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; + +class BleTest : public ::testing::Test { + protected: + using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback; + + BleTest() { env_.Stop(); } + + MediumEnvironment& env_{MediumEnvironment::Instance()}; +}; + +TEST_F(BleTest, CanConstructValidObject) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + Ble ble_a{radio_a}; + Ble ble_b{radio_b}; + + EXPECT_TRUE(ble_a.IsMediumValid()); + EXPECT_TRUE(ble_a.IsAdapterValid()); + EXPECT_TRUE(ble_a.IsAvailable()); + EXPECT_TRUE(ble_b.IsMediumValid()); + EXPECT_TRUE(ble_b.IsAdapterValid()); + EXPECT_TRUE(ble_b.IsAvailable()); + EXPECT_NE(&radio_a.GetBluetoothAdapter(), &radio_b.GetBluetoothAdapter()); + env_.Stop(); +} + +TEST_F(BleTest, CanStartAdvertising) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + Ble ble_a{radio_a}; + Ble ble_b{radio_b}; + radio_a.Enable(); + radio_b.Enable(); + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch found_latch(1); + + ble_b.StartScanning(service_id, + DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BlePeripheral& peripheral, + const std::string& service_id) { + found_latch.CountDown(); + }, + }); + + EXPECT_TRUE(ble_a.StartAdvertising(service_id, advertisement_bytes)); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopAdvertising(service_id)); + EXPECT_TRUE(ble_b.StopScanning(service_id)); + env_.Stop(); +} + +TEST_F(BleTest, CanStartDiscovery) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + Ble ble_a{radio_a}; + Ble ble_b{radio_b}; + radio_a.Enable(); + radio_b.Enable(); + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch accept_latch(1); + CountDownLatch lost_latch(1); + + ble_b.StartAdvertising(service_id, advertisement_bytes); + + EXPECT_TRUE(ble_a.StartScanning( + service_id, DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&accept_latch](BlePeripheral& peripheral, + const std::string& service_id) { + accept_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch](BlePeripheral& peripheral, + const std::string& service_id) { + lost_latch.CountDown(); + }, + })); + EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); + ble_b.StopAdvertising(service_id); + EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning(service_id)); + env_.Stop(); +} + +TEST_F(BleTest, CanStartAcceptingConnectionsAndConnect) { + env_.Start(); + BluetoothRadio radio_a; + BluetoothRadio radio_b; + Ble ble_a{radio_a}; + Ble ble_b{radio_b}; + radio_a.Enable(); + radio_b.Enable(); + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch found_latch(1); + CountDownLatch accept_latch(1); + + ble_a.StartAdvertising(service_id, advertisement_bytes); + ble_a.StartAcceptingConnections( + service_id, + { + .accepted_cb = [&accept_latch]( + BleSocket socket, + const std::string&) { accept_latch.CountDown(); }, + }); + BlePeripheral discovered_peripheral; + ble_b.StartScanning( + service_id, + { + .peripheral_discovered_cb = + [&found_latch, &discovered_peripheral]( + BlePeripheral& peripheral, const std::string& service_id) { + discovered_peripheral = peripheral; + NEARBY_LOG(INFO, "Discovered peripheral=%p [impl=%p]", + &peripheral, &peripheral.GetImpl()); + found_latch.CountDown(); + }, + }); + + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + ASSERT_TRUE(discovered_peripheral.IsValid()); + + BleSocket socket = + ble_b.Connect(discovered_peripheral, service_id); + + EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(socket.IsValid()); + ble_b.StopScanning(service_id); + ble_a.StopAdvertising(service_id); + env_.Stop(); +} + +} // namespace +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/bluetooth_classic.cc b/cpp/core_v2/internal/mediums/bluetooth_classic.cc index 39ebda64..97da7811 100644 --- a/cpp/core_v2/internal/mediums/bluetooth_classic.cc +++ b/cpp/core_v2/internal/mediums/bluetooth_classic.cc @@ -368,6 +368,12 @@ BluetoothSocket BluetoothClassic::Connect(BluetoothDevice& bluetooth_device, return socket; } +BluetoothDevice BluetoothClassic::FindRemoteDevice( + const std::string& mac_address) { + MutexLock lock(&mutex_); + return medium_.FindRemoteDevice(mac_address); +} + std::string BluetoothClassic::GenerateUuidFromString(const std::string& data) { return std::string(Uuid(data)); } diff --git a/cpp/core_v2/internal/mediums/bluetooth_classic.h b/cpp/core_v2/internal/mediums/bluetooth_classic.h index 69308309..f45ab79d 100644 --- a/cpp/core_v2/internal/mediums/bluetooth_classic.h +++ b/cpp/core_v2/internal/mediums/bluetooth_classic.h @@ -100,6 +100,9 @@ class BluetoothClassic { const std::string& service_name) ABSL_LOCKS_EXCLUDED(mutex_); + BluetoothDevice FindRemoteDevice(const std::string& mac_address) + ABSL_LOCKS_EXCLUDED(mutex_); + private: struct ScanInfo { bool valid = false; diff --git a/cpp/core_v2/internal/mediums/mediums.cc b/cpp/core_v2/internal/mediums/mediums.cc index 54b3a24f..2a9c58b2 100644 --- a/cpp/core_v2/internal/mediums/mediums.cc +++ b/cpp/core_v2/internal/mediums/mediums.cc @@ -12,6 +12,8 @@ BluetoothClassic& Mediums::GetBluetoothClassic() { return bluetooth_classic_; } +Ble& Mediums::GetBle() { return ble_; } + WifiLan& Mediums::GetWifiLan() { return wifi_lan_; } diff --git a/cpp/core_v2/internal/mediums/mediums.h b/cpp/core_v2/internal/mediums/mediums.h index 4e6b07ec..367365ea 100644 --- a/cpp/core_v2/internal/mediums/mediums.h +++ b/cpp/core_v2/internal/mediums/mediums.h @@ -1,6 +1,7 @@ #ifndef CORE_V2_INTERNAL_MEDIUMS_MEDIUMS_H_ #define CORE_V2_INTERNAL_MEDIUMS_MEDIUMS_H_ +#include "core_v2/internal/mediums/ble.h" #include "core_v2/internal/mediums/bluetooth_classic.h" #include "core_v2/internal/mediums/bluetooth_radio.h" #include "core_v2/internal/mediums/webrtc.h" @@ -22,6 +23,9 @@ class Mediums { // Returns a handle to the Bluetooth Classic medium. BluetoothClassic& GetBluetoothClassic(); + // Returns a handle to the Ble medium. + Ble& GetBle(); + // Returns a handle to the Wifi-Lan medium. WifiLan& GetWifiLan(); @@ -39,6 +43,7 @@ class Mediums { // corresponding radio. BluetoothRadio bluetooth_radio_; BluetoothClassic bluetooth_classic_{bluetooth_radio_}; + Ble ble_{bluetooth_radio_}; WifiLan wifi_lan_; mediums::WebRtc webrtc_; }; diff --git a/cpp/core_v2/internal/mediums/wifi_lan.cc b/cpp/core_v2/internal/mediums/wifi_lan.cc index 019fc0e6..531b3941 100644 --- a/cpp/core_v2/internal/mediums/wifi_lan.cc +++ b/cpp/core_v2/internal/mediums/wifi_lan.cc @@ -44,8 +44,7 @@ bool WifiLan::StartAdvertising(const std::string& service_id, } NEARBY_LOGS(INFO) << "Turned on WifiLan advertising with service info name=" - << service_info_name - << ", service id=" << service_id; + << service_info_name << ", service id=" << service_id; advertising_info_.Add(service_id); return true; } @@ -208,7 +207,8 @@ bool WifiLan::IsAcceptingConnectionsLocked(const std::string& service_id) { WifiLanSocket WifiLan::Connect(WifiLanService& wifi_lan_service, const std::string& service_id) { MutexLock lock(&mutex_); - NEARBY_LOG(INFO, "WifiLan::Connect: service=%p", &wifi_lan_service); + NEARBY_LOG(INFO, "WifiLan::Connect: service=%p, service_info_name=%s", + &wifi_lan_service, wifi_lan_service.GetName().c_str()); // Socket to return. To allow for NRVO to work, it has to be a single object. WifiLanSocket socket; @@ -228,13 +228,19 @@ WifiLanSocket WifiLan::Connect(WifiLanService& wifi_lan_service, socket = medium_.Connect(wifi_lan_service, service_id); if (!socket.IsValid()) { - NEARBY_LOG(INFO, "Failed to Connect via WifiLan [service=%s]", + NEARBY_LOG(INFO, "Failed to Connect via WifiLan [service_id=%s]", service_id.c_str()); } return socket; } +WifiLanService WifiLan::GetRemoteWifiLanService(const std::string& ip_address, + int port) { + MutexLock lock(&mutex_); + return medium_.FindRemoteService(ip_address, port); +} + } // namespace connections } // namespace nearby } // namespace location diff --git a/cpp/core_v2/internal/mediums/wifi_lan.h b/cpp/core_v2/internal/mediums/wifi_lan.h index 890b22e8..1aeabb2d 100644 --- a/cpp/core_v2/internal/mediums/wifi_lan.h +++ b/cpp/core_v2/internal/mediums/wifi_lan.h @@ -69,6 +69,9 @@ class WifiLan { const std::string& service_id) ABSL_LOCKS_EXCLUDED(mutex_); + WifiLanService GetRemoteWifiLanService(const std::string& ip_address, + int port) ABSL_LOCKS_EXCLUDED(mutex_); + private: struct AdvertisingInfo { bool Empty() const { return service_ids.empty(); } @@ -115,7 +118,7 @@ class WifiLan { // Same as IsAvailable(), but must be called with mutex_ held. bool IsAvailableLocked() const ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); - // Same as IsAdvertising(), but must be called with mutex_ held. + // Same as IsAdvertising(), but must be called with mutex_ held. bool IsAdvertisingLocked(const std::string& service_id) ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); diff --git a/cpp/core_v2/internal/mediums/wifi_lan_test.cc b/cpp/core_v2/internal/mediums/wifi_lan_test.cc index 24e64d02..c0586b94 100644 --- a/cpp/core_v2/internal/mediums/wifi_lan_test.cc +++ b/cpp/core_v2/internal/mediums/wifi_lan_test.cc @@ -8,6 +8,7 @@ #include "platform_v2/public/wifi_lan.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" namespace location { namespace nearby { @@ -33,7 +34,6 @@ TEST_F(WifiLanTest, CanConstructValidObject) { WifiLan wifi_lan_a; WifiLan wifi_lan_b; std::string service_id(kServiceID); - std::string service_name{kServiceInfoName}; EXPECT_TRUE(wifi_lan_a.IsAvailable()); EXPECT_TRUE(wifi_lan_b.IsAvailable()); @@ -45,19 +45,19 @@ TEST_F(WifiLanTest, CanStartAdvertising) { WifiLan wifi_lan_a; WifiLan wifi_lan_b; std::string service_id(kServiceID); - std::string service_name{kServiceInfoName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch found_latch(1); wifi_lan_b.StartDiscovery( service_id, DiscoveredServiceCallback{ .service_discovered_cb = [&found_latch](WifiLanService& service, - const std::string& service_id) { + absl::string_view service_id) { found_latch.CountDown(); }, }); - EXPECT_TRUE(wifi_lan_a.StartAdvertising(service_id, service_name)); + EXPECT_TRUE(wifi_lan_a.StartAdvertising(service_id, service_info_name)); EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); EXPECT_TRUE(wifi_lan_a.StopAdvertising(service_id)); EXPECT_TRUE(wifi_lan_b.StopDiscovery(service_id)); @@ -69,11 +69,11 @@ TEST_F(WifiLanTest, CanStartDiscovery) { WifiLan wifi_lan_a; WifiLan wifi_lan_b; std::string service_id(kServiceID); - std::string service_name{kServiceInfoName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch accept_latch(1); CountDownLatch lost_latch(1); - wifi_lan_b.StartAdvertising(service_id, service_name); + wifi_lan_b.StartAdvertising(service_id, service_info_name); EXPECT_TRUE(wifi_lan_a.StartDiscovery( service_id, { @@ -100,17 +100,17 @@ TEST_F(WifiLanTest, CanStartAcceptingConnectionsAndConnect) { WifiLan wifi_lan_a; WifiLan wifi_lan_b; std::string service_id(kServiceID); - std::string service_name{kServiceInfoName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch found_latch(1); CountDownLatch accept_latch(1); - wifi_lan_a.StartAdvertising(service_id, service_name); + wifi_lan_a.StartAdvertising(service_id, service_info_name); wifi_lan_a.StartAcceptingConnections( service_id, { .accepted_cb = [&accept_latch]( WifiLanSocket socket, - const std::string&) { accept_latch.CountDown(); }, + absl::string_view) { accept_latch.CountDown(); }, }); WifiLanService discovered_service; wifi_lan_b.StartDiscovery( @@ -118,7 +118,7 @@ TEST_F(WifiLanTest, CanStartAcceptingConnectionsAndConnect) { { .service_discovered_cb = [&found_latch, &discovered_service]( - WifiLanService& service, const std::string& service_id) { + WifiLanService& service, absl::string_view service_id) { discovered_service = service; NEARBY_LOG(INFO, "Discovered service=%p [impl=%p]", &service, &service.GetImpl()); @@ -135,6 +135,7 @@ TEST_F(WifiLanTest, CanStartAcceptingConnectionsAndConnect) { EXPECT_TRUE(accept_latch.Await(kWaitDuration).result()); EXPECT_TRUE(socket.IsValid()); wifi_lan_b.StopDiscovery(service_id); + wifi_lan_a.StopAcceptingConnections(service_id); wifi_lan_a.StopAdvertising(service_id); env_.Stop(); } diff --git a/cpp/core_v2/internal/mock_service_controller.h b/cpp/core_v2/internal/mock_service_controller.h index f2668139..d6029bbd 100644 --- a/cpp/core_v2/internal/mock_service_controller.h +++ b/cpp/core_v2/internal/mock_service_controller.h @@ -35,7 +35,8 @@ class MockServiceController : public ServiceController { MOCK_METHOD(Status, RequestConnection, (ClientProxy * client, const std::string& endpoint_id, - const ConnectionRequestInfo& info), + const ConnectionRequestInfo& info, + const ConnectionOptions& options), (override)); MOCK_METHOD(Status, AcceptConnection, diff --git a/cpp/core_v2/internal/offline_frames.cc b/cpp/core_v2/internal/offline_frames.cc index 6ccb6d9e..636334ff 100644 --- a/cpp/core_v2/internal/offline_frames.cc +++ b/cpp/core_v2/internal/offline_frames.cc @@ -4,6 +4,7 @@ #include #include "core/internal/message_lite.h" +#include "proto/connections/offline_wire_formats.pb.h" #include "platform_v2/base/byte_array.h" namespace location { @@ -13,7 +14,6 @@ namespace parser { namespace { using ExceptionOrOfflineFrame = ExceptionOr; -using Medium = proto::connections::Medium; using MessageLite = ::google::protobuf::MessageLite; ByteArray ToBytes(OfflineFrame&& frame) { @@ -44,7 +44,7 @@ V1Frame::FrameType GetFrameType(const OfflineFrame& frame) { } ByteArray ForConnectionRequest(const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, std::int32_t nonce, const std::vector& mediums) { OfflineFrame frame; @@ -54,8 +54,8 @@ ByteArray ForConnectionRequest(const std::string& endpoint_id, v1_frame->set_type(V1Frame::CONNECTION_REQUEST); auto* connection_request = v1_frame->mutable_connection_request(); connection_request->set_endpoint_id(endpoint_id); - connection_request->set_endpoint_name(endpoint_name); - connection_request->set_endpoint_info(endpoint_name); + connection_request->set_endpoint_name(std::string(endpoint_info)); + connection_request->set_endpoint_info(std::string(endpoint_info)); connection_request->set_nonce(nonce); for (const auto& medium : mediums) { connection_request->add_mediums(MediumToConnectionRequestMedium(medium)); @@ -108,7 +108,7 @@ ByteArray ForControlPayloadTransfer( return ToBytes(std::move(frame)); } -ByteArray ForBandwidthUpgradeWifiHotspot(const std::string& ssid, +ByteArray ForBwuWifiHotspotPathAvailable(const std::string& ssid, const std::string& password, std::int32_t port) { OfflineFrame frame; @@ -120,8 +120,7 @@ ByteArray ForBandwidthUpgradeWifiHotspot(const std::string& ssid, sub_frame->set_event_type( BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE); auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info(); - upgrade_path_info->set_medium( - BandwidthUpgradeNegotiationFrame::UpgradePathInfo::WIFI_HOTSPOT); + upgrade_path_info->set_medium(UpgradePathInfo::WIFI_HOTSPOT); auto* wifi_hotspot_credentials = upgrade_path_info->mutable_wifi_hotspot_credentials(); wifi_hotspot_credentials->set_ssid(ssid); @@ -131,7 +130,46 @@ ByteArray ForBandwidthUpgradeWifiHotspot(const std::string& ssid, return ToBytes(std::move(frame)); } -ByteArray ForBandwidthUpgradeLastWrite() { +ByteArray ForBwuWifiLanPathAvailable(const std::string& ip_address, + std::int32_t port) { + OfflineFrame frame; + + frame.set_version(OfflineFrame::V1); + auto* v1_frame = frame.mutable_v1(); + v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION); + auto* sub_frame = v1_frame->mutable_bandwidth_upgrade_negotiation(); + sub_frame->set_event_type( + BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE); + auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info(); + upgrade_path_info->set_medium(UpgradePathInfo::WIFI_LAN); + auto* wifi_lan_socket = upgrade_path_info->mutable_wifi_lan_socket(); + wifi_lan_socket->set_ip_address(ip_address); + wifi_lan_socket->set_wifi_port(port); + + return ToBytes(std::move(frame)); +} + +ByteArray ForBwuBluetoothPathAvailable(const std::string& service_id, + const std::string& mac_address) { + OfflineFrame frame; + + frame.set_version(OfflineFrame::V1); + auto* v1_frame = frame.mutable_v1(); + v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION); + auto* sub_frame = v1_frame->mutable_bandwidth_upgrade_negotiation(); + sub_frame->set_event_type( + BandwidthUpgradeNegotiationFrame::UPGRADE_PATH_AVAILABLE); + auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info(); + upgrade_path_info->set_medium(UpgradePathInfo::BLUETOOTH); + auto* bluetooth_credentials = + upgrade_path_info->mutable_bluetooth_credentials(); + bluetooth_credentials->set_mac_address(mac_address); + bluetooth_credentials->set_service_name(service_id); + + return ToBytes(std::move(frame)); +} + +ByteArray ForBwuLastWrite() { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -144,7 +182,7 @@ ByteArray ForBandwidthUpgradeLastWrite() { return ToBytes(std::move(frame)); } -ByteArray ForBandwidthUpgradeSafeToClose() { +ByteArray ForBwuSafeToClose() { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -157,7 +195,7 @@ ByteArray ForBandwidthUpgradeSafeToClose() { return ToBytes(std::move(frame)); } -ByteArray ForBandwidthUpgradeIntroduction(const std::string& endpoint_id) { +ByteArray ForBwuIntroduction(const std::string& endpoint_id) { OfflineFrame frame; frame.set_version(OfflineFrame::V1); @@ -172,6 +210,21 @@ ByteArray ForBandwidthUpgradeIntroduction(const std::string& endpoint_id) { return ToBytes(std::move(frame)); } +ByteArray ForBwuFailure(const UpgradePathInfo& info) { + OfflineFrame frame; + + frame.set_version(OfflineFrame::V1); + auto* v1_frame = frame.mutable_v1(); + v1_frame->set_type(V1Frame::BANDWIDTH_UPGRADE_NEGOTIATION); + auto* sub_frame = v1_frame->mutable_bandwidth_upgrade_negotiation(); + sub_frame->set_event_type( + BandwidthUpgradeNegotiationFrame::UPGRADE_FAILURE); + auto* upgrade_path_info = sub_frame->mutable_upgrade_path_info(); + *upgrade_path_info = info; + + return ToBytes(std::move(frame)); +} + ByteArray ForKeepAlive() { OfflineFrame frame; @@ -183,8 +236,57 @@ ByteArray ForKeepAlive() { return ToBytes(std::move(frame)); } -ConnectionRequestFrame::Medium MediumToConnectionRequestMedium( - proto::connections::Medium medium) { +UpgradePathInfo::Medium MediumToUpgradePathInfoMedium(Medium medium) { + switch (medium) { + case Medium::MDNS: + return UpgradePathInfo::MDNS; + case Medium::BLUETOOTH: + return UpgradePathInfo::BLUETOOTH; + case Medium::WIFI_HOTSPOT: + return UpgradePathInfo::WIFI_HOTSPOT; + case Medium::BLE: + return UpgradePathInfo::BLE; + case Medium::WIFI_LAN: + return UpgradePathInfo::WIFI_LAN; + case Medium::WIFI_AWARE: + return UpgradePathInfo::WIFI_AWARE; + case Medium::NFC: + return UpgradePathInfo::NFC; + case Medium::WIFI_DIRECT: + return UpgradePathInfo::WIFI_DIRECT; + case Medium::WEB_RTC: + return UpgradePathInfo::WEB_RTC; + default: + return UpgradePathInfo::UNKNOWN_MEDIUM; + } +} + +Medium UpgradePathInfoMediumToMedium(UpgradePathInfo::Medium medium) { + switch (medium) { + case UpgradePathInfo::MDNS: + return Medium::MDNS; + case UpgradePathInfo::BLUETOOTH: + return Medium::BLUETOOTH; + case UpgradePathInfo::WIFI_HOTSPOT: + return Medium::WIFI_HOTSPOT; + case UpgradePathInfo::BLE: + return Medium::BLE; + case UpgradePathInfo::WIFI_LAN: + return Medium::WIFI_LAN; + case UpgradePathInfo::WIFI_AWARE: + return Medium::WIFI_AWARE; + case UpgradePathInfo::NFC: + return Medium::NFC; + case UpgradePathInfo::WIFI_DIRECT: + return Medium::WIFI_DIRECT; + case UpgradePathInfo::WEB_RTC: + return Medium::WEB_RTC; + default: + return Medium::UNKNOWN_MEDIUM; + } +} + +ConnectionRequestFrame::Medium MediumToConnectionRequestMedium(Medium medium) { switch (medium) { case Medium::MDNS: return ConnectionRequestFrame::MDNS; @@ -209,8 +311,7 @@ ConnectionRequestFrame::Medium MediumToConnectionRequestMedium( } } -proto::connections::Medium ConnectionRequestMediumToMedium( - ConnectionRequestFrame::Medium medium) { +Medium ConnectionRequestMediumToMedium(ConnectionRequestFrame::Medium medium) { switch (medium) { case ConnectionRequestFrame::MDNS: return Medium::MDNS; @@ -235,9 +336,9 @@ proto::connections::Medium ConnectionRequestMediumToMedium( } } -std::vector ConnectionRequestMediumsToMediums( +std::vector ConnectionRequestMediumsToMediums( const ConnectionRequestFrame& frame) { - std::vector result; + std::vector result; for (const auto& int_medium : frame.mediums()) { result.push_back(ConnectionRequestMediumToMedium( static_cast(int_medium))); diff --git a/cpp/core_v2/internal/offline_frames.h b/cpp/core_v2/internal/offline_frames.h index 81bf8aca..339c543a 100644 --- a/cpp/core_v2/internal/offline_frames.h +++ b/cpp/core_v2/internal/offline_frames.h @@ -4,6 +4,7 @@ #include #include +#include "core_v2/options.h" #include "proto/connections/offline_wire_formats.pb.h" #include "platform_v2/base/byte_array.h" #include "platform_v2/base/exception.h" @@ -14,6 +15,8 @@ namespace nearby { namespace connections { namespace parser { +using UpgradePathInfo = BandwidthUpgradeNegotiationFrame::UpgradePathInfo; + // Serialize/Deserialize Nearby Connections Protocol messages. // Parses incoming message. @@ -25,12 +28,13 @@ ExceptionOr FromBytes(const ByteArray& offline_frame_bytes); // V1Frame::UNKNOWN_FRAME_TYPE, if frame contents is not recognized. V1Frame::FrameType GetFrameType(const OfflineFrame& offline_frame); -// Build ConnectionRequest message. +// Builds Connection Request / Response messages. ByteArray ForConnectionRequest( - const std::string& endpoint_id, const std::string& endpoint_name, - std::int32_t nonce, const std::vector& mediums); + const std::string& endpoint_id, const ByteArray& endpoint_info, + std::int32_t nonce, const std::vector& mediums); ByteArray ForConnectionResponse(std::int32_t status); +// Builds Payload transfer messages. ByteArray ForDataPayloadTransfer( const PayloadTransferFrame::PayloadHeader& header, const PayloadTransferFrame::PayloadChunk& chunk); @@ -38,19 +42,27 @@ ByteArray ForControlPayloadTransfer( const PayloadTransferFrame::PayloadHeader& header, const PayloadTransferFrame::ControlMessage& control); -ByteArray ForBandwidthUpgradeWifiHotspot( - const std::string& ssid, const std::string& password, std::int32_t port); -ByteArray ForBandwidthUpgradeLastWrite(); -ByteArray ForBandwidthUpgradeSafeToClose(); -ByteArray ForBandwidthUpgradeIntroduction(const std::string& endpoint_id); +// Builds Bandwidth Upgrade [BWU] messages. +ByteArray ForBwuIntroduction(const std::string& endpoint_id); +ByteArray ForBwuWifiHotspotPathAvailable(const std::string& ssid, + const std::string& password, + std::int32_t port); +ByteArray ForBwuWifiLanPathAvailable(const std::string& ip_address, + std::int32_t port); +ByteArray ForBwuBluetoothPathAvailable(const std::string& service_id, + const std::string& mac_address); +ByteArray ForBwuFailure(const UpgradePathInfo& info); +ByteArray ForBwuLastWrite(); +ByteArray ForBwuSafeToClose(); ByteArray ForKeepAlive(); -ConnectionRequestFrame::Medium MediumToConnectionRequestMedium( - proto::connections::Medium medium); -proto::connections::Medium ConnectionRequestMediumToMedium( - ConnectionRequestFrame::Medium medium); -std::vector ConnectionRequestMediumsToMediums( +UpgradePathInfo::Medium MediumToUpgradePathInfoMedium(Medium medium); +Medium UpgradePathInfoMediumToMedium(UpgradePathInfo::Medium medium); + +ConnectionRequestFrame::Medium MediumToConnectionRequestMedium(Medium medium); +Medium ConnectionRequestMediumToMedium(ConnectionRequestFrame::Medium medium); +std::vector ConnectionRequestMediumsToMediums( const ConnectionRequestFrame& connection_request_frame); } // namespace parser diff --git a/cpp/core_v2/internal/offline_frames_test.cc b/cpp/core_v2/internal/offline_frames_test.cc index d5ba067b..42dce1f3 100644 --- a/cpp/core_v2/internal/offline_frames_test.cc +++ b/cpp/core_v2/internal/offline_frames_test.cc @@ -79,7 +79,7 @@ TEST(OfflineFramesTest, CanGenerateConnectionRequest) { > >)pb"; ByteArray bytes = ForConnectionRequest( - std::string(kEndpointId), std::string(kEndpointName), kNonce, + std::string(kEndpointId), ByteArray{std::string(kEndpointName)}, kNonce, std::vector(kMediums.begin(), kMediums.end())); auto response = FromBytes(bytes); ASSERT_TRUE(response.ok()); @@ -157,7 +157,7 @@ TEST(OfflineFramesTest, CanGenerateDataPayloadTransfer) { EXPECT_THAT(message, EqualsProto(kExpected)); } -TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeWifiHotspot) { +TEST(OfflineFramesTest, CanGenerateBwuWifiHotspotPathAvailable) { constexpr char kExpected[] = R"pb( version: V1 @@ -175,14 +175,60 @@ TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeWifiHotspot) { > > >)pb"; - ByteArray bytes = ForBandwidthUpgradeWifiHotspot("ssid", "password", 1234); + ByteArray bytes = ForBwuWifiHotspotPathAvailable("ssid", "password", 1234); auto response = FromBytes(bytes); ASSERT_TRUE(response.ok()); OfflineFrame message = FromBytes(bytes).result(); EXPECT_THAT(message, EqualsProto(kExpected)); } -TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeLastWrite) { +TEST(OfflineFramesTest, CanGenerateBwuWifiLanPathAvailable) { + constexpr char kExpected[] = + R"pb( + version: V1 + v1: < + type: BANDWIDTH_UPGRADE_NEGOTIATION + bandwidth_upgrade_negotiation: < + event_type: UPGRADE_PATH_AVAILABLE + upgrade_path_info: < + medium: WIFI_LAN + wifi_lan_socket: < ip_address: "\x01\x02\x03\x04" wifi_port: 1234 > + > + > + >)pb"; + ByteArray bytes = ForBwuWifiLanPathAvailable("\x01\x02\x03\x04", 1234); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + OfflineFrame message = FromBytes(bytes).result(); + EXPECT_THAT(message, EqualsProto(kExpected)); +} + +TEST(OfflineFramesTest, CanGenerateBwuBluetoothPathAvailable) { + constexpr char kExpected[] = + R"pb( + version: V1 + v1: < + type: BANDWIDTH_UPGRADE_NEGOTIATION + bandwidth_upgrade_negotiation: < + event_type: UPGRADE_PATH_AVAILABLE + upgrade_path_info: < + medium: BLUETOOTH + bluetooth_credentials: < + service_name: "service" + mac_address: "\x11\x22\x33\x44\x55\x66" + > + > + > + >)pb"; + ByteArray bytes = + ForBwuBluetoothPathAvailable("service", "\x11\x22\x33\x44\x55\x66"); + auto response = FromBytes(bytes); + ASSERT_TRUE(response.ok()); + OfflineFrame message = FromBytes(bytes).result(); + EXPECT_THAT(message, EqualsProto(kExpected)); +} + +TEST(OfflineFramesTest, CanGenerateBwuLastWrite) { constexpr char kExpected[] = R"pb( version: V1 @@ -190,14 +236,14 @@ TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeLastWrite) { type: BANDWIDTH_UPGRADE_NEGOTIATION bandwidth_upgrade_negotiation: < event_type: LAST_WRITE_TO_PRIOR_CHANNEL > >)pb"; - ByteArray bytes = ForBandwidthUpgradeLastWrite(); + ByteArray bytes = ForBwuLastWrite(); auto response = FromBytes(bytes); ASSERT_TRUE(response.ok()); OfflineFrame message = FromBytes(bytes).result(); EXPECT_THAT(message, EqualsProto(kExpected)); } -TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeSafeToClose) { +TEST(OfflineFramesTest, CanGenerateBwuSafeToClose) { constexpr char kExpected[] = R"pb( version: V1 @@ -205,14 +251,14 @@ TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeSafeToClose) { type: BANDWIDTH_UPGRADE_NEGOTIATION bandwidth_upgrade_negotiation: < event_type: SAFE_TO_CLOSE_PRIOR_CHANNEL > >)pb"; - ByteArray bytes = ForBandwidthUpgradeSafeToClose(); + ByteArray bytes = ForBwuSafeToClose(); auto response = FromBytes(bytes); ASSERT_TRUE(response.ok()); OfflineFrame message = FromBytes(bytes).result(); EXPECT_THAT(message, EqualsProto(kExpected)); } -TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeIntroduction) { +TEST(OfflineFramesTest, CanGenerateBwuIntroduction) { constexpr char kExpected[] = R"pb( version: V1 @@ -223,7 +269,7 @@ TEST(OfflineFramesTest, CanGenerateBandwidthUpgradeIntroduction) { client_introduction: < endpoint_id: "ABC" > > >)pb"; - ByteArray bytes = ForBandwidthUpgradeIntroduction(std::string(kEndpointId)); + ByteArray bytes = ForBwuIntroduction(std::string(kEndpointId)); auto response = FromBytes(bytes); ASSERT_TRUE(response.ok()); OfflineFrame message = FromBytes(bytes).result(); diff --git a/cpp/core_v2/internal/offline_service_controller.cc b/cpp/core_v2/internal/offline_service_controller.cc index 249c97b8..3c1de259 100644 --- a/cpp/core_v2/internal/offline_service_controller.cc +++ b/cpp/core_v2/internal/offline_service_controller.cc @@ -6,9 +6,7 @@ namespace location { namespace nearby { namespace connections { -OfflineServiceController::~OfflineServiceController() { - Stop(); -} +OfflineServiceController::~OfflineServiceController() { Stop(); } void OfflineServiceController::Stop() { if (stop_.Set(true)) return; @@ -38,8 +36,8 @@ void OfflineServiceController::StopDiscovery(ClientProxy* client) { Status OfflineServiceController::RequestConnection( ClientProxy* client, const std::string& endpoint_id, - const ConnectionRequestInfo& info) { - return pcp_manager_.RequestConnection(client, endpoint_id, info); + const ConnectionRequestInfo& info, const ConnectionOptions& options) { + return pcp_manager_.RequestConnection(client, endpoint_id, info, options); } Status OfflineServiceController::AcceptConnection( diff --git a/cpp/core_v2/internal/offline_service_controller.h b/cpp/core_v2/internal/offline_service_controller.h index bcb6e2c7..97517fa7 100644 --- a/cpp/core_v2/internal/offline_service_controller.h +++ b/cpp/core_v2/internal/offline_service_controller.h @@ -40,7 +40,8 @@ class OfflineServiceController : public ServiceController { Status RequestConnection(ClientProxy* client, const std::string& endpoint_id, - const ConnectionRequestInfo& info) override; + const ConnectionRequestInfo& info, + const ConnectionOptions& options) override; Status AcceptConnection(ClientProxy* client, const std::string& endpoint_id, const PayloadListener& listener) override; @@ -51,8 +52,8 @@ class OfflineServiceController : public ServiceController { const std::string& endpoint_id) override; void SendPayload(ClientProxy* client, - const std::vector& endpoint_ids, - Payload payload) override; + const std::vector& endpoint_ids, + Payload payload) override; Status CancelPayload(ClientProxy* client, Payload::Id payload_id) override; diff --git a/cpp/core_v2/internal/offline_service_controller_test.cc b/cpp/core_v2/internal/offline_service_controller_test.cc index 260dd527..b7286cdf 100644 --- a/cpp/core_v2/internal/offline_service_controller_test.cc +++ b/cpp/core_v2/internal/offline_service_controller_test.cc @@ -25,7 +25,21 @@ constexpr absl::Duration kProgressTimeout = absl::Milliseconds(1000); constexpr absl::Duration kDefaultTimeout = absl::Milliseconds(1000); constexpr absl::Duration kDisconnectTimeout = absl::Milliseconds(15000); -class OfflineServiceControllerTest : public ::testing::Test { +constexpr BooleanMediumSelector kTestCases[] = { + BooleanMediumSelector{ + .bluetooth = true, + }, + BooleanMediumSelector{ + .wifi_lan = true, + }, + BooleanMediumSelector{ + .bluetooth = true, + .wifi_lan = true, + }, +}; + +class OfflineServiceControllerTest + : public ::testing::TestWithParam { protected: OfflineServiceControllerTest() { env_.Stop(); } @@ -35,7 +49,7 @@ class OfflineServiceControllerTest : public ::testing::Test { user_b.StartDiscovery(std::string(kServiceId), &discover_latch_); EXPECT_TRUE(discover_latch_.Await(kDefaultTimeout).result()); EXPECT_EQ(user_b.GetDiscovered().service_id, kServiceId); - EXPECT_EQ(user_b.GetDiscovered().endpoint_name, user_a.GetName()); + EXPECT_EQ(user_b.GetDiscovered().endpoint_info, user_a.GetInfo()); EXPECT_FALSE(user_b.GetDiscovered().endpoint_id.empty()); NEARBY_LOG(INFO, "EP-B: [discovered] %s", user_b.GetDiscovered().endpoint_id.c_str()); @@ -53,29 +67,30 @@ class OfflineServiceControllerTest : public ::testing::Test { } CountDownLatch discover_latch_{1}; + CountDownLatch lost_latch_{1}; CountDownLatch connect_latch_{2}; CountDownLatch accept_latch_{2}; CountDownLatch payload_latch_{1}; MediumEnvironment& env_ = MediumEnvironment::Instance(); }; -TEST_F(OfflineServiceControllerTest, CanCreateOne) { +TEST_P(OfflineServiceControllerTest, CanCreateOne) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); + OfflineSimulationUser user_a(kDeviceA, GetParam()); env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanCreateMany) { +TEST_P(OfflineServiceControllerTest, CanCreateMany) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanStartAdvertising) { +TEST_P(OfflineServiceControllerTest, CanStartAdvertising) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_FALSE(user_a.IsAdvertising()); EXPECT_THAT(user_a.StartAdvertising(std::string(kServiceId), nullptr), Eq(Status{Status::kSuccess})); @@ -83,10 +98,10 @@ TEST_F(OfflineServiceControllerTest, CanStartAdvertising) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanStartDiscoveryBeforeAdvertising) { +TEST_P(OfflineServiceControllerTest, CanStartDiscoveryBeforeAdvertising) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_FALSE(user_b.IsDiscovering()); EXPECT_THAT(user_b.StartDiscovery(std::string(kServiceId), &discover_latch_), Eq(Status{Status::kSuccess})); @@ -99,10 +114,10 @@ TEST_F(OfflineServiceControllerTest, CanStartDiscoveryBeforeAdvertising) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanStartDiscoveryAfterAdvertising) { +TEST_P(OfflineServiceControllerTest, CanStartDiscoveryAfterAdvertising) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_FALSE(user_b.IsDiscovering()); EXPECT_FALSE(user_b.IsAdvertising()); EXPECT_THAT(user_a.StartAdvertising(std::string(kServiceId), nullptr), @@ -117,29 +132,39 @@ TEST_F(OfflineServiceControllerTest, CanStartDiscoveryAfterAdvertising) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanStopAdvertising) { +TEST_P(OfflineServiceControllerTest, CanStopAdvertising) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_FALSE(user_a.IsAdvertising()); EXPECT_THAT(user_a.StartAdvertising(std::string(kServiceId), nullptr), Eq(Status{Status::kSuccess})); EXPECT_TRUE(user_a.IsAdvertising()); user_a.StopAdvertising(); EXPECT_FALSE(user_a.IsAdvertising()); - EXPECT_THAT(user_b.StartDiscovery(std::string(kServiceId), &discover_latch_), + EXPECT_THAT(user_b.StartDiscovery(std::string(kServiceId), &discover_latch_, + &lost_latch_), Eq(Status{Status::kSuccess})); EXPECT_TRUE(user_b.IsDiscovering()); - EXPECT_FALSE(discover_latch_.Await(kDefaultTimeout).result()); + auto discover_none = discover_latch_.Await(kDefaultTimeout).GetResult(); + if (!discover_none) { + EXPECT_TRUE(true); + } else { + // There are rare cases (1/1000) that advertisment data has been captured by + // discovery device before advertising is stopped. So we need to check if + // lost_cb has grabbed the event in the end to prove the advertising service + // is stopped. + EXPECT_TRUE(lost_latch_.Await(kDefaultTimeout).result()); + } user_a.Stop(); user_b.Stop(); env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanStopDiscovery) { +TEST_P(OfflineServiceControllerTest, CanStopDiscovery) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_FALSE(user_b.IsDiscovering()); EXPECT_THAT(user_b.StartDiscovery(std::string(kServiceId), &discover_latch_), Eq(Status{Status::kSuccess})); @@ -154,10 +179,10 @@ TEST_F(OfflineServiceControllerTest, CanStopDiscovery) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanConnect) { +TEST_P(OfflineServiceControllerTest, CanConnect) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_THAT(user_a.StartAdvertising(std::string(kServiceId), &connect_latch_), Eq(Status{Status::kSuccess})); EXPECT_THAT(user_b.StartDiscovery(std::string(kServiceId), &discover_latch_), @@ -171,10 +196,10 @@ TEST_F(OfflineServiceControllerTest, CanConnect) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanAcceptConnection) { +TEST_P(OfflineServiceControllerTest, CanAcceptConnection) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); EXPECT_THAT(user_a.StartAdvertising(std::string(kServiceId), &connect_latch_), Eq(Status{Status::kSuccess})); EXPECT_THAT(user_b.StartDiscovery(std::string(kServiceId), &discover_latch_), @@ -195,10 +220,10 @@ TEST_F(OfflineServiceControllerTest, CanAcceptConnection) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanRejectConnection) { +TEST_P(OfflineServiceControllerTest, CanRejectConnection) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); CountDownLatch reject_latch(1); EXPECT_THAT(user_a.StartAdvertising(std::string(kServiceId), &connect_latch_), Eq(Status{Status::kSuccess})); @@ -216,10 +241,10 @@ TEST_F(OfflineServiceControllerTest, CanRejectConnection) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanSendBytePayload) { +TEST_P(OfflineServiceControllerTest, CanSendBytePayload) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); user_a.SendPayload(Payload(message)); @@ -231,10 +256,10 @@ TEST_F(OfflineServiceControllerTest, CanSendBytePayload) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanSendStreamPayload) { +TEST_P(OfflineServiceControllerTest, CanSendStreamPayload) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); auto pipe = std::make_shared(); @@ -258,10 +283,10 @@ TEST_F(OfflineServiceControllerTest, CanSendStreamPayload) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanCancelStreamPayload) { +TEST_P(OfflineServiceControllerTest, CanCancelStreamPayload) { env_.Start(); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); ByteArray message(std::string{kMessage}); auto pipe = std::make_shared(); @@ -298,11 +323,11 @@ TEST_F(OfflineServiceControllerTest, CanCancelStreamPayload) { env_.Stop(); } -TEST_F(OfflineServiceControllerTest, CanDisconnect) { +TEST_P(OfflineServiceControllerTest, CanDisconnect) { env_.Start(); CountDownLatch disconnect_latch(1); - OfflineSimulationUser user_a(kDeviceA); - OfflineSimulationUser user_b(kDeviceB); + OfflineSimulationUser user_a(kDeviceA, GetParam()); + OfflineSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); NEARBY_LOGS(INFO) << "Disconnecting"; user_b.ExpectDisconnect(disconnect_latch); @@ -315,6 +340,10 @@ TEST_F(OfflineServiceControllerTest, CanDisconnect) { env_.Stop(); } +INSTANTIATE_TEST_SUITE_P(ParametrisedOfflineServiceControllerTest, + OfflineServiceControllerTest, + ::testing::ValuesIn(kTestCases)); + } // namespace } // namespace connections } // namespace nearby diff --git a/cpp/core_v2/internal/offline_simulation_user.cc b/cpp/core_v2/internal/offline_simulation_user.cc index 6ed65174..4f79ac99 100644 --- a/cpp/core_v2/internal/offline_simulation_user.cc +++ b/cpp/core_v2/internal/offline_simulation_user.cc @@ -1,6 +1,7 @@ #include "core_v2/internal/offline_simulation_user.h" #include "core_v2/listeners.h" +#include "platform_v2/base/byte_array.h" #include "platform_v2/public/count_down_latch.h" #include "platform_v2/public/system_clock.h" #include "absl/functional/bind_front.h" @@ -18,7 +19,7 @@ void OfflineSimulationUser::OnConnectionInitiated( NEARBY_LOG(INFO, "StartAdvertising: initiated_cb called"); discovered_ = DiscoveredInfo{ .endpoint_id = endpoint_id, - .endpoint_name = name_, + .endpoint_info = GetInfo(), .service_id = service_id_, }; } @@ -43,12 +44,12 @@ void OfflineSimulationUser::OnEndpointDisconnect( } void OfflineSimulationUser::OnEndpointFound(const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& service_id) { NEARBY_LOG(INFO, "Device discovered: id=%s", endpoint_id.c_str()); discovered_ = DiscoveredInfo{ .endpoint_id = endpoint_id, - .endpoint_name = endpoint_name, + .endpoint_info = endpoint_info, .service_id = service_id, }; if (found_latch_) found_latch_->CountDown(); @@ -107,7 +108,7 @@ Status OfflineSimulationUser::StartAdvertising(const std::string& service_id, }; return ctrl_.StartAdvertising(&client_, service_id_, options_, { - .name = name_, + .endpoint_info = info_, .listener = std::move(listener), }); } @@ -117,8 +118,10 @@ void OfflineSimulationUser::StopAdvertising() { } Status OfflineSimulationUser::StartDiscovery(const std::string& service_id, - CountDownLatch* latch) { - found_latch_ = latch; + CountDownLatch* found_latch, + CountDownLatch* lost_latch) { + found_latch_ = found_latch; + lost_latch_ = lost_latch; DiscoveryListener listener = { .endpoint_found_cb = absl::bind_front(&OfflineSimulationUser::OnEndpointFound, this), @@ -144,11 +147,13 @@ Status OfflineSimulationUser::RequestConnection(CountDownLatch* latch) { .disconnected_cb = absl::bind_front(&OfflineSimulationUser::OnEndpointDisconnect, this), }; - return ctrl_.RequestConnection(&client_, discovered_.endpoint_id, - { - .name = discovered_.endpoint_name, - .listener = std::move(listener), - }); + return ctrl_.RequestConnection( + &client_, discovered_.endpoint_id, + { + .endpoint_info = discovered_.endpoint_info, + .listener = std::move(listener), + }, + connection_options_); } Status OfflineSimulationUser::AcceptConnection(CountDownLatch* latch) { diff --git a/cpp/core_v2/internal/offline_simulation_user.h b/cpp/core_v2/internal/offline_simulation_user.h index 4c00d8eb..5b103b85 100644 --- a/cpp/core_v2/internal/offline_simulation_user.h +++ b/cpp/core_v2/internal/offline_simulation_user.h @@ -5,6 +5,7 @@ #include "core_v2/internal/client_proxy.h" #include "core_v2/internal/offline_service_controller.h" +#include "core_v2/options.h" #include "platform_v2/public/atomic_boolean.h" #include "platform_v2/public/condition_variable.h" #include "platform_v2/public/count_down_latch.h" @@ -25,15 +26,21 @@ class OfflineSimulationUser { public: struct DiscoveredInfo { std::string endpoint_id; - std::string endpoint_name; + ByteArray endpoint_info; std::string service_id; bool Empty() const { return endpoint_id.empty(); } void Clear() { endpoint_id.clear(); } }; - explicit OfflineSimulationUser(absl::string_view device_name) - : name_(device_name) {} + explicit OfflineSimulationUser( + absl::string_view device_name, + BooleanMediumSelector allowed = BooleanMediumSelector()) + : info_{ByteArray{std::string(device_name)}}, + options_{ + .strategy = Strategy::kP2pCluster, + .allowed = allowed, + } {} virtual ~OfflineSimulationUser() = default; // Calls PcpManager::StartAdvertising(). @@ -45,9 +52,13 @@ class OfflineSimulationUser { void StopAdvertising(); // Calls PcpManager::StartDiscovery(). - // If latch is provided, will call latch->CountDown() in the endpoint_found_cb - // callback. - Status StartDiscovery(const std::string& service_id, CountDownLatch* latch); + // If found_latch is provided, will call found_latch->CountDown() in the + // endpoint_found_cb callback. + // If lost_latch is provided, will call lost_latch->CountDown() in the + // endpoint_lost_cb callback. + Status StartDiscovery(const std::string& service_id, + CountDownLatch* found_latch, + CountDownLatch* lost_latch = nullptr); // Calls PcpManager::StopDiscovery(). void StopDiscovery(); @@ -79,7 +90,7 @@ class OfflineSimulationUser { void ExpectDisconnect(CountDownLatch& latch) { disconnect_latch_ = &latch; } const DiscoveredInfo& GetDiscovered() const { return discovered_; } - std::string GetName() const { return name_; } + ByteArray GetInfo() const { return info_; } bool WaitForProgress(std::function pred, absl::Duration timeout); @@ -109,6 +120,8 @@ class OfflineSimulationUser { } void Stop() { + StopAdvertising(); + StopDiscovery(); ctrl_.Stop(); } @@ -123,7 +136,7 @@ class OfflineSimulationUser { // DiscoveryListener callbacks void OnEndpointFound(const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& service_id); void OnEndpointLost(const std::string& endpoint_id); @@ -134,6 +147,8 @@ class OfflineSimulationUser { std::string service_id_; DiscoveredInfo discovered_; + ConnectionOptions connection_options_; + Mutex progress_mutex_; ConditionVariable progress_sync_{&progress_mutex_}; PayloadProgressInfo progress_info_; @@ -148,8 +163,8 @@ class OfflineSimulationUser { CountDownLatch* disconnect_latch_ = nullptr; Future* future_ = nullptr; std::function predicate_; - std::string name_; - ConnectionOptions options_{.strategy = Strategy::kP2pCluster}; + ByteArray info_; + ConnectionOptions options_; ClientProxy client_; OfflineServiceController ctrl_; }; diff --git a/cpp/core_v2/internal/p2p_cluster_pcp_handler.cc b/cpp/core_v2/internal/p2p_cluster_pcp_handler.cc index 0ca1ee8c..ad7c8bc3 100644 --- a/cpp/core_v2/internal/p2p_cluster_pcp_handler.cc +++ b/cpp/core_v2/internal/p2p_cluster_pcp_handler.cc @@ -1,6 +1,8 @@ #include "core_v2/internal/p2p_cluster_pcp_handler.h" #include "core_v2/internal/base_pcp_handler.h" +#include "core_v2/internal/ble_advertisement.h" +#include "core_v2/internal/ble_endpoint_channel.h" #include "core_v2/internal/bluetooth_endpoint_channel.h" #include "core_v2/internal/mediums/webrtc/webrtc_socket_wrapper.h" #include "core_v2/internal/webrtc_endpoint_channel.h" @@ -8,6 +10,8 @@ #include "platform_v2/base/types.h" #include "platform_v2/public/crypto.h" #include "proto/connections_enums.pb.h" +#include "absl/functional/bind_front.h" +#include "absl/strings/escaping.h" namespace location { namespace nearby { @@ -22,13 +26,14 @@ ByteArray P2pClusterPcpHandler::GenerateHash(const std::string& source, } P2pClusterPcpHandler::P2pClusterPcpHandler( - Mediums& mediums, EndpointManager* endpoint_manager, + Mediums* mediums, EndpointManager* endpoint_manager, EndpointChannelManager* endpoint_channel_manager, Pcp pcp) - : BasePcpHandler(endpoint_manager, endpoint_channel_manager, pcp), - bluetooth_radio_(mediums.GetBluetoothRadio()), - bluetooth_medium_(mediums.GetBluetoothClassic()), - wifi_lan_medium_(mediums.GetWifiLan()), - webrtc_medium_(mediums.GetWebRtc()) {} + : BasePcpHandler(mediums, endpoint_manager, endpoint_channel_manager, pcp), + bluetooth_radio_(mediums->GetBluetoothRadio()), + bluetooth_medium_(mediums->GetBluetoothClassic()), + ble_medium_(mediums->GetBle()), + wifi_lan_medium_(mediums->GetWifiLan()), + webrtc_medium_(mediums->GetWebRtc()) {} // Returns a vector or mediums sorted in order or decreasing priority for // all the supported mediums. @@ -45,6 +50,9 @@ P2pClusterPcpHandler::GetConnectionMediumsByPriority() { if (bluetooth_medium_.IsAvailable()) { mediums.push_back(proto::connections::BLUETOOTH); } + if (ble_medium_.IsAvailable()) { + mediums.push_back(proto::connections::BLE); + } return mediums; } @@ -54,35 +62,55 @@ proto::connections::Medium P2pClusterPcpHandler::GetDefaultUpgradeMedium() { BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartAdvertisingImpl( ClientProxy* client, const std::string& service_id, - const std::string& local_endpoint_id, - const std::string& local_endpoint_name, const ConnectionOptions& options) { + const std::string& local_endpoint_id, const ByteArray& local_endpoint_info, + const ConnectionOptions& options) { std::vector mediums_started_successfully; - const ByteArray wifi_lan_hash = - GenerateHash(service_id, WifiLanServiceInfo::kServiceIdHashLength); - proto::connections::Medium wifi_lan_medium = - StartWifiLanAdvertising(client, service_id, wifi_lan_hash, - local_endpoint_id, local_endpoint_name); - if (wifi_lan_medium != proto::connections::UNKNOWN_MEDIUM) { - NEARBY_LOG(INFO, - "P2pClusterPcpHandler::StartAdvertisingImpl: WifiLan added"); - mediums_started_successfully.push_back(wifi_lan_medium); + if (options.allowed.wifi_lan) { + const ByteArray wifi_lan_hash = + GenerateHash(service_id, WifiLanServiceInfo::kServiceIdHashLength); + proto::connections::Medium wifi_lan_medium = + StartWifiLanAdvertising(client, service_id, wifi_lan_hash, + local_endpoint_id, local_endpoint_info); + if (wifi_lan_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::StartAdvertisingImpl: WifiLan added"); + mediums_started_successfully.push_back(wifi_lan_medium); + } } - proto::connections::Medium webrtc_medium = StartListeningForWebRtcConnections( - client, service_id, local_endpoint_id, local_endpoint_name); - if (webrtc_medium != proto::connections::UNKNOWN_MEDIUM) { - mediums_started_successfully.push_back(webrtc_medium); + if (options.allowed.web_rtc) { + proto::connections::Medium webrtc_medium = + StartListeningForWebRtcConnections( + client, service_id, local_endpoint_id, local_endpoint_info); + if (webrtc_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::StartAdvertisingImpl: WebRtc added"); + mediums_started_successfully.push_back(webrtc_medium); + } } - const ByteArray bluetooth_hash = - GenerateHash(service_id, BluetoothDeviceName::kServiceIdHashLength); - proto::connections::Medium bluetooth_medium = - StartBluetoothAdvertising(client, service_id, bluetooth_hash, - local_endpoint_id, local_endpoint_name); - if (bluetooth_medium != proto::connections::UNKNOWN_MEDIUM) { - NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartAdvertisingImpl: BT added"); - mediums_started_successfully.push_back(bluetooth_medium); + if (options.allowed.bluetooth) { + const ByteArray bluetooth_hash = + GenerateHash(service_id, BluetoothDeviceName::kServiceIdHashLength); + proto::connections::Medium bluetooth_medium = + StartBluetoothAdvertising(client, service_id, bluetooth_hash, + local_endpoint_id, local_endpoint_info); + if (bluetooth_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartAdvertisingImpl: BT added"); + mediums_started_successfully.push_back(bluetooth_medium); + } + } + + if (options.allowed.ble) { + const ByteArray ble_hash = + GenerateHash(service_id, BleAdvertisement::kServiceIdHashLength); + proto::connections::Medium ble_medium = StartBleAdvertising( + client, service_id, ble_hash, local_endpoint_id, local_endpoint_info); + if (ble_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartAdvertisingImpl: Ble added"); + mediums_started_successfully.push_back(ble_medium); + } } if (mediums_started_successfully.empty()) { @@ -106,6 +134,8 @@ Status P2pClusterPcpHandler::StopAdvertisingImpl(ClientProxy* client) { bluetooth_medium_.TurnOffDiscoverability(); bluetooth_medium_.StopAcceptingConnections(client->GetAdvertisingServiceId()); + ble_medium_.StopAdvertising(client->GetAdvertisingServiceId()); + webrtc_medium_.StopAcceptingConnections(); wifi_lan_medium_.StopAdvertising(client->GetAdvertisingServiceId()); @@ -146,90 +176,210 @@ bool P2pClusterPcpHandler::IsRecognizedBluetoothEndpoint( return true; } -std::function -P2pClusterPcpHandler::MakeBluetoothDeviceDiscoveredHandler( - ClientProxy* client, const std::string& service_id) { - return [this, client, service_id](BluetoothDevice& device) { - RunOnPcpHandlerThread([this, client, service_id, &device]() { - // Make sure we are still discovering before proceeding. - if (!client->IsDiscovering()) { - NEARBY_LOG(INFO, - "BT discovery handler (FOUND) [client=%p, service=%s]: not " - "in discovery mode", - client, service_id.c_str()); - return; - } - - // Parse the Bluetooth device name. - const std::string& device_name_string = device.GetName(); - BluetoothDeviceName device_name(device_name_string); - - // Make sure the Bluetooth device name points to a valid - // endpoint we're discovering. - if (!IsRecognizedBluetoothEndpoint(device_name_string, service_id, - device_name)) - return; - - // Report the discovered endpoint to the client. +void P2pClusterPcpHandler::BluetoothDeviceDiscoveredHandler( + ClientProxy* client, const std::string& service_id, + BluetoothDevice& device) { + RunOnPcpHandlerThread([this, client, service_id, &device]() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { NEARBY_LOG(INFO, - "Invoking BasePcpHandler::OnEndpointFound() for BT " - "service=%s; id=%s; name=%s", - service_id.c_str(), device_name.GetEndpointId().c_str(), - device_name.GetEndpointName().c_str()); - OnEndpointFound(client, - std::make_shared(BluetoothEndpoint{ - { - device_name.GetEndpointId(), - device_name.GetEndpointName(), - service_id, - proto::connections::Medium::BLUETOOTH, - }, - device, - })); - }); - }; + "BT discovery handler (FOUND) [client=%p, service=%s]: not " + "in discovery mode", + client, service_id.c_str()); + return; + } + + // Parse the Bluetooth device name. + const std::string& device_name_string = device.GetName(); + BluetoothDeviceName device_name(device_name_string); + + // Make sure the Bluetooth device name points to a valid + // endpoint we're discovering. + if (!IsRecognizedBluetoothEndpoint(device_name_string, service_id, + device_name)) + return; + + // Report the discovered endpoint to the client. + NEARBY_LOGS(INFO) + << "Invoking BasePcpHandler::OnEndpointFound() for BT service=" + << service_id << "; id=" << device_name.GetEndpointId() << "; name=" + << absl::BytesToHexString(device_name.GetEndpointInfo().data()); + OnEndpointFound(client, + std::make_shared(BluetoothEndpoint{ + { + device_name.GetEndpointId(), + device_name.GetEndpointInfo(), + service_id, + proto::connections::Medium::BLUETOOTH, + }, + device, + })); + }); } -std::function -P2pClusterPcpHandler::MakeBluetoothDeviceLostHandler( - ClientProxy* client, const std::string& service_id) { - return [this, client, service_id](BluetoothDevice& device) { - RunOnPcpHandlerThread([this, client, &service_id, &device]() { - // Make sure we are still discovering before proceeding. - if (!client->IsDiscovering()) { - NEARBY_LOG(INFO, - "BT discovery handler (LOST) [client=%p, service=%s]: not " - "in discovery mode", - client, service_id.c_str()); - return; - } +void P2pClusterPcpHandler::BluetoothDeviceLostHandler( + ClientProxy* client, const std::string& service_id, + BluetoothDevice& device) { + const std::string& device_name_string = device.GetName(); + RunOnPcpHandlerThread([this, client, service_id, device_name_string]() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { + NEARBY_LOG(INFO, + "BT discovery handler (LOST) [client=%p, service=%s]: not " + "in discovery mode", + client, service_id.c_str()); + return; + } - // Parse the Bluetooth device name. - const std::string& device_name_string = device.GetName(); - BluetoothDeviceName device_name(device_name_string); + // Parse the Bluetooth device name. + BluetoothDeviceName device_name(device_name_string); - // Make sure the Bluetooth device name points to a valid - // endpoint we're discovering. - if (!IsRecognizedBluetoothEndpoint(device_name_string, service_id, - device_name)) - return; + // Make sure the Bluetooth device name points to a valid + // endpoint we're discovering. + if (!IsRecognizedBluetoothEndpoint(device_name_string, service_id, + device_name)) + return; + + // Report the discovered endpoint to the client. + NEARBY_LOG(INFO, + "BT discovery handler (LOST) [client=%p, service=%s]: report " + "to client", + client, service_id.c_str()); + OnEndpointLost(client, DiscoveredEndpoint{ + device_name.GetEndpointId(), + device_name.GetEndpointInfo(), + service_id, + proto::connections::Medium::BLUETOOTH, + }); + }); +} + +bool P2pClusterPcpHandler::IsRecognizedBleEndpoint( + const std::string& service_id, + const BleAdvertisement& advertisement) const { + if (!advertisement.IsValid()) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::IsRecognizedBleEndpoint: advertisement " + "is invalid"); + return false; + } + + if (advertisement.GetVersion() != BleAdvertisement::Version::kV1) { + NEARBY_LOG( + INFO, + "P2pClusterPcpHandler::IsRecognizedBluetoothEndpoint: Version is " + "not matched; advertisement.Version=%d, Version=%d", + advertisement.GetVersion(), BleAdvertisement::Version::kV1); + return false; + } + + if (advertisement.GetPcp() != GetPcp()) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::IsRecognizedBluetoothEndpoint: Pcp is " + "not matched; advertisement.Pcp=%d, Pcp=%d", + advertisement.GetPcp(), GetPcp()); + return false; + } + + ByteArray expected_service_id_hash = + GenerateHash(service_id, BluetoothDeviceName::kServiceIdHashLength); + + if (advertisement.GetServiceIdHash() != expected_service_id_hash) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::IsRecognizedBleEndpoint: service " + "id hash is " + "not matched; advertisement.service_id_hash=%s, expected=%s", + advertisement.GetServiceIdHash().data(), + expected_service_id_hash.data()); + return false; + } + + return true; +} + +void P2pClusterPcpHandler::BlePeripheralDiscoveredHandler( + ClientProxy* client, BlePeripheral& peripheral, + const std::string& service_id) { + RunOnPcpHandlerThread([this, client, service_id, &peripheral]() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { + NEARBY_LOG(INFO, + "Ble scanning handler (FOUND) [client=%p, service_id=%s]: not " + "in discovery mode", + client, service_id.c_str()); + return; + } + + // Parse the Ble advertisement bytes. + BleAdvertisement advertisement( + /*fast_advertisement=*/false, + peripheral.GetAdvertisementBytes(service_id)); + + // Make sure the Ble advertisement points to a valid + // endpoint we're discovering. + if (!IsRecognizedBleEndpoint(service_id, advertisement)) return; + + // Store all the state we need to be able to re-create a BleEndpoint + // in BlePeripheralLostHandler, since that isn't privy to + // the bytes of the ble advertisement itself. + found_ble_endpoints_.emplace( + peripheral.GetName(), + BleEndpointState(advertisement.GetEndpointId(), + advertisement.GetEndpointInfo())); + + // Report the discovered endpoint to the client. + NEARBY_LOGS(INFO) + << "Invoking BasePcpHandler::OnEndpointFound() for Ble service=" + << service_id << "; id=" << advertisement.GetEndpointId() << "; name=" + << absl::BytesToHexString(advertisement.GetEndpointInfo().data()); + OnEndpointFound(client, std::make_shared(BleEndpoint{ + { + advertisement.GetEndpointId(), + advertisement.GetEndpointInfo(), + service_id, + proto::connections::Medium::BLE, + }, + peripheral, + })); + }); +} + +void P2pClusterPcpHandler::BlePeripheralLostHandler( + ClientProxy* client, BlePeripheral& peripheral, + const std::string& service_id) { + std::string peripheral_name = peripheral.GetName(); + NEARBY_LOG(INFO, "Ble: [LOST, SCHED] peripheral_name=%s", + peripheral_name.c_str()); + RunOnPcpHandlerThread([this, client, service_id, &peripheral]() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { + NEARBY_LOG(INFO, + "Ble scanning handler (LOST) [client=%p, service_id=%s]: not " + "in scanning mode", + client, service_id.c_str()); + return; + } + + // Remove this BlePeripheral from found_ble_endpoints_, and + // report the endpoint as lost to the client. + auto item = found_ble_endpoints_.find(peripheral.GetName()); + if (item != found_ble_endpoints_.end()) { + BleEndpointState ble_endpoint_state(item->second); + found_ble_endpoints_.erase(item); // Report the discovered endpoint to the client. NEARBY_LOG(INFO, - "BT discovery handler (LOST) [client=%p, service=%s]: report " - "to client", + "Ble scanning handler (LOST) [client=%p, " + "service_id=%s]: report to client", client, service_id.c_str()); - OnEndpointLost(client, BluetoothEndpoint{ - { - device_name.GetEndpointId(), - device_name.GetEndpointName(), - service_id, - proto::connections::Medium::BLUETOOTH, - }, - device, + OnEndpointLost(client, DiscoveredEndpoint{ + ble_endpoint_state.endpoint_id, + ble_endpoint_state.endpoint_info, + service_id, + proto::connections::Medium::BLE, }); - }); - }; + } + }); } bool P2pClusterPcpHandler::IsRecognizedWifiLanEndpoint( @@ -266,90 +416,84 @@ bool P2pClusterPcpHandler::IsRecognizedWifiLanEndpoint( return true; } -std::function -P2pClusterPcpHandler::MakeWifiLanServiceDiscoveredHandler( - ClientProxy* client, const std::string& service_id) { - return [this, client](WifiLanService& service, - const std::string& service_id) { - RunOnPcpHandlerThread([this, client, service_id, &service]() { - // Make sure we are still discovering before proceeding. - if (!client->IsDiscovering()) { - NEARBY_LOG( - INFO, - "WifiLan discovery handler (FOUND) [client=%p, service=%s]: not " - "in discovery mode", - client, service_id.c_str()); - return; - } - - // Parse the WifiLan service name. - const std::string& service_info_name = service.GetName(); - WifiLanServiceInfo service_info(service_info_name); - - // Make sure the WifiLan service name points to a valid - // endpoint we're discovering. - if (!IsRecognizedWifiLanEndpoint(service_id, service_info)) return; - - // Report the discovered endpoint to the client. - NEARBY_LOG(INFO, - "Invoking BasePcpHandler::OnEndpointFound() for WifiLan " - "service=%s; id=%s; name=%s", - service_id.c_str(), service_info.GetEndpointId().c_str(), - service_info.GetEndpointName().c_str()); - OnEndpointFound(client, std::make_shared(WifiLanEndpoint{ - { - service_info.GetEndpointId(), - service_info.GetEndpointName(), - service_id, - proto::connections::Medium::WIFI_LAN, - }, - service, - })); - }); - }; -} - -std::function -P2pClusterPcpHandler::MakeWifiLanServiceLostHandler( - ClientProxy* client, const std::string& service_id) { - return [this, client](WifiLanService& service, - const std::string& service_id) { - RunOnPcpHandlerThread([this, client, &service_id, &service]() { - // Make sure we are still discovering before proceeding. - if (!client->IsDiscovering()) { - NEARBY_LOG( - INFO, - "WifiLan discovery handler (LOST) [client=%p, service=%s]: not " - "in discovery mode", - client, service_id.c_str()); - return; - } - - // Parse the WifiLan service name. - const std::string& service_info_name = service.GetName(); - WifiLanServiceInfo service_info(service_info_name); - - // Make sure the WifiLan service name points to a valid - // endpoint we're discovering. - if (!IsRecognizedWifiLanEndpoint(service_id, service_info)) return; - - // Report the discovered endpoint to the client. +void P2pClusterPcpHandler::WifiLanServiceDiscoveredHandler( + ClientProxy* client, WifiLanService& service, + const std::string& service_id) { + RunOnPcpHandlerThread([this, client, service_id, &service]() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { NEARBY_LOG( INFO, - "WifiLan discovery handler (LOST) [client=%p, service=%s]: report " - "to client", + "WifiLan discovery handler (FOUND) [client=%p, service=%s]: not " + "in discovery mode", client, service_id.c_str()); - OnEndpointLost(client, WifiLanEndpoint{ - { - service_info.GetEndpointId(), - service_info.GetEndpointName(), - service_id, - proto::connections::Medium::WIFI_LAN, - }, - service, - }); - }); - }; + return; + } + + // Parse the WifiLan service name. + const std::string& service_info_name = service.GetName(); + WifiLanServiceInfo service_info(service_info_name); + + // Make sure the WifiLan service name points to a valid + // endpoint we're discovering. + if (!IsRecognizedWifiLanEndpoint(service_id, service_info)) return; + + // Report the discovered endpoint to the client. + NEARBY_LOG( + INFO, + "Invoking BasePcpHandler::OnEndpointFound() for WifiLan " + "service=%s; id=%s; name=%s", + service_id.c_str(), service_info.GetEndpointId().c_str(), + absl::BytesToHexString(service_info.GetEndpointInfo().data()).c_str()); + OnEndpointFound(client, std::make_shared(WifiLanEndpoint{ + { + service_info.GetEndpointId(), + service_info.GetEndpointInfo(), + service_id, + proto::connections::Medium::WIFI_LAN, + }, + service, + })); + }); +} + +void P2pClusterPcpHandler::WifiLanServiceLostHandler( + ClientProxy* client, WifiLanService& service, + const std::string& service_id) { + std::string service_info_name = service.GetName(); + NEARBY_LOG(INFO, "WifiLAN: [LOST, SCHED] service_info_name=%s", + service_info_name.c_str()); + RunOnPcpHandlerThread([this, client, service_id, service_info_name]() { + // Make sure we are still discovering before proceeding. + if (!client->IsDiscovering()) { + NEARBY_LOG( + INFO, + "WifiLan discovery handler (LOST) [client=%p, service=%s]: not " + "in discovery mode", + client, service_id.c_str()); + return; + } + + // Parse the WifiLan service name. + WifiLanServiceInfo service_info(service_info_name); + + // Make sure the WifiLan service name points to a valid + // endpoint we're discovering. + if (!IsRecognizedWifiLanEndpoint(service_id, service_info)) return; + + // Report the discovered endpoint to the client. + NEARBY_LOG( + INFO, + "WifiLan discovery handler (LOST) [client=%p, service_id=%s]: report " + "to client", + client, service_id.c_str()); + OnEndpointLost(client, DiscoveredEndpoint{ + service_info.GetEndpointId(), + service_info.GetEndpointInfo(), + service_id, + proto::connections::Medium::WIFI_LAN, + }); + }); } BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( @@ -357,28 +501,54 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( const ConnectionOptions& options) { std::vector mediums_started_successfully; - proto::connections::Medium wifi_lan_medium = StartWifiLanDiscovery( - { - .service_discovered_cb = - MakeWifiLanServiceDiscoveredHandler(client, service_id), - .service_lost_cb = MakeWifiLanServiceLostHandler(client, service_id), - }, - client, service_id); - if (wifi_lan_medium != proto::connections::UNKNOWN_MEDIUM) { - NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartDiscoveryImpl: WifiLan added"); - mediums_started_successfully.push_back(wifi_lan_medium); + if (options.allowed.wifi_lan) { + proto::connections::Medium wifi_lan_medium = StartWifiLanDiscovery( + { + .service_discovered_cb = absl::bind_front( + &P2pClusterPcpHandler::WifiLanServiceDiscoveredHandler, this, + client), + .service_lost_cb = absl::bind_front( + &P2pClusterPcpHandler::WifiLanServiceLostHandler, this, client), + }, + client, service_id); + if (wifi_lan_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::StartDiscoveryImpl: WifiLan added"); + mediums_started_successfully.push_back(wifi_lan_medium); + } } - proto::connections::Medium bluetooth_medium = StartBluetoothDiscovery( - { - .device_discovered_cb = - MakeBluetoothDeviceDiscoveredHandler(client, service_id), - .device_lost_cb = MakeBluetoothDeviceLostHandler(client, service_id), - }, - client, service_id); - if (bluetooth_medium != proto::connections::UNKNOWN_MEDIUM) { - NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartDiscoveryImpl: BT added"); - mediums_started_successfully.push_back(bluetooth_medium); + if (options.allowed.bluetooth) { + proto::connections::Medium bluetooth_medium = StartBluetoothDiscovery( + { + .device_discovered_cb = absl::bind_front( + &P2pClusterPcpHandler::BluetoothDeviceDiscoveredHandler, this, + client, service_id), + .device_lost_cb = absl::bind_front( + &P2pClusterPcpHandler::BluetoothDeviceLostHandler, this, client, + service_id), + }, + client, service_id); + if (bluetooth_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartDiscoveryImpl: BT added"); + mediums_started_successfully.push_back(bluetooth_medium); + } + } + + if (options.allowed.ble) { + proto::connections::Medium ble_medium = StartBleScanning( + { + .peripheral_discovered_cb = absl::bind_front( + &P2pClusterPcpHandler::BlePeripheralDiscoveredHandler, this, + client), + .peripheral_lost_cb = absl::bind_front( + &P2pClusterPcpHandler::BlePeripheralLostHandler, this, client), + }, + client, service_id); + if (ble_medium != proto::connections::UNKNOWN_MEDIUM) { + NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartDiscoveryImpl: Ble added"); + mediums_started_successfully.push_back(ble_medium); + } } if (mediums_started_successfully.empty()) { @@ -397,6 +567,7 @@ BasePcpHandler::StartOperationResult P2pClusterPcpHandler::StartDiscoveryImpl( Status P2pClusterPcpHandler::StopDiscoveryImpl(ClientProxy* client) { wifi_lan_medium_.StopDiscovery(client->GetDiscoveryServiceId()); bluetooth_medium_.StopDiscovery(); + ble_medium_.StopScanning(client->GetDiscoveryServiceId()); return {Status::kSuccess}; } @@ -415,6 +586,13 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::ConnectImpl( } break; } + case proto::connections::Medium::BLE: { + auto* ble_endpoint = down_cast(endpoint); + if (ble_endpoint) { + return BleConnectImpl(client, ble_endpoint); + } + break; + } case proto::connections::Medium::WIFI_LAN: { auto* wifi_lan_endpoint = down_cast(endpoint); if (wifi_lan_endpoint) { @@ -441,7 +619,7 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::ConnectImpl( proto::connections::Medium P2pClusterPcpHandler::StartBluetoothAdvertising( ClientProxy* client, const std::string& service_id, const ByteArray& service_id_hash, const std::string& local_endpoint_id, - const std::string& local_endpoint_name) { + const ByteArray& local_endpoint_info) { // Start listening for connections before advertising in case a connection // request comes in very quickly. NEARBY_LOG( @@ -460,20 +638,22 @@ proto::connections::Medium P2pClusterPcpHandler::StartBluetoothAdvertising( service_id.c_str()); if (!bluetooth_radio_.Enable() || !bluetooth_medium_.StartAcceptingConnections( - service_id, {.accepted_cb = [this, client, local_endpoint_name]( + service_id, {.accepted_cb = [this, client, local_endpoint_info]( BluetoothSocket socket) { if (!socket.IsValid()) { NEARBY_LOG(ERROR, "Invalid socket in accept callback: name=%s", - local_endpoint_name.c_str()); + std::string(local_endpoint_info).c_str()); return; } - RunOnPcpHandlerThread([this, client, local_endpoint_name, + RunOnPcpHandlerThread([this, client, local_endpoint_info, socket = std::move(socket)]() mutable { std::string remote_device_name = socket.GetRemoteDevice().GetName(); auto channel = absl::make_unique( remote_device_name, socket); - OnIncomingConnection(client, remote_device_name, + ByteArray remote_device_info{remote_device_name}; + + OnIncomingConnection(client, remote_device_info, std::move(channel), proto::connections::Medium::BLUETOOTH); }); @@ -487,11 +667,12 @@ proto::connections::Medium P2pClusterPcpHandler::StartBluetoothAdvertising( "P2pClusterPcpHandler::StartBluetoothAdvertising: service=%s: " "make name; id=%s, hash=%s, name=%s", service_id.c_str(), local_endpoint_id.c_str(), - std::string(service_id_hash).c_str(), local_endpoint_name.c_str()); + absl::BytesToHexString(service_id_hash.data()).c_str(), + absl::BytesToHexString(local_endpoint_info.data()).c_str()); // Generate a BluetoothDeviceName with which to become Bluetooth discoverable. std::string device_name(BluetoothDeviceName( BluetoothDeviceName::Version::kV1, GetPcp(), local_endpoint_id, - service_id_hash, local_endpoint_name)); + service_id_hash, local_endpoint_info)); if (device_name.empty()) { NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartBluetoothAdvertising: generate " @@ -564,10 +745,132 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BluetoothConnectImpl( }; } +proto::connections::Medium P2pClusterPcpHandler::StartBleAdvertising( + ClientProxy* client, const std::string& service_id, + const ByteArray& service_id_hash, const std::string& local_endpoint_id, + const ByteArray& local_endpoint_info) { + // Start listening for connections before advertising in case a connection + // request comes in very quickly. + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleAdvertising: service_id=" + << service_id << ": start"; + if (ble_medium_.IsAcceptingConnections(service_id)) { + NEARBY_LOGS(ERROR) << "Ble is already accepting connections for service_id=" + << service_id; + return proto::connections::UNKNOWN_MEDIUM; + } + + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleAdvertising: service_id=" + << service_id << ": invoking"; + if (!bluetooth_radio_.Enable() || + !ble_medium_.StartAcceptingConnections( + service_id, + {.accepted_cb = [this, client, local_endpoint_info]( + BleSocket socket, const std::string& service_id) { + if (!socket.IsValid()) { + NEARBY_LOG(ERROR, "Invalid socket in accept callback: name=%s", + std::string(local_endpoint_info).c_str()); + return; + } + RunOnPcpHandlerThread([this, client, local_endpoint_info, + service_id, + socket = std::move(socket)]() mutable { + std::string remote_peripheral_name = + socket.GetRemotePeripheral().GetName(); + auto channel = absl::make_unique( + remote_peripheral_name, socket); + ByteArray remote_peripheral_info = + socket.GetRemotePeripheral().GetAdvertisementBytes( + service_id); + + OnIncomingConnection(client, remote_peripheral_info, + std::move(channel), + proto::connections::Medium::BLE); + }); + }})) { + NEARBY_LOGS(ERROR) + << "Ble failed to start accepting connections for service_id=" + << service_id; + return proto::connections::UNKNOWN_MEDIUM; + } + + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::StartBleAdvertising: service=%s: " + "make advertisement; id=%s, hash=%s, name=%s", + service_id.c_str(), local_endpoint_id.c_str(), + std::string(service_id_hash).c_str(), + std::string(local_endpoint_info).c_str()); + // Generate a BleAdvertisement with which to become Ble discoverable. + // TODO(edwinwu): Add a bluetooth_adapter method to get the mac address. + std::string bluetooth_mac_address; + ByteArray advertisement_bytes(BleAdvertisement( + BleAdvertisement::Version::kV1, GetPcp(), service_id_hash, + local_endpoint_id, local_endpoint_info, bluetooth_mac_address)); + if (advertisement_bytes.Empty()) { + NEARBY_LOG(INFO, + "P2pClusterPcpHandler::StartBleAdvertising: generate " + "BleAdvertisement failed"); + ble_medium_.StopAcceptingConnections(service_id); + return proto::connections::UNKNOWN_MEDIUM; + } else { + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleAdvertising: generate " + "BleAdvertisement succeeded; advertisement_bytes=" + << advertisement_bytes.data(); + } + + NEARBY_LOG( + INFO, "P2pClusterPcpHandler::StartBleAdvertising: service_id=%s: come up", + service_id.c_str()); + + if (!ble_medium_.StartAdvertising(service_id, advertisement_bytes)) { + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleAdvertising: failed to " + "start advertising, advertisement_bytes=%p" + << advertisement_bytes.data(); + ble_medium_.StopAcceptingConnections(service_id); + return proto::connections::UNKNOWN_MEDIUM; + } + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleAdvertising: service_id=" + << service_id << ": done"; + return proto::connections::BLE; +} + +proto::connections::Medium P2pClusterPcpHandler::StartBleScanning( + BleDiscoveredPeripheralCallback callback, ClientProxy* client, + const std::string& service_id) { + if (bluetooth_radio_.Enable() && + ble_medium_.StartScanning(service_id, std::move(callback))) { + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleScanning: ok"; + return proto::connections::BLE; + } else { + NEARBY_LOGS(INFO) << "P2pClusterPcpHandler::StartBleScanning: failed"; + return proto::connections::UNKNOWN_MEDIUM; + } +} + +BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::BleConnectImpl( + ClientProxy* client, BleEndpoint* endpoint) { + BlePeripheral& peripheral = endpoint->ble_peripheral; + + BleSocket ble_socket = ble_medium_.Connect(peripheral, endpoint->service_id); + if (!ble_socket.IsValid()) { + return BasePcpHandler::ConnectImplResult{ + .status = {Status::kBleError}, + }; + } + + auto channel = + absl::make_unique(endpoint->endpoint_id, ble_socket); + + return BasePcpHandler::ConnectImplResult{ + .medium = proto::connections::Medium::BLE, + .status = {Status::kSuccess}, + .endpoint_channel = std::move(channel), + }; +} + proto::connections::Medium P2pClusterPcpHandler::StartWifiLanAdvertising( ClientProxy* client, const std::string& service_id, const ByteArray& service_id_hash, const std::string& local_endpoint_id, - const std::string& local_endpoint_name) { + const ByteArray& local_endpoint_info) { // Start listening for connections before advertising in case a connection // request comes in very quickly. NEARBY_LOG(INFO, @@ -584,21 +887,23 @@ proto::connections::Medium P2pClusterPcpHandler::StartWifiLanAdvertising( "P2pClusterPcpHandler::StartWifiLanAdvertising: service=%s: invoking", service_id.c_str()); if (!wifi_lan_medium_.StartAcceptingConnections( - service_id, {.accepted_cb = [this, client, local_endpoint_name]( + service_id, {.accepted_cb = [this, client, local_endpoint_info]( WifiLanSocket socket, const std::string& service_id) { if (!socket.IsValid()) { NEARBY_LOG(ERROR, "Invalid socket in accept callback: name=%s", - local_endpoint_name.c_str()); + std::string(local_endpoint_info).c_str()); return; } - RunOnPcpHandlerThread([this, client, local_endpoint_name, + RunOnPcpHandlerThread([this, client, local_endpoint_info, socket = std::move(socket)]() mutable { std::string remote_service_info_name = socket.GetRemoteWifiLanService().GetName(); auto channel = absl::make_unique( remote_service_info_name, socket); - OnIncomingConnection(client, remote_service_info_name, + ByteArray remote_service_info{remote_service_info_name}; + + OnIncomingConnection(client, remote_service_info, std::move(channel), proto::connections::Medium::WIFI_LAN); }); @@ -613,11 +918,12 @@ proto::connections::Medium P2pClusterPcpHandler::StartWifiLanAdvertising( "P2pClusterPcpHandler::StartWifiLanAdvertising: service=%s: " "make name; id=%s, hash=%s, name=%s", service_id.c_str(), local_endpoint_id.c_str(), - std::string(service_id_hash).c_str(), local_endpoint_name.c_str()); + absl::BytesToHexString(service_id_hash.data()).c_str(), + absl::BytesToHexString(local_endpoint_info.data()).c_str()); // Generate a WifiLanServiceInfo with which to become WifiLan discoverable. std::string service_info_name(WifiLanServiceInfo( WifiLanServiceInfo::Version::kV1, GetPcp(), local_endpoint_id, - service_id_hash, local_endpoint_name)); + service_id_hash, local_endpoint_info)); if (service_info_name.empty()) { NEARBY_LOG(INFO, "P2pClusterPcpHandler::StartWifiLanAdvertising: generate " @@ -687,20 +993,20 @@ BasePcpHandler::ConnectImplResult P2pClusterPcpHandler::WifiLanConnectImpl( proto::connections::Medium P2pClusterPcpHandler::StartListeningForWebRtcConnections( ClientProxy* client, const string& service_id, - const string& local_endpoint_id, const string& local_endpoint_name) { + const string& local_endpoint_id, const ByteArray& local_endpoint_info) { if (!webrtc_medium_.IsAvailable()) { return proto::connections::UNKNOWN_MEDIUM; } if (!webrtc_medium_.IsAcceptingConnections()) { mediums::PeerId self_id = CreatePeerIdFromAdvertisement( - service_id, local_endpoint_id, local_endpoint_name); + service_id, local_endpoint_id, local_endpoint_info); if (!webrtc_medium_.StartAcceptingConnections( - self_id, {[this, client, local_endpoint_name]( + self_id, {[this, client, local_endpoint_info]( mediums::WebRtcSocketWrapper socket) { if (!socket.IsValid()) { NEARBY_LOG(ERROR, "Invalid socket in accept callback: name=%s", - local_endpoint_name.c_str()); + std::string(local_endpoint_info).c_str()); return; } @@ -709,8 +1015,9 @@ P2pClusterPcpHandler::StartListeningForWebRtcConnections( string remote_device_name = "WebRtcSocket"; auto channel = absl::make_unique( remote_device_name, socket); + ByteArray remote_device_info{remote_device_name}; - OnIncomingConnection(client, remote_device_name, + OnIncomingConnection(client, remote_device_info, std::move(channel), proto::connections::WEB_RTC); }); diff --git a/cpp/core_v2/internal/p2p_cluster_pcp_handler.h b/cpp/core_v2/internal/p2p_cluster_pcp_handler.h index 7b5c4172..a18be31f 100644 --- a/cpp/core_v2/internal/p2p_cluster_pcp_handler.h +++ b/cpp/core_v2/internal/p2p_cluster_pcp_handler.h @@ -36,7 +36,7 @@ namespace connections { // connects over Bluetooth. class P2pClusterPcpHandler : public BasePcpHandler { public: - P2pClusterPcpHandler(Mediums& mediums, EndpointManager* endpoint_manager, + P2pClusterPcpHandler(Mediums* mediums, EndpointManager* endpoint_manager, EndpointChannelManager* channel_manager, Pcp pcp = Pcp::kP2pCluster); ~P2pClusterPcpHandler() override = default; @@ -50,7 +50,7 @@ class P2pClusterPcpHandler : public BasePcpHandler { BasePcpHandler::StartOperationResult StartAdvertisingImpl( ClientProxy* client, const std::string& service_id, const std::string& local_endpoint_id, - const std::string& local_endpoint_name, + const ByteArray& local_endpoint_info, const ConnectionOptions& options) override; // @PCPHandlerThread @@ -77,15 +77,36 @@ class P2pClusterPcpHandler : public BasePcpHandler { BluetoothDevice bluetooth_device; }; + struct BleEndpoint : public BasePcpHandler::DiscoveredEndpoint { + BleEndpoint(DiscoveredEndpoint endpoint, BlePeripheral peripheral) + : DiscoveredEndpoint(std::move(endpoint)), + ble_peripheral(std::move(peripheral)) {} + BlePeripheral ble_peripheral; + }; + + // Holds the state required to re-create a BleEndpoint we see on a + // BlePeripheral, so BlePeripheralLostHandler can call + // BasePcpHandler::OnEndpointLost() with the same information as was passed + // in to BasePCPHandler::onEndpointFound(). + struct BleEndpointState { + public: + BleEndpointState(const string& endpoint_id, const ByteArray& endpoint_info) + : endpoint_id(endpoint_id), endpoint_info(endpoint_info) {} + + std::string endpoint_id; + ByteArray endpoint_info; + }; struct WifiLanEndpoint : public BasePcpHandler::DiscoveredEndpoint { WifiLanEndpoint(DiscoveredEndpoint endpoint, WifiLanService service) : DiscoveredEndpoint(std::move(endpoint)), wifi_lan_service(std::move(service)) {} + WifiLanService wifi_lan_service; }; using BluetoothDiscoveredDeviceCallback = BluetoothClassic::DiscoveredDeviceCallback; + using BleDiscoveredPeripheralCallback = Ble::DiscoveredPeripheralCallback; using WifiLanDiscoveredServiceCallback = WifiLan::DiscoveredServiceCallback; static constexpr BluetoothDeviceName::Version kBluetoothDeviceNameVersion = @@ -99,34 +120,55 @@ class P2pClusterPcpHandler : public BasePcpHandler { bool IsRecognizedBluetoothEndpoint(const std::string& name_string, const std::string& service_id, const BluetoothDeviceName& name) const; - std::function MakeBluetoothDeviceDiscoveredHandler( - ClientProxy* client, const std::string& service_id); - std::function MakeBluetoothDeviceLostHandler( - ClientProxy* client, const std::string& service_id); + void BluetoothDeviceDiscoveredHandler(ClientProxy* client, + const std::string& service_id, + BluetoothDevice& device); + void BluetoothDeviceLostHandler(ClientProxy* client, + const std::string& service_id, + BluetoothDevice& device); proto::connections::Medium StartBluetoothAdvertising( ClientProxy* client, const std::string& service_id, const ByteArray& service_id_hash, const std::string& local_endpoint_id, - const std::string& local_endpoint_name); + const ByteArray& local_endpoint_info); proto::connections::Medium StartBluetoothDiscovery( BluetoothDiscoveredDeviceCallback callback, ClientProxy* client, const std::string& service_id); BasePcpHandler::ConnectImplResult BluetoothConnectImpl( ClientProxy* client, BluetoothEndpoint* endpoint); + // Ble + // Maps a BlePeripheral to its corresponding BleEndpointState. + absl::flat_hash_map found_ble_endpoints_; + bool IsRecognizedBleEndpoint(const std::string& service_id, + const BleAdvertisement& advertisement) const; + void BlePeripheralDiscoveredHandler(ClientProxy* client, + BlePeripheral& peripheral, + const std::string& service_id); + void BlePeripheralLostHandler(ClientProxy* client, BlePeripheral& peripheral, + const std::string& service_id); + proto::connections::Medium StartBleAdvertising( + ClientProxy* client, const std::string& service_id, + const ByteArray& service_id_hash, const std::string& local_endpoint_id, + const ByteArray& local_endpoint_info); + proto::connections::Medium StartBleScanning( + BleDiscoveredPeripheralCallback callback, ClientProxy* client, + const std::string& service_id); + BasePcpHandler::ConnectImplResult BleConnectImpl(ClientProxy* client, + BleEndpoint* endpoint); + // WifiLan bool IsRecognizedWifiLanEndpoint( const std::string& service_id, const WifiLanServiceInfo& service_info) const; - std::function - MakeWifiLanServiceDiscoveredHandler(ClientProxy* client, - const std::string& service_id); - std::function - MakeWifiLanServiceLostHandler(ClientProxy* client, - const std::string& service_id); + void WifiLanServiceDiscoveredHandler(ClientProxy* client, + WifiLanService& service, + const std::string& service_id); + void WifiLanServiceLostHandler(ClientProxy* client, WifiLanService& service, + const std::string& service_id); proto::connections::Medium StartWifiLanAdvertising( ClientProxy* client, const std::string& service_id, const ByteArray& service_id_hash, const std::string& local_endpoint_id, - const std::string& local_endpoint_name); + const ByteArray& local_endpoint_info); proto::connections::Medium StartWifiLanDiscovery( WifiLanDiscoveredServiceCallback callback, ClientProxy* client, const std::string& service_id); @@ -137,12 +179,13 @@ class P2pClusterPcpHandler : public BasePcpHandler { proto::connections::Medium StartListeningForWebRtcConnections( ClientProxy* client, const std::string& service_id, const std::string& local_endpoint_id, - const std::string& local_endpoint_name); + const ByteArray& local_endpoint_info); BasePcpHandler::ConnectImplResult WebRtcConnectImpl( ClientProxy* client, WebRtcEndpoint* webrtc_endpoint); BluetoothRadio& bluetooth_radio_; BluetoothClassic& bluetooth_medium_; + Ble& ble_medium_; WifiLan& wifi_lan_medium_; mediums::WebRtc& webrtc_medium_; }; diff --git a/cpp/core_v2/internal/p2p_cluster_pcp_handler_test.cc b/cpp/core_v2/internal/p2p_cluster_pcp_handler_test.cc index 9d3ec83d..51bce6df 100644 --- a/cpp/core_v2/internal/p2p_cluster_pcp_handler_test.cc +++ b/cpp/core_v2/internal/p2p_cluster_pcp_handler_test.cc @@ -15,31 +15,57 @@ namespace nearby { namespace connections { namespace { -class P2pClusterPcpHandlerTest : public ::testing::Test { +constexpr BooleanMediumSelector kTestCases[] = { + BooleanMediumSelector{ + .bluetooth = true, + }, + BooleanMediumSelector{ + .wifi_lan = true, + }, + BooleanMediumSelector{ + .bluetooth = true, + .wifi_lan = true, + }, +}; + +class P2pClusterPcpHandlerTest + : public ::testing::TestWithParam { protected: void SetUp() override { NEARBY_LOG(INFO, "SetUp: begin"); env_.Stop(); + if (options_.allowed.bluetooth) { + NEARBY_LOG(INFO, "SetUp: BT enabled"); + } + if (options_.allowed.wifi_lan) { + NEARBY_LOG(INFO, "SetUp: Wifi LAN enabled"); + } + if (options_.allowed.web_rtc) { + NEARBY_LOG(INFO, "SetUp: WebRTC enabled"); + } NEARBY_LOG(INFO, "SetUp: end"); } ClientProxy client_a_; ClientProxy client_b_; std::string service_id_{"service"}; - ConnectionOptions options_{.strategy = Strategy::kP2pCluster}; + ConnectionOptions options_{ + .strategy = Strategy::kP2pCluster, + .allowed = GetParam(), + }; MediumEnvironment& env_{MediumEnvironment::Instance()}; }; -TEST_F(P2pClusterPcpHandlerTest, CanConstructOne) { +TEST_P(P2pClusterPcpHandlerTest, CanConstructOne) { env_.Start(); Mediums mediums; EndpointChannelManager ecm; EndpointManager em(&ecm); - P2pClusterPcpHandler handler(mediums, &em, &ecm); + P2pClusterPcpHandler handler(&mediums, &em, &ecm); env_.Stop(); } -TEST_F(P2pClusterPcpHandlerTest, CanConstructMultiple) { +TEST_P(P2pClusterPcpHandlerTest, CanConstructMultiple) { env_.Start(); Mediums mediums_a; Mediums mediums_b; @@ -47,25 +73,26 @@ TEST_F(P2pClusterPcpHandlerTest, CanConstructMultiple) { EndpointChannelManager ecm_b; EndpointManager em_a(&ecm_a); EndpointManager em_b(&ecm_b); - P2pClusterPcpHandler handler_a(mediums_a, &em_a, &ecm_a); - P2pClusterPcpHandler handler_b(mediums_b, &em_b, &ecm_b); + P2pClusterPcpHandler handler_a(&mediums_a, &em_a, &ecm_a); + P2pClusterPcpHandler handler_b(&mediums_b, &em_b, &ecm_b); env_.Stop(); } -TEST_F(P2pClusterPcpHandlerTest, CanAdvertise) { +TEST_P(P2pClusterPcpHandlerTest, CanAdvertise) { env_.Start(); std::string endpoint_name{"endpoint_name"}; Mediums mediums_a; EndpointChannelManager ecm_a; EndpointManager em_a(&ecm_a); - P2pClusterPcpHandler handler_a(mediums_a, &em_a, &ecm_a); - EXPECT_EQ(handler_a.StartAdvertising(&client_a_, service_id_, options_, - {.name = endpoint_name}), - Status{Status::kSuccess}); + P2pClusterPcpHandler handler_a(&mediums_a, &em_a, &ecm_a); + EXPECT_EQ( + handler_a.StartAdvertising(&client_a_, service_id_, options_, + {.endpoint_info = ByteArray{endpoint_name}}), + Status{Status::kSuccess}); env_.Stop(); } -TEST_F(P2pClusterPcpHandlerTest, CanDiscover) { +TEST_P(P2pClusterPcpHandlerTest, CanDiscover) { env_.Start(); std::string endpoint_name{"endpoint_name"}; Mediums mediums_a; @@ -74,18 +101,19 @@ TEST_F(P2pClusterPcpHandlerTest, CanDiscover) { EndpointChannelManager ecm_b; EndpointManager em_a(&ecm_a); EndpointManager em_b(&ecm_b); - P2pClusterPcpHandler handler_a(mediums_a, &em_a, &ecm_a); - P2pClusterPcpHandler handler_b(mediums_b, &em_b, &ecm_b); + P2pClusterPcpHandler handler_a(&mediums_a, &em_a, &ecm_a); + P2pClusterPcpHandler handler_b(&mediums_b, &em_b, &ecm_b); CountDownLatch latch(1); - EXPECT_EQ(handler_a.StartAdvertising(&client_a_, service_id_, options_, - {.name = endpoint_name}), - Status{Status::kSuccess}); + EXPECT_EQ( + handler_a.StartAdvertising(&client_a_, service_id_, options_, + {.endpoint_info = ByteArray{endpoint_name}}), + Status{Status::kSuccess}); EXPECT_EQ(handler_b.StartDiscovery( &client_b_, service_id_, options_, { .endpoint_found_cb = [&latch](const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& service_id) { NEARBY_LOG(INFO, "Device discovered: id=%s", endpoint_id.c_str()); @@ -94,10 +122,13 @@ TEST_F(P2pClusterPcpHandlerTest, CanDiscover) { }), Status{Status::kSuccess}); EXPECT_TRUE(latch.Await(absl::Milliseconds(1000)).result()); + // We discovered endpoint over one medium. Before we finish the test, we have + // to stop discovery for other mediums that may be still ongoing. + handler_b.StopDiscovery(&client_b_); env_.Stop(); } -TEST_F(P2pClusterPcpHandlerTest, CanConnect) { +TEST_P(P2pClusterPcpHandlerTest, CanConnect) { env_.Start(); std::string endpoint_name_a{"endpoint_name"}; Mediums mediums_a; @@ -110,20 +141,20 @@ TEST_F(P2pClusterPcpHandlerTest, CanConnect) { EndpointChannelManager ecm_b; EndpointManager em_a(&ecm_a); EndpointManager em_b(&ecm_b); - P2pClusterPcpHandler handler_a(mediums_a, &em_a, &ecm_a); - P2pClusterPcpHandler handler_b(mediums_b, &em_b, &ecm_b); + P2pClusterPcpHandler handler_a(&mediums_a, &em_a, &ecm_a); + P2pClusterPcpHandler handler_b(&mediums_b, &em_b, &ecm_b); CountDownLatch discover_latch(1); CountDownLatch connect_latch(2); struct DiscoveredInfo { std::string endpoint_id; - std::string endpoint_name; + ByteArray endpoint_info; std::string service_id; } discovered; EXPECT_EQ( handler_a.StartAdvertising( &client_a_, service_id_, options_, { - .name = endpoint_name_a, + .endpoint_info = ByteArray{endpoint_name_a}, .listener = { .initiated_cb = @@ -142,13 +173,13 @@ TEST_F(P2pClusterPcpHandlerTest, CanConnect) { .endpoint_found_cb = [&discover_latch, &discovered]( const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& service_id) { NEARBY_LOG(INFO, "Device discovered: id=%s", endpoint_id.c_str()); discovered = { .endpoint_id = endpoint_id, - .endpoint_name = endpoint_name, + .endpoint_info = endpoint_info, .service_id = service_id, }; discover_latch.CountDown(); @@ -157,12 +188,12 @@ TEST_F(P2pClusterPcpHandlerTest, CanConnect) { Status{Status::kSuccess}); EXPECT_TRUE(discover_latch.Await(absl::Milliseconds(1000)).result()); - EXPECT_EQ(endpoint_name_a, discovered.endpoint_name); + EXPECT_EQ(endpoint_name_a, std::string{discovered.endpoint_info}); handler_b.RequestConnection( &client_b_, discovered.endpoint_id, { - .name = discovered.endpoint_name, + .endpoint_info = discovered.endpoint_info, .listener = { .initiated_cb = @@ -173,11 +204,15 @@ TEST_F(P2pClusterPcpHandlerTest, CanConnect) { connect_latch.CountDown(); }, }, - }); + }, + options_); EXPECT_TRUE(connect_latch.Await(absl::Milliseconds(1000)).result()); env_.Stop(); } +INSTANTIATE_TEST_SUITE_P(ParametrisedPcpHandlerTest, P2pClusterPcpHandlerTest, + ::testing::ValuesIn(kTestCases)); + } // namespace } // namespace connections } // namespace nearby diff --git a/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.cc b/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.cc index 60da6883..c3525bdd 100644 --- a/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.cc +++ b/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.cc @@ -7,8 +7,7 @@ namespace connections { P2pPointToPointPcpHandler::P2pPointToPointPcpHandler( Mediums& mediums, EndpointManager& endpoint_manager, EndpointChannelManager& channel_manager, Pcp pcp) - : P2pStarPcpHandler(mediums, endpoint_manager, channel_manager, pcp), - mediums_(&mediums) {} + : P2pStarPcpHandler(mediums, endpoint_manager, channel_manager, pcp) {} std::vector P2pPointToPointPcpHandler::GetConnectionMediumsByPriority() { diff --git a/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.h b/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.h index e6da2dd9..cd9cb39b 100644 --- a/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.h +++ b/cpp/core_v2/internal/p2p_point_to_point_pcp_handler.h @@ -3,7 +3,6 @@ #include "core_v2/internal/endpoint_channel_manager.h" #include "core_v2/internal/endpoint_manager.h" -#include "core_v2/internal/mediums/mediums.h" #include "core_v2/internal/p2p_star_pcp_handler.h" #include "core_v2/internal/pcp.h" #include "core_v2/strategy.h" @@ -15,7 +14,7 @@ namespace connections { // Concrete implementation of the PCPHandler for the P2P_POINT_TO_POINT. This // PCP is for mediums that have limitations on the number of simultaneous // connections; all mediums in P2P_STAR are valid for P2P_POINT_TO_POINT, but -// not all mediums in P2P_POINT_TO_POINT and valid for P2P_STAR. +// not all mediums in P2P_POINT_TO_POINT are valid for P2P_STAR. // // Currently, this implementation advertises/discovers over Bluetooth // and connects over Bluetooth. @@ -31,9 +30,6 @@ class P2pPointToPointPcpHandler : public P2pStarPcpHandler { bool CanSendOutgoingConnection(ClientProxy* client) const override; bool CanReceiveIncomingConnection(ClientProxy* client) const override; - - private: - Mediums* mediums_; }; } // namespace connections diff --git a/cpp/core_v2/internal/p2p_star_pcp_handler.cc b/cpp/core_v2/internal/p2p_star_pcp_handler.cc index 25901ebc..acb45e38 100644 --- a/cpp/core_v2/internal/p2p_star_pcp_handler.cc +++ b/cpp/core_v2/internal/p2p_star_pcp_handler.cc @@ -10,8 +10,8 @@ P2pStarPcpHandler::P2pStarPcpHandler(Mediums& mediums, EndpointManager& endpoint_manager, EndpointChannelManager& channel_manager, Pcp pcp) - : P2pClusterPcpHandler(mediums, &endpoint_manager, &channel_manager, pcp), - mediums_(&mediums) {} + : P2pClusterPcpHandler(&mediums, &endpoint_manager, &channel_manager, pcp) { +} std::vector P2pStarPcpHandler::GetConnectionMediumsByPriority() { diff --git a/cpp/core_v2/internal/p2p_star_pcp_handler.h b/cpp/core_v2/internal/p2p_star_pcp_handler.h index a50bd054..203bfcf5 100644 --- a/cpp/core_v2/internal/p2p_star_pcp_handler.h +++ b/cpp/core_v2/internal/p2p_star_pcp_handler.h @@ -6,7 +6,6 @@ #include "core_v2/internal/client_proxy.h" #include "core_v2/internal/endpoint_channel_manager.h" #include "core_v2/internal/endpoint_manager.h" -#include "core_v2/internal/mediums/mediums.h" #include "core_v2/internal/p2p_cluster_pcp_handler.h" #include "core_v2/internal/pcp.h" #include "core_v2/strategy.h" @@ -17,7 +16,7 @@ namespace connections { // Concrete implementation of the PcpHandler for the P2P_STAR PCP. This Pcp is // for mediums that have one server with (potentially) many clients; all mediums -// in P2P_CLUSTER are valid for P2P_STAR, but not all mediums in P2P_STAR and +// in P2P_CLUSTER are valid for P2P_STAR, but not all mediums in P2P_STAR are // valid for P2P_CLUSTER. // // Currently, this implementation advertises/discovers over Bluetooth @@ -35,9 +34,6 @@ class P2pStarPcpHandler : public P2pClusterPcpHandler { bool CanSendOutgoingConnection(ClientProxy* client) const override; bool CanReceiveIncomingConnection(ClientProxy* client) const override; - - private: - Mediums* mediums_; }; } // namespace connections diff --git a/cpp/core_v2/internal/payload_manager_test.cc b/cpp/core_v2/internal/payload_manager_test.cc index e88a050c..bf843881 100644 --- a/cpp/core_v2/internal/payload_manager_test.cc +++ b/cpp/core_v2/internal/payload_manager_test.cc @@ -20,12 +20,27 @@ constexpr absl::string_view kMessage = "message"; constexpr absl::Duration kProgressTimeout = absl::Milliseconds(1000); constexpr absl::Duration kDefaultTimeout = absl::Milliseconds(1000); +constexpr BooleanMediumSelector kTestCases[] = { + BooleanMediumSelector{ + .bluetooth = true, + }, + BooleanMediumSelector{ + .wifi_lan = true, + }, + BooleanMediumSelector{ + .bluetooth = true, + .wifi_lan = true, + }, +}; + class PayloadSimulationUser : public SimulationUser { public: - explicit PayloadSimulationUser(absl::string_view name) - : SimulationUser(std::string(name)) {} + explicit PayloadSimulationUser( + absl::string_view name, + BooleanMediumSelector allowed = BooleanMediumSelector()) + : SimulationUser(std::string(name), allowed) {} ~PayloadSimulationUser() override { - NEARBY_LOGS(INFO) << "PayloadSimulationUser: [down] name=" << name_; + NEARBY_LOGS(INFO) << "PayloadSimulationUser: [down] name=" << info_.data(); // SystemClock::Sleep(kDefaultTimeout); } @@ -51,7 +66,8 @@ class PayloadSimulationUser : public SimulationUser { Payload::Id sender_payload_id_ = 0; }; -class PayloadManagerTest : public ::testing::Test { +class PayloadManagerTest + : public ::testing::TestWithParam { protected: PayloadManagerTest() { env_.Stop(); } @@ -61,7 +77,7 @@ class PayloadManagerTest : public ::testing::Test { user_b.StartDiscovery(std::string(kServiceId), &discovery_latch_); EXPECT_TRUE(discovery_latch_.Await(kDefaultTimeout).result()); EXPECT_EQ(user_b.GetDiscovered().service_id, kServiceId); - EXPECT_EQ(user_b.GetDiscovered().endpoint_name, user_a.GetName()); + EXPECT_EQ(user_b.GetDiscovered().endpoint_info, user_a.GetInfo()); EXPECT_FALSE(user_b.GetDiscovered().endpoint_id.empty()); NEARBY_LOG(INFO, "EP-B: [discovered] %s", user_b.GetDiscovered().endpoint_id.c_str()); @@ -85,23 +101,23 @@ class PayloadManagerTest : public ::testing::Test { MediumEnvironment& env_{MediumEnvironment::Instance()}; }; -TEST_F(PayloadManagerTest, CanCreateOne) { +TEST_P(PayloadManagerTest, CanCreateOne) { env_.Start(); - PayloadSimulationUser user_a(kDeviceA); + PayloadSimulationUser user_a(kDeviceA, GetParam()); env_.Stop(); } -TEST_F(PayloadManagerTest, CanCreateMultiple) { +TEST_P(PayloadManagerTest, CanCreateMultiple) { env_.Start(); - PayloadSimulationUser user_a(kDeviceA); - PayloadSimulationUser user_b(kDeviceB); + PayloadSimulationUser user_a(kDeviceA, GetParam()); + PayloadSimulationUser user_b(kDeviceB, GetParam()); env_.Stop(); } -TEST_F(PayloadManagerTest, CanSendBytePayload) { +TEST_P(PayloadManagerTest, CanSendBytePayload) { env_.Start(); - PayloadSimulationUser user_a(kDeviceA); - PayloadSimulationUser user_b(kDeviceB); + PayloadSimulationUser user_a(kDeviceA, GetParam()); + PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); user_a.ExpectPayload(payload_latch_); @@ -115,10 +131,10 @@ TEST_F(PayloadManagerTest, CanSendBytePayload) { env_.Stop(); } -TEST_F(PayloadManagerTest, CanSendStreamPayload) { +TEST_P(PayloadManagerTest, CanSendStreamPayload) { env_.Start(); - PayloadSimulationUser user_a(kDeviceA); - PayloadSimulationUser user_b(kDeviceB); + PayloadSimulationUser user_a(kDeviceA, GetParam()); + PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); auto pipe = std::make_shared(); @@ -165,10 +181,10 @@ TEST_F(PayloadManagerTest, CanSendStreamPayload) { env_.Stop(); } -TEST_F(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { +TEST_P(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { env_.Start(); - PayloadSimulationUser user_a(kDeviceA); - PayloadSimulationUser user_b(kDeviceB); + PayloadSimulationUser user_a(kDeviceA, GetParam()); + PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); auto pipe = std::make_shared(); @@ -212,7 +228,7 @@ TEST_F(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { [status = PayloadProgressInfo::Status::kCanceled]( const PayloadProgressInfo& info) { return info.status == status; }, kProgressTimeout)); - NEARBY_LOG(INFO, "Stream cancelation recevied."); + NEARBY_LOG(INFO, "Stream cancelation received."); tx.Close(); rx.Close(); @@ -223,10 +239,10 @@ TEST_F(PayloadManagerTest, CanCancelPayloadOnReceiverSide) { env_.Stop(); } -TEST_F(PayloadManagerTest, CanCancelPayloadOnSenderSide) { +TEST_P(PayloadManagerTest, CanCancelPayloadOnSenderSide) { env_.Start(); - PayloadSimulationUser user_a(kDeviceA); - PayloadSimulationUser user_b(kDeviceB); + PayloadSimulationUser user_a(kDeviceA, GetParam()); + PayloadSimulationUser user_b(kDeviceB, GetParam()); ASSERT_TRUE(SetupConnection(user_a, user_b)); auto pipe = std::make_shared(); @@ -270,7 +286,7 @@ TEST_F(PayloadManagerTest, CanCancelPayloadOnSenderSide) { [status = PayloadProgressInfo::Status::kCanceled]( const PayloadProgressInfo& info) { return info.status == status; }, kProgressTimeout)); - NEARBY_LOG(INFO, "Stream cancelation recevied."); + NEARBY_LOG(INFO, "Stream cancelation received."); tx.Close(); rx.Close(); @@ -281,6 +297,9 @@ TEST_F(PayloadManagerTest, CanCancelPayloadOnSenderSide) { env_.Stop(); } +INSTANTIATE_TEST_SUITE_P(ParametrisedPayloadManagerTest, PayloadManagerTest, + ::testing::ValuesIn(kTestCases)); + } // namespace } // namespace connections } // namespace nearby diff --git a/cpp/core_v2/internal/pcp_handler.h b/cpp/core_v2/internal/pcp_handler.h index cb181dd9..8997f8a7 100644 --- a/cpp/core_v2/internal/pcp_handler.h +++ b/cpp/core_v2/internal/pcp_handler.h @@ -79,12 +79,13 @@ class PcpHandler { // connection, update state on ClientProxy. virtual Status RequestConnection(ClientProxy* client, const std::string& endpoint_id, - const ConnectionRequestInfo& info) = 0; + const ConnectionRequestInfo& info, + const ConnectionOptions& options) = 0; // Either party may call this to accept connection on their part. // Until both parties call it, connection will not reach a data phase. // Update state in ClientProxy. - virtual Status AcceptConnection(ClientProxy* clientProxy, + virtual Status AcceptConnection(ClientProxy* client, const std::string& endpoint_id, const PayloadListener& payload_listener) = 0; diff --git a/cpp/core_v2/internal/pcp_manager.cc b/cpp/core_v2/internal/pcp_manager.cc index b6f071c0..c3c62aee 100644 --- a/cpp/core_v2/internal/pcp_manager.cc +++ b/cpp/core_v2/internal/pcp_manager.cc @@ -13,7 +13,7 @@ PcpManager::PcpManager(Mediums& mediums, EndpointChannelManager& channel_manager, EndpointManager& endpoint_manager) { handlers_[Pcp::kP2pCluster] = std::make_unique( - mediums, &endpoint_manager, &channel_manager); + &mediums, &endpoint_manager, &channel_manager); handlers_[Pcp::kP2pStar] = std::make_unique( mediums, endpoint_manager, channel_manager); handlers_[Pcp::kP2pPointToPoint] = @@ -69,12 +69,13 @@ void PcpManager::StopDiscovery(ClientProxy* client) { Status PcpManager::RequestConnection(ClientProxy* client, const string& endpoint_id, - const ConnectionRequestInfo& info) { + const ConnectionRequestInfo& info, + const ConnectionOptions& options) { if (!current_) { return {Status::kOutOfOrderApiCall}; } - return current_->RequestConnection(client, endpoint_id, info); + return current_->RequestConnection(client, endpoint_id, info, options); } Status PcpManager::AcceptConnection(ClientProxy* client, diff --git a/cpp/core_v2/internal/pcp_manager.h b/cpp/core_v2/internal/pcp_manager.h index 68228b38..ddeb4107 100644 --- a/cpp/core_v2/internal/pcp_manager.h +++ b/cpp/core_v2/internal/pcp_manager.h @@ -32,21 +32,22 @@ class PcpManager { EndpointManager& endpoint_manager); ~PcpManager(); - Status StartAdvertising(ClientProxy* client_proxy, const string& service_id, + Status StartAdvertising(ClientProxy* client, const string& service_id, const ConnectionOptions& options, const ConnectionRequestInfo& info); - void StopAdvertising(ClientProxy* client_proxy); + void StopAdvertising(ClientProxy* client); - Status StartDiscovery(ClientProxy* client_proxy, const string& service_id, + Status StartDiscovery(ClientProxy* client, const string& service_id, const ConnectionOptions& options, DiscoveryListener listener); - void StopDiscovery(ClientProxy* client_proxy); + void StopDiscovery(ClientProxy* client); - Status RequestConnection(ClientProxy* client_proxy, const string& endpoint_id, - const ConnectionRequestInfo& info); - Status AcceptConnection(ClientProxy* client_proxy, const string& endpoint_id, + Status RequestConnection(ClientProxy* client, const string& endpoint_id, + const ConnectionRequestInfo& info, + const ConnectionOptions& options); + Status AcceptConnection(ClientProxy* client, const string& endpoint_id, const PayloadListener& payload_listener); - Status RejectConnection(ClientProxy* client_proxy, const string& endpoint_id); + Status RejectConnection(ClientProxy* client, const string& endpoint_id); proto::connections::Medium GetBandwidthUpgradeMedium(); void DisconnectFromEndpointManager(); diff --git a/cpp/core_v2/internal/pcp_manager_test.cc b/cpp/core_v2/internal/pcp_manager_test.cc index a2404719..ec261f8d 100644 --- a/cpp/core_v2/internal/pcp_manager_test.cc +++ b/cpp/core_v2/internal/pcp_manager_test.cc @@ -4,6 +4,7 @@ #include "core_v2/internal/endpoint_channel_manager.h" #include "core_v2/internal/simulation_user.h" +#include "core_v2/options.h" #include "platform_v2/base/medium_environment.h" #include "platform_v2/public/count_down_latch.h" #include "gmock/gmock.h" @@ -19,58 +20,71 @@ constexpr char kServiceId[] = "service-id"; constexpr char kDeviceA[] = "device-A"; constexpr char kDeviceB[] = "device-B"; -class PcpManagerTest : public ::testing::Test { +constexpr BooleanMediumSelector kTestCases[] = { + BooleanMediumSelector{ + .bluetooth = true, + }, + BooleanMediumSelector{ + .wifi_lan = true, + }, + BooleanMediumSelector{ + .bluetooth = true, + .wifi_lan = true, + }, +}; + +class PcpManagerTest : public ::testing::TestWithParam { protected: PcpManagerTest() { env_.Stop(); } MediumEnvironment& env_{MediumEnvironment::Instance()}; }; -TEST_F(PcpManagerTest, CanCreateOne) { +TEST_P(PcpManagerTest, CanCreateOne) { env_.Start(); - SimulationUser user(kDeviceA); + SimulationUser user(kDeviceA, GetParam()); env_.Stop(); } -TEST_F(PcpManagerTest, CanCreateMany) { +TEST_P(PcpManagerTest, CanCreateMany) { env_.Start(); - SimulationUser user_a(kDeviceA); - SimulationUser user_b(kDeviceB); + SimulationUser user_a(kDeviceA, GetParam()); + SimulationUser user_b(kDeviceB, GetParam()); env_.Stop(); } -TEST_F(PcpManagerTest, CanAdvertise) { +TEST_P(PcpManagerTest, CanAdvertise) { env_.Start(); - SimulationUser user_a(kDeviceA); - SimulationUser user_b(kDeviceB); + SimulationUser user_a(kDeviceA, GetParam()); + SimulationUser user_b(kDeviceB, GetParam()); user_a.StartAdvertising(kServiceId, nullptr); env_.Stop(); } -TEST_F(PcpManagerTest, CanDiscover) { +TEST_P(PcpManagerTest, CanDiscover) { env_.Start(); - SimulationUser user_a("device-a"); - SimulationUser user_b("device-b"); + SimulationUser user_a("device-a", GetParam()); + SimulationUser user_b("device-b", GetParam()); user_a.StartAdvertising(kServiceId, nullptr); CountDownLatch latch(1); user_b.StartDiscovery(kServiceId, &latch); EXPECT_TRUE(latch.Await(absl::Milliseconds(1000)).result()); EXPECT_EQ(user_b.GetDiscovered().service_id, kServiceId); - EXPECT_EQ(user_b.GetDiscovered().endpoint_name, user_a.GetName()); + EXPECT_EQ(user_b.GetDiscovered().endpoint_info, user_a.GetInfo()); env_.Stop(); } -TEST_F(PcpManagerTest, CanConnect) { +TEST_P(PcpManagerTest, CanConnect) { env_.Start(); - SimulationUser user_a("device-a"); - SimulationUser user_b("device-b"); + SimulationUser user_a("device-a", GetParam()); + SimulationUser user_b("device-b", GetParam()); CountDownLatch discovery_latch(1); CountDownLatch connection_latch(2); user_a.StartAdvertising(kServiceId, &connection_latch); user_b.StartDiscovery(kServiceId, &discovery_latch); EXPECT_TRUE(discovery_latch.Await(absl::Milliseconds(1000)).result()); EXPECT_EQ(user_b.GetDiscovered().service_id, kServiceId); - EXPECT_EQ(user_b.GetDiscovered().endpoint_name, user_a.GetName()); + EXPECT_EQ(user_b.GetDiscovered().endpoint_info, user_a.GetInfo()); user_b.RequestConnection(&connection_latch); EXPECT_TRUE(connection_latch.Await(absl::Milliseconds(1000)).result()); user_a.Stop(); @@ -78,10 +92,10 @@ TEST_F(PcpManagerTest, CanConnect) { env_.Stop(); } -TEST_F(PcpManagerTest, CanAccept) { +TEST_P(PcpManagerTest, CanAccept) { env_.Start(); - SimulationUser user_a("device-a"); - SimulationUser user_b("device-b"); + SimulationUser user_a("device-a", GetParam()); + SimulationUser user_b("device-b", GetParam()); CountDownLatch discovery_latch(1); CountDownLatch connection_latch(2); CountDownLatch accept_latch(2); @@ -89,7 +103,7 @@ TEST_F(PcpManagerTest, CanAccept) { user_b.StartDiscovery(kServiceId, &discovery_latch); EXPECT_TRUE(discovery_latch.Await(absl::Milliseconds(1000)).result()); EXPECT_EQ(user_b.GetDiscovered().service_id, kServiceId); - EXPECT_EQ(user_b.GetDiscovered().endpoint_name, user_a.GetName()); + EXPECT_EQ(user_b.GetDiscovered().endpoint_info, user_a.GetInfo()); user_b.RequestConnection(&connection_latch); EXPECT_TRUE(connection_latch.Await(absl::Milliseconds(1000)).result()); user_a.AcceptConnection(&accept_latch); @@ -100,10 +114,10 @@ TEST_F(PcpManagerTest, CanAccept) { env_.Stop(); } -TEST_F(PcpManagerTest, CanReject) { +TEST_P(PcpManagerTest, CanReject) { env_.Start(); - SimulationUser user_a("device-a"); - SimulationUser user_b("device-b"); + SimulationUser user_a("device-a", GetParam()); + SimulationUser user_b("device-b", GetParam()); CountDownLatch discovery_latch(1); CountDownLatch connection_latch(2); CountDownLatch reject_latch(1); @@ -111,7 +125,7 @@ TEST_F(PcpManagerTest, CanReject) { user_b.StartDiscovery(kServiceId, &discovery_latch); EXPECT_TRUE(discovery_latch.Await(absl::Milliseconds(1000)).result()); EXPECT_EQ(user_b.GetDiscovered().service_id, kServiceId); - EXPECT_EQ(user_b.GetDiscovered().endpoint_name, user_a.GetName()); + EXPECT_EQ(user_b.GetDiscovered().endpoint_info, user_a.GetInfo()); user_b.RequestConnection(&connection_latch); EXPECT_TRUE(connection_latch.Await(absl::Milliseconds(1000)).result()); user_b.ExpectRejectedConnection(reject_latch); @@ -122,6 +136,9 @@ TEST_F(PcpManagerTest, CanReject) { env_.Stop(); } +INSTANTIATE_TEST_SUITE_P(ParametrisedPcpManagerTest, PcpManagerTest, + ::testing::ValuesIn(kTestCases)); + } // namespace } // namespace connections } // namespace nearby diff --git a/cpp/core_v2/internal/service_controller.h b/cpp/core_v2/internal/service_controller.h index 0b6e8c60..ce186949 100644 --- a/cpp/core_v2/internal/service_controller.h +++ b/cpp/core_v2/internal/service_controller.h @@ -49,7 +49,8 @@ class ServiceController { virtual Status RequestConnection(ClientProxy* client, const std::string& endpoint_id, - const ConnectionRequestInfo& info) = 0; + const ConnectionRequestInfo& info, + const ConnectionOptions& options) = 0; virtual Status AcceptConnection(ClientProxy* client, const std::string& endpoint_id, const PayloadListener& listener) = 0; @@ -63,8 +64,7 @@ class ServiceController { const std::vector& endpoint_ids, Payload payload) = 0; - virtual Status CancelPayload(ClientProxy* client, - Payload::Id payload_id) = 0; + virtual Status CancelPayload(ClientProxy* client, Payload::Id payload_id) = 0; virtual void DisconnectFromEndpoint(ClientProxy* client, const std::string& endpoint_id) = 0; diff --git a/cpp/core_v2/internal/service_controller_router.cc b/cpp/core_v2/internal/service_controller_router.cc index 17ef83c2..e5ce4496 100644 --- a/cpp/core_v2/internal/service_controller_router.cc +++ b/cpp/core_v2/internal/service_controller_router.cc @@ -92,23 +92,25 @@ void ServiceControllerRouter::StopDiscovery(ClientProxy* client, void ServiceControllerRouter::RequestConnection( ClientProxy* client, absl::string_view endpoint_id, - const ConnectionRequestInfo& info, const ResultCallback& callback) { - RouteToServiceController( - [this, client, endpoint_id = std::string(endpoint_id), info, callback]() { - if (!ClientHasAcquiredServiceController(client)) { - callback.result_cb({Status::kOutOfOrderApiCall}); - return; - } + const ConnectionRequestInfo& info, const ConnectionOptions& options, + const ResultCallback& callback) { + RouteToServiceController([this, client, + endpoint_id = std::string(endpoint_id), info, + options, callback]() { + if (!ClientHasAcquiredServiceController(client)) { + callback.result_cb({Status::kOutOfOrderApiCall}); + return; + } - if (client->HasPendingConnectionToEndpoint(endpoint_id) || - client->IsConnectedToEndpoint(endpoint_id)) { - callback.result_cb({Status::kAlreadyConnectedToEndpoint}); - return; - } + if (client->HasPendingConnectionToEndpoint(endpoint_id) || + client->IsConnectedToEndpoint(endpoint_id)) { + callback.result_cb({Status::kAlreadyConnectedToEndpoint}); + return; + } - callback.result_cb( - service_controller_->RequestConnection(client, endpoint_id, info)); - }); + callback.result_cb(service_controller_->RequestConnection( + client, endpoint_id, info, options)); + }); } void ServiceControllerRouter::AcceptConnection(ClientProxy* client, @@ -204,7 +206,7 @@ void ServiceControllerRouter::SendPayload( std::vector(endpoint_ids.begin(), endpoint_ids.end()); RouteToServiceController( - [this, client, shared_payload, endpoints, &callback]() { + [this, client, shared_payload, endpoints, callback]() { if (!ClientHasAcquiredServiceController(client)) { callback.result_cb({Status::kOutOfOrderApiCall}); return; diff --git a/cpp/core_v2/internal/service_controller_router.h b/cpp/core_v2/internal/service_controller_router.h index 8ccfd057..70e9742a 100644 --- a/cpp/core_v2/internal/service_controller_router.h +++ b/cpp/core_v2/internal/service_controller_router.h @@ -59,6 +59,7 @@ class ServiceControllerRouter { void RequestConnection(ClientProxy* client, absl::string_view endpoint_id, const ConnectionRequestInfo& info, + const ConnectionOptions& options, const ResultCallback& callback); void AcceptConnection(ClientProxy* client, absl::string_view endpoint_id, const PayloadListener& listener, diff --git a/cpp/core_v2/internal/service_controller_router_test.cc b/cpp/core_v2/internal/service_controller_router_test.cc index 2fc45d00..0f34225d 100644 --- a/cpp/core_v2/internal/service_controller_router_test.cc +++ b/cpp/core_v2/internal/service_controller_router_test.cc @@ -101,20 +101,22 @@ class ServiceControllerRouterTest : public testing::Test { ResultCallback callback) { EXPECT_CALL(mock_, RequestConnection) .WillOnce(Return(Status{Status::kSuccess})); + ConnectionOptions options; { MutexLock lock(&mutex_); complete_ = false; - router_.RequestConnection(client, endpoint_id, request_info, callback); + router_.RequestConnection(client, endpoint_id, request_info, options, + callback); while (!complete_) cond_.Wait(); EXPECT_EQ(result_, Status{Status::kSuccess}); } ConnectionResponseInfo response_info{ - .remote_endpoint_name = "endpoint_name", + .remote_endpoint_info = ByteArray{"endpoint_name"}, .authentication_token = "auth_token", - .raw_authentication_token = ByteArray("auth_token"), + .raw_authentication_token = ByteArray{"auth_token"}, .is_incoming_connection = true, }; - client->OnConnectionInitiated(endpoint_id, response_info, + client->OnConnectionInitiated(endpoint_id, response_info, options, request_info.listener); EXPECT_TRUE(client->HasPendingConnectionToEndpoint(endpoint_id)); } @@ -242,7 +244,7 @@ class ServiceControllerRouterTest : public testing::Test { std::vector mediums_{ proto::connections::Medium::BLUETOOTH}; const ConnectionRequestInfo kConnectionRequestInfo{ - .name = kRequestorName, + .endpoint_info = ByteArray{kRequestorName}, .listener = ConnectionListener(), }; diff --git a/cpp/core_v2/internal/simulation_user.cc b/cpp/core_v2/internal/simulation_user.cc index 54dac813..7c38f5e5 100644 --- a/cpp/core_v2/internal/simulation_user.cc +++ b/cpp/core_v2/internal/simulation_user.cc @@ -18,7 +18,7 @@ void SimulationUser::OnConnectionInitiated(const std::string& endpoint_id, NEARBY_LOG(INFO, "StartAdvertising: initiated_cb called"); discovered_ = DiscoveredInfo{ .endpoint_id = endpoint_id, - .endpoint_name = name_, + .endpoint_info = GetInfo(), .service_id = service_id_, }; } @@ -35,12 +35,12 @@ void SimulationUser::OnConnectionRejected(const std::string& endpoint_id, } void SimulationUser::OnEndpointFound(const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& service_id) { NEARBY_LOG(INFO, "Device discovered: id=%s", endpoint_id.c_str()); discovered_ = DiscoveredInfo{ .endpoint_id = endpoint_id, - .endpoint_name = endpoint_name, + .endpoint_info = endpoint_info, .service_id = service_id, }; if (found_latch_) found_latch_->CountDown(); @@ -97,7 +97,7 @@ void SimulationUser::StartAdvertising(const std::string& service_id, }; EXPECT_TRUE(mgr_.StartAdvertising(&client_, service_id_, options_, { - .name = name_, + .endpoint_info = info_, .listener = std::move(listener), }) .Ok()); @@ -128,12 +128,14 @@ void SimulationUser::RequestConnection(CountDownLatch* latch) { .rejected_cb = absl::bind_front(&SimulationUser::OnConnectionRejected, this), }; - EXPECT_TRUE(mgr_.RequestConnection(&client_, discovered_.endpoint_id, - { - .name = discovered_.endpoint_name, - .listener = std::move(listener), - }) - .Ok()); + EXPECT_TRUE( + mgr_.RequestConnection(&client_, discovered_.endpoint_id, + { + .endpoint_info = discovered_.endpoint_info, + .listener = std::move(listener), + }, + connection_options_) + .Ok()); } void SimulationUser::AcceptConnection(CountDownLatch* latch) { diff --git a/cpp/core_v2/internal/simulation_user.h b/cpp/core_v2/internal/simulation_user.h index 6d24929c..4674be0d 100644 --- a/cpp/core_v2/internal/simulation_user.h +++ b/cpp/core_v2/internal/simulation_user.h @@ -8,6 +8,7 @@ #include "core_v2/internal/endpoint_manager.h" #include "core_v2/internal/payload_manager.h" #include "core_v2/internal/pcp_manager.h" +#include "core_v2/options.h" #include "platform_v2/base/medium_environment.h" #include "platform_v2/public/condition_variable.h" #include "platform_v2/public/count_down_latch.h" @@ -27,18 +28,22 @@ class SimulationUser { public: struct DiscoveredInfo { std::string endpoint_id; - std::string endpoint_name; + ByteArray endpoint_info; std::string service_id; bool Empty() const { return endpoint_id.empty(); } void Clear() { endpoint_id.clear(); } }; - explicit SimulationUser(const std::string& device_name) - : name_(device_name) {} - virtual ~SimulationUser() { - Stop(); - } + explicit SimulationUser( + const std::string& device_name, + BooleanMediumSelector allowed = BooleanMediumSelector()) + : info_{ByteArray{device_name}}, + options_{ + .strategy = Strategy::kP2pCluster, + .allowed = allowed, + } {} + virtual ~SimulationUser() { Stop(); } void Stop() { pm_.DisconnectFromEndpointManager(); mgr_.DisconnectFromEndpointManager(); @@ -80,7 +85,7 @@ class SimulationUser { void ExpectPayload(CountDownLatch& latch) { payload_latch_ = &latch; } const DiscoveredInfo& GetDiscovered() const { return discovered_; } - std::string GetName() const { return name_; } + ByteArray GetInfo() const { return info_; } bool WaitForProgress(std::function pred, absl::Duration timeout); @@ -95,7 +100,7 @@ class SimulationUser { // DiscoveryListener callbacks void OnEndpointFound(const std::string& endpoint_id, - const std::string& endpoint_name, + const ByteArray& endpoint_info, const std::string& service_id); void OnEndpointLost(const std::string& endpoint_id); @@ -106,6 +111,7 @@ class SimulationUser { std::string service_id_; DiscoveredInfo discovered_; + ConnectionOptions connection_options_; Mutex progress_mutex_; ConditionVariable progress_sync_{&progress_mutex_}; PayloadProgressInfo progress_info_; @@ -118,9 +124,9 @@ class SimulationUser { CountDownLatch* payload_latch_ = nullptr; Future* future_ = nullptr; std::function predicate_; - std::string name_; + ByteArray info_; Mediums mediums_; - ConnectionOptions options_{.strategy = Strategy::kP2pCluster}; + ConnectionOptions options_; ClientProxy client_; EndpointChannelManager ecm_; EndpointManager em_{&ecm_}; diff --git a/cpp/core_v2/internal/wifi_lan_service_info.cc b/cpp/core_v2/internal/wifi_lan_service_info.cc index 92496867..566cf40e 100644 --- a/cpp/core_v2/internal/wifi_lan_service_info.cc +++ b/cpp/core_v2/internal/wifi_lan_service_info.cc @@ -17,7 +17,7 @@ namespace connections { WifiLanServiceInfo::WifiLanServiceInfo(Version version, Pcp pcp, absl::string_view endpoint_id, const ByteArray& service_id_hash, - absl::string_view endpoint_name) { + const ByteArray& endpoint_info) { if (version != Version::kV1 || endpoint_id.empty() || endpoint_id.length() != kEndpointIdLength || service_id_hash.size() != kServiceIdHashLength) { @@ -36,7 +36,7 @@ WifiLanServiceInfo::WifiLanServiceInfo(Version version, Pcp pcp, pcp_ = pcp; service_id_hash_ = service_id_hash; endpoint_id_ = std::string(endpoint_id); - endpoint_name_ = std::string(endpoint_name); + endpoint_info_ = endpoint_info; } WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { @@ -66,11 +66,11 @@ WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { return; } - if (service_info_bytes.size() > kMaxEndpointNameLength) { + if (service_info_bytes.size() > kMaxEndpointInfoLength) { NEARBY_LOG(INFO, "Cannot deserialize WifiLanServiceInfo: expecting max %d raw " "bytes, got %" PRIu64, - kMaxEndpointNameLength, service_info_bytes.size()); + kMaxEndpointInfoLength, service_info_bytes.size()); return; } @@ -105,24 +105,22 @@ WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { // The next 3 bytes are supposed to be the service_id_hash. service_id_hash_ = base_input_stream.ReadBytes(kServiceIdHashLength); - // The next 1 byte are supposed to be the length of the endpoint_name. - std::uint32_t expected_endpoint_name_length = base_input_stream.ReadUint8(); + // The next 1 byte are supposed to be the length of the endpoint_info. + std::uint32_t expected_endpoint_info_length = base_input_stream.ReadUint8(); - // The rest bytes are supposed to be the endpoint_name - auto endpoint_name_bytes = - base_input_stream.ReadBytes(expected_endpoint_name_length); - if (endpoint_name_bytes.Empty() || - endpoint_name_bytes.size() != expected_endpoint_name_length) { + // The rest bytes are supposed to be the endpoint_info + endpoint_info_ = base_input_stream.ReadBytes(expected_endpoint_info_length); + if (endpoint_info_.Empty() || + endpoint_info_.size() != expected_endpoint_info_length) { NEARBY_LOG(INFO, "Cannot deserialize WifiLanServiceInfo: expected " - "endpointName to be %d bytes, got %" PRIu64, - expected_endpoint_name_length, endpoint_name_bytes.size()); + "endpoint info to be %d bytes, got %" PRIu64, + expected_endpoint_info_length, endpoint_info_.size()); // Clear enpoint_id for validadity. endpoint_id_.clear(); return; } - endpoint_name_ = std::string{endpoint_name_bytes}; } WifiLanServiceInfo::operator std::string() const { @@ -137,22 +135,23 @@ WifiLanServiceInfo::operator std::string() const { version_and_pcp_byte |= static_cast(static_cast(pcp_) & kPcpBitmask); - std::string usable_endpoint_name(endpoint_name_); - if (endpoint_name_.size() > kMaxEndpointNameLength) { + ByteArray usable_endpoint_info(endpoint_info_); + if (endpoint_info_.size() > kMaxEndpointInfoLength) { NEARBY_LOG( INFO, - "While serializing WifiLanServiceInfo, truncating Endpoint Name %s " + "While serializing WifiLanServiceInfo, truncating Endpoint info %s " "(%lu bytes) down to %d bytes", - endpoint_name_.c_str(), endpoint_name_.size(), kMaxEndpointNameLength); - usable_endpoint_name.erase(kMaxEndpointNameLength); + std::string(endpoint_info_).c_str(), endpoint_info_.size(), + kMaxEndpointInfoLength); + usable_endpoint_info.SetData(endpoint_info_.data(), kMaxEndpointInfoLength); } // clang-format off std::string out = absl::StrCat(std::string(1, version_and_pcp_byte), endpoint_id_, std::string(service_id_hash_), - std::string(1, usable_endpoint_name.size()), - usable_endpoint_name); + std::string(1, usable_endpoint_info.size()), + std::string(usable_endpoint_info)); // clang-format on return Base64Utils::Encode(ByteArray{std::move(out)}); diff --git a/cpp/core_v2/internal/wifi_lan_service_info.h b/cpp/core_v2/internal/wifi_lan_service_info.h index dff5e0d4..4b6b3897 100644 --- a/cpp/core_v2/internal/wifi_lan_service_info.h +++ b/cpp/core_v2/internal/wifi_lan_service_info.h @@ -28,7 +28,7 @@ class WifiLanServiceInfo { WifiLanServiceInfo() = default; WifiLanServiceInfo(Version version, Pcp pcp, absl::string_view endpoint_id, const ByteArray& service_id_hash, - absl::string_view endpoint_name); + const ByteArray& endpoint_info); explicit WifiLanServiceInfo(absl::string_view service_info_string); WifiLanServiceInfo(const WifiLanServiceInfo&) = default; WifiLanServiceInfo& operator=(const WifiLanServiceInfo&) = default; @@ -42,7 +42,7 @@ class WifiLanServiceInfo { Version GetVersion() const { return version_; } Pcp GetPcp() const { return pcp_; } std::string GetEndpointId() const { return endpoint_id_; } - std::string GetEndpointName() const { return endpoint_name_; } + ByteArray GetEndpointInfo() const { return endpoint_info_; } ByteArray GetServiceIdHash() const { return service_id_hash_; } private: @@ -53,7 +53,7 @@ class WifiLanServiceInfo { // The length for endpoint id in encrypted WifiLanServiceInfo string. static constexpr int kEndpointIdLength = 4; // The maximum length for endpoint id in encrypted WifiLanServiceInfo string. - static constexpr int kMaxEndpointNameLength = 131; + static constexpr int kMaxEndpointInfoLength = 131; static constexpr int kVersionBitmask = 0x0E0; static constexpr int kPcpBitmask = 0x01F; @@ -67,8 +67,8 @@ class WifiLanServiceInfo { std::string endpoint_id_; // Connected hash service id. ByteArray service_id_hash_; - // Connected endpoint name. - std::string endpoint_name_; + // Connected endpoint info. + ByteArray endpoint_info_; }; } // namespace connections diff --git a/cpp/core_v2/internal/wifi_lan_service_info_test.cc b/cpp/core_v2/internal/wifi_lan_service_info_test.cc index 31a09955..90215c06 100644 --- a/cpp/core_v2/internal/wifi_lan_service_info_test.cc +++ b/cpp/core_v2/internal/wifi_lan_service_info_test.cc @@ -20,21 +20,23 @@ constexpr absl::string_view kEndPointName{"RAWK + ROWL!"}; TEST(WifiLanServiceInfoTest, ConstructionWorks) { ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; - WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, kEndPointID, - service_id_hash, kEndPointName}; + ByteArray endpoint_info{std::string(kEndPointName)}; + WifiLanServiceInfo wifi_lan_service_info{ + kVersion, kPcp, kEndPointID, service_id_hash, endpoint_info}; EXPECT_TRUE(wifi_lan_service_info.IsValid()); EXPECT_EQ(kPcp, wifi_lan_service_info.GetPcp()); EXPECT_EQ(kVersion, wifi_lan_service_info.GetVersion()); EXPECT_EQ(kEndPointID, wifi_lan_service_info.GetEndpointId()); EXPECT_EQ(service_id_hash, wifi_lan_service_info.GetServiceIdHash()); - EXPECT_EQ(kEndPointName, wifi_lan_service_info.GetEndpointName()); + EXPECT_EQ(endpoint_info, wifi_lan_service_info.GetEndpointInfo()); } TEST(WifiLanServiceInfoTest, ConstructionFromSerializedStringWorks) { ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo org_wifi_lan_service_info{kVersion, kPcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; std::string wifi_lan_service_info_string{org_wifi_lan_service_info}; WifiLanServiceInfo wifi_lan_service_info{wifi_lan_service_info_string}; @@ -44,15 +46,16 @@ TEST(WifiLanServiceInfoTest, ConstructionFromSerializedStringWorks) { EXPECT_EQ(kVersion, wifi_lan_service_info.GetVersion()); EXPECT_EQ(kEndPointID, wifi_lan_service_info.GetEndpointId()); EXPECT_EQ(service_id_hash, wifi_lan_service_info.GetServiceIdHash()); - EXPECT_EQ(kEndPointName, wifi_lan_service_info.GetEndpointName()); + EXPECT_EQ(endpoint_info, wifi_lan_service_info.GetEndpointInfo()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithBadVersion) { auto bad_version = static_cast(666); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo wifi_lan_service_info{bad_version, kPcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(wifi_lan_service_info.IsValid()); } @@ -61,8 +64,9 @@ TEST(WifiLanServiceInfoTest, ConstructionFailsWithBadPCP) { auto bad_pcp = static_cast(666); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo wifi_lan_service_info{kVersion, bad_pcp, kEndPointID, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(wifi_lan_service_info.IsValid()); } @@ -71,8 +75,9 @@ TEST(WifiLanServiceInfoTest, ConstructionFailsWithShortEndpointId) { std::string short_endpoint_id("AB1"); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, short_endpoint_id, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(wifi_lan_service_info.IsValid()); } @@ -81,8 +86,9 @@ TEST(WifiLanServiceInfoTest, ConstructionFailsWithLongEndpointId) { std::string long_endpoint_id("AB12X"); ByteArray service_id_hash{std::string(kServiceIDHashBytes)}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, long_endpoint_id, - service_id_hash, kEndPointName}; + service_id_hash, endpoint_info}; EXPECT_FALSE(wifi_lan_service_info.IsValid()); } @@ -91,8 +97,9 @@ TEST(WifiLanServiceInfoTest, ConstructionFailsWithShortServiceIdHash) { char short_service_id_hash_bytes[] = "\x0a\x0b"; ByteArray short_service_id_hash{short_service_id_hash_bytes}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo wifi_lan_service_info{ - kVersion, kPcp, kEndPointID, short_service_id_hash, kEndPointName}; + kVersion, kPcp, kEndPointID, short_service_id_hash, endpoint_info}; EXPECT_FALSE(wifi_lan_service_info.IsValid()); } @@ -101,8 +108,9 @@ TEST(WifiLanServiceInfoTest, ConstructionFailsWithLongServiceIdHash) { char long_service_id_hash_bytes[] = "\x0a\x0b\x0c\x0d"; ByteArray long_service_id_hash{long_service_id_hash_bytes}; + ByteArray endpoint_info{std::string(kEndPointName)}; WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, kEndPointID, - long_service_id_hash, kEndPointName}; + long_service_id_hash, endpoint_info}; EXPECT_FALSE(wifi_lan_service_info.IsValid()); } diff --git a/cpp/core_v2/listeners.h b/cpp/core_v2/listeners.h index 649ea6d9..90bddc7f 100644 --- a/cpp/core_v2/listeners.h +++ b/cpp/core_v2/listeners.h @@ -35,10 +35,9 @@ struct ResultCallback { }; struct ConnectionResponseInfo { - std::string remote_endpoint_name; + ByteArray remote_endpoint_info; std::string authentication_token; ByteArray raw_authentication_token; - ByteArray endpoint_info; bool is_incoming_connection = false; bool is_connection_verified = false; }; @@ -121,13 +120,13 @@ struct DiscoveryListener { // Called when a remote endpoint is discovered. // // endpoint_id - The ID of the remote endpoint that was discovered. - // endpoint_name - The human readable name of the remote endpoint. + // endpoint_info - The info of the remote endpoint representd by ByteArray. // service_id - The ID of the service advertised by the remote endpoint. std::function endpoint_found_cb = - DefaultCallback(); // Called when a remote endpoint is no longer discoverable; only called for diff --git a/cpp/core_v2/options.h b/cpp/core_v2/options.h index 86fe59dc..9ee207ee 100644 --- a/cpp/core_v2/options.h +++ b/cpp/core_v2/options.h @@ -2,17 +2,64 @@ #define CORE_V2_OPTIONS_H_ #include "core_v2/strategy.h" +#include "platform_v2/base/byte_array.h" +#include "proto/connections_enums.pb.h" +#include "proto/connections_enums.pb.h" namespace location { namespace nearby { namespace connections { +using Medium = ::location::nearby::proto::connections::Medium; + // Generic type: allows definition of a feature T for every Medium. template struct MediumSelector { T bluetooth; + T ble; T web_rtc; T wifi_lan; + + constexpr MediumSelector() = default; + constexpr MediumSelector(const MediumSelector&) = default; + constexpr MediumSelector& operator=(const MediumSelector&) = default; + + constexpr bool Any(T value) const { + return bluetooth == value || ble == value || web_rtc == value || + wifi_lan == value; + } + + constexpr bool All(T value) const { + return bluetooth == value && ble == value && web_rtc == value && + wifi_lan == value; + } + + constexpr int Count(T value) const { + int count = 0; + if (bluetooth == value) count++; + if (ble == value) count++; + if (wifi_lan == value) count++; + if (web_rtc == value) count++; + return count; + } + + constexpr MediumSelector& SetAll(T value) { + bluetooth = value; + ble = value; + web_rtc = value; + wifi_lan = value; + return *this; + } + + std::vector GetMediums(T value) const { + std::vector mediums; + // Mediums are sorted in order of decreasing preference. + if (wifi_lan == value) mediums.push_back(Medium::WIFI_LAN); + if (web_rtc == value) mediums.push_back(Medium::WEB_RTC); + if (ble == value) mediums.push_back(Medium::BLE); + if (bluetooth == value) mediums.push_back(Medium::BLUETOOTH); + return mediums; + } }; // Feature On/Off switch for mediums. @@ -22,17 +69,23 @@ using BooleanMediumSelector = MediumSelector; // All fields are mutable, to make the type copy-assignable. struct ConnectionOptions { Strategy strategy; - BooleanMediumSelector allowed; + BooleanMediumSelector allowed{BooleanMediumSelector().SetAll(true)}; bool auto_upgrade_bandwidth; bool enforce_topology_constraints; + ByteArray remote_bluetooth_mac_address; // Verify if ConnectionOptions is in a not-initialized (Empty) state. - bool Empty() const { - return strategy.IsNone(); - } + bool Empty() const { return strategy.IsNone(); } // Bring ConnectionOptions to a not-initialized (Empty) state. - void Clear() { - strategy.Clear(); + void Clear() { strategy.Clear(); } + // Returns a copy, but if no mediums are allowed, allowes all mediums. + ConnectionOptions CompatibleOptions() const { + ConnectionOptions result = *this; + if (!allowed.Any(true)) { + result.allowed.SetAll(true); + } + return result; } + std::vector GetMediums() const { return allowed.GetMediums(true); } }; } // namespace connections diff --git a/cpp/core_v2/params.h b/cpp/core_v2/params.h index b0ddde22..2cbc89b7 100644 --- a/cpp/core_v2/params.h +++ b/cpp/core_v2/params.h @@ -4,6 +4,7 @@ #include #include "core_v2/listeners.h" +#include "platform_v2/base/byte_array.h" namespace location { namespace nearby { @@ -12,11 +13,11 @@ namespace connections { // Used by Discovery in Core::RequestConnection(). // Used by Advertising in Core::StartAdvertising(). struct ConnectionRequestInfo { - // name - A human readable name for this endpoint, to appear on - // other devices. - // listener - A set of callbacks notified when remote endpoints request a - // connection to this endpoint. - std::string name; + // endpoint_info - Identifing information about this endpoint (eg. name, + // device type). + // listener - A set of callbacks notified when remote endpoints request a + // connection to this endpoint. + ByteArray endpoint_info; ConnectionListener listener; }; diff --git a/cpp/core_v2/status.h b/cpp/core_v2/status.h index d56dab42..c5d49740 100644 --- a/cpp/core_v2/status.h +++ b/cpp/core_v2/status.h @@ -24,6 +24,7 @@ struct Status { kAlreadyConnectedToEndpoint, kNotConnectedToEndpoint, kBluetoothError, + kBleError, kWifiLanError, kPayloadUnknown, }; diff --git a/cpp/platform/impl/g3/BUILD b/cpp/platform/impl/g3/BUILD index c231beae..ddb412ed 100644 --- a/cpp/platform/impl/g3/BUILD +++ b/cpp/platform/impl/g3/BUILD @@ -7,7 +7,6 @@ cc_library( "system_clock_impl.h", ], visibility = [ - "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", "//core:__subpackages__", "//platform:__subpackages__", ], diff --git a/cpp/platform/impl/sample/BUILD b/cpp/platform/impl/sample/BUILD index dc3922f7..fdba4e14 100644 --- a/cpp/platform/impl/sample/BUILD +++ b/cpp/platform/impl/sample/BUILD @@ -5,11 +5,7 @@ cc_library( "sample_platform.cc", "settable_future_impl.h", ], - visibility = [ - "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", - "//core:__subpackages__", - "//location/nearby/setup/core:__subpackages__", - ], + visibility = ["//visibility:private"], deps = [ "//platform:types", "//platform:utils", diff --git a/cpp/platform/impl/shared/sample/BUILD b/cpp/platform/impl/shared/sample/BUILD index a1d0605f..0a29de7d 100644 --- a/cpp/platform/impl/shared/sample/BUILD +++ b/cpp/platform/impl/shared/sample/BUILD @@ -10,7 +10,6 @@ cc_library( "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", "//core:__subpackages__", "//platform/impl:__subpackages__", - "//location/nearby/setup/core:__subpackages__", ], deps = [ "//platform:types", diff --git a/cpp/platform_v2/api/ble.h b/cpp/platform_v2/api/ble.h index 26883dec..49c9107c 100644 --- a/cpp/platform_v2/api/ble.h +++ b/cpp/platform_v2/api/ble.h @@ -5,7 +5,6 @@ #include "platform_v2/base/byte_array.h" #include "platform_v2/base/input_stream.h" #include "platform_v2/base/output_stream.h" -#include "absl/strings/string_view.h" namespace location { namespace nearby { @@ -15,15 +14,17 @@ namespace api { // particular BLE device to connect to its GATT server. class BlePeripheral { public: - virtual ~BlePeripheral() {} + virtual ~BlePeripheral() = default; - // The returned reference lifetime matches BlePeripheral object. - virtual BluetoothDevice& GetBluetoothDevice() = 0; + virtual std::string GetName() const = 0; + + virtual ByteArray GetAdvertisementBytes( + const std::string& service_id) const = 0; }; class BleSocket { public: - virtual ~BleSocket() {} + virtual ~BleSocket() = default; // Returns the InputStream of the BleSocket. // On error, returned stream will report Exception::kIo on any operation. @@ -45,64 +46,58 @@ class BleSocket { // Returns Exception::kIo on error, Exception::kSuccess otherwise. virtual Exception Close() = 0; - // The returned object is not owned by the caller, and can be invalidated once - // the BleSocket object is destroyed. - virtual BlePeripheral& GetRemotePeripheral() = 0; + // Returns valid BlePeripheral pointer if there is a connection, and + // nullptr otherwise. + virtual BlePeripheral* GetRemotePeripheral() = 0; }; // Container of operations that can be performed over the BLE medium. class BleMedium { public: - virtual ~BleMedium() {} + virtual ~BleMedium() = default; - virtual bool StartAdvertising(absl::string_view service_id, - const ByteArray& advertisement) = 0; - virtual void StopAdvertising(absl::string_view service_id) = 0; + virtual bool StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes) = 0; + virtual bool StopAdvertising(const std::string& service_id) = 0; - class DiscoveredPeripheralCallback { - public: - virtual ~DiscoveredPeripheralCallback() {} - - // The BlePeripheral* is not owned by callbacks. - // It is passed to give access to its non-const methods. - // It is guaranteed to be valid for the duration of call. - virtual void OnPeripheralDiscovered(BlePeripheral* ble_peripheral, - absl::string_view service_id, - const ByteArray& advertisement) = 0; - virtual void OnPeripheralLost(BlePeripheral* ble_peripheral, - absl::string_view service_id) = 0; + // Callback that is invoked when a discovered peripheral is found or lost. + struct DiscoveredPeripheralCallback { + std::function + peripheral_discovered_cb = + DefaultCallback(); + std::function + peripheral_lost_cb = + DefaultCallback(); }; // Returns true once the BLE scan has been initiated. - virtual bool StartScanning( - absl::string_view service_id, - const DiscoveredPeripheralCallback& discovered_peripheral_callback) = 0; + virtual bool StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback) = 0; // Returns true once BLE scanning for service_id is well and truly stopped; // after this returns, there must be no more invocations of the // DiscoveredPeripheralCallback passed in to StartScanning() for service_id. - virtual void StopScanning(absl::string_view service_id) = 0; + virtual bool StopScanning(const std::string& service_id) = 0; // Callback that is invoked when a new connection is accepted. - class AcceptedConnectionCallback { - public: - virtual ~AcceptedConnectionCallback() {} - - virtual void OnConnectionAccepted(std::unique_ptr socket, - absl::string_view service_id) = 0; + struct AcceptedConnectionCallback { + std::function + accepted_cb = DefaultCallback(); }; // Returns true once BLE socket connection requests to service_id can be // accepted. virtual bool StartAcceptingConnections( - absl::string_view service_id, - const AcceptedConnectionCallback& accepted_connection_callback) = 0; - virtual void StopAcceptingConnections(const std::string& service_id) = 0; + const std::string& service_id, AcceptedConnectionCallback callback) = 0; + virtual bool StopAcceptingConnections(const std::string& service_id) = 0; - // BlePeripheral* is not owned by this call; - // it must remain valid for the duration of a call. - virtual std::unique_ptr Connect(BlePeripheral* ble_peripheral, - absl::string_view service_id) = 0; + // Connects to a BLE peripheral. + // On success, returns a new BleSocket. + // On error, returns nullptr. + virtual std::unique_ptr Connect(BlePeripheral& peripheral, + const std::string& service_id) = 0; }; } // namespace api diff --git a/cpp/platform_v2/api/bluetooth_adapter.h b/cpp/platform_v2/api/bluetooth_adapter.h index a18bbef3..96ec2e7a 100644 --- a/cpp/platform_v2/api/bluetooth_adapter.h +++ b/cpp/platform_v2/api/bluetooth_adapter.h @@ -49,6 +49,9 @@ class BluetoothAdapter { virtual std::string GetName() const = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String) virtual bool SetName(absl::string_view name) = 0; + + // Returns BT MAC address assigned to this adapter. + virtual std::string GetMacAddress() const = 0; }; } // namespace api diff --git a/cpp/platform_v2/api/bluetooth_classic.h b/cpp/platform_v2/api/bluetooth_classic.h index fa3a6061..6dddd606 100644 --- a/cpp/platform_v2/api/bluetooth_classic.h +++ b/cpp/platform_v2/api/bluetooth_classic.h @@ -21,6 +21,9 @@ class BluetoothDevice { // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() virtual std::string GetName() const = 0; + + // Returns BT MAC address assigned to this device. + virtual std::string GetMacAddress() const = 0; }; // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. @@ -132,6 +135,8 @@ class BluetoothClassicMedium { // Returns nullptr error. virtual std::unique_ptr ListenForService( const std::string& service_name, const std::string& service_uuid) = 0; + + virtual BluetoothDevice* FindRemoteDevice(const std::string& mac_address) = 0; }; } // namespace api diff --git a/cpp/platform_v2/api/wifi_lan.h b/cpp/platform_v2/api/wifi_lan.h index 10e6cdb2..12a9e423 100644 --- a/cpp/platform_v2/api/wifi_lan.h +++ b/cpp/platform_v2/api/wifi_lan.h @@ -20,6 +20,10 @@ class WifiLanService { virtual ~WifiLanService() = default; virtual std::string GetName() const = 0; + + // Returns the local device's as a pair. + // IP address is in byte sequence, in network order. + virtual std::pair GetServiceAddress() const = 0; }; class WifiLanSocket { @@ -88,8 +92,7 @@ class WifiLanMedium { // Returns true once WifiLan socket connection requests to service_id can be // accepted. virtual bool StartAcceptingConnections( - const std::string& service_id, - AcceptedConnectionCallback callback) = 0; + const std::string& service_id, AcceptedConnectionCallback callback) = 0; virtual bool StopAcceptingConnections(const std::string& service_id) = 0; // Connects to a WifiLan service. @@ -97,6 +100,9 @@ class WifiLanMedium { // On error, returns nullptr. virtual std::unique_ptr Connect( WifiLanService& service, const std::string& service_id) = 0; + + virtual WifiLanService* FindRemoteService(const std::string& ip_address, + int port) = 0; }; } // namespace api diff --git a/cpp/platform_v2/base/BUILD b/cpp/platform_v2/base/BUILD index 2fd6a8ca..f9d16585 100644 --- a/cpp/platform_v2/base/BUILD +++ b/cpp/platform_v2/base/BUILD @@ -4,10 +4,12 @@ cc_library( name = "base", srcs = [ "base64_utils.cc", + "bluetooth_utils.cc", "prng.cc", ], hdrs = [ "base64_utils.h", + "bluetooth_utils.h", "byte_array.h", "callable.h", "exception.h", @@ -28,6 +30,7 @@ cc_library( deps = [ "//absl/meta:type_traits", "//absl/strings", + "//absl/strings:str_format", "//absl/time", ], ) @@ -96,6 +99,7 @@ cc_library( cc_test( name = "platform_base_test", srcs = [ + "bluetooth_utils_test.cc", "byte_array_test.cc", "prng_test.cc", ], diff --git a/cpp/platform_v2/base/bluetooth_utils.cc b/cpp/platform_v2/base/bluetooth_utils.cc new file mode 100644 index 00000000..e3221878 --- /dev/null +++ b/cpp/platform_v2/base/bluetooth_utils.cc @@ -0,0 +1,61 @@ +#include "platform_v2/base/bluetooth_utils.h" + +#include "absl/strings/escaping.h" +#include "absl/strings/str_format.h" + +namespace location { +namespace nearby { + +std::string BluetoothUtils::ToString(const ByteArray& bluetooth_mac_address) { + std::string colon_delimited_string; + + if (bluetooth_mac_address.size() != kBluetoothMacAddressLength) + return colon_delimited_string; + + if (IsBluetoothMacAddressUnset(bluetooth_mac_address)) + return colon_delimited_string; + + for (auto byte : std::string(bluetooth_mac_address)) { + if (!colon_delimited_string.empty()) + absl::StrAppend(&colon_delimited_string, ":"); + absl::StrAppend(&colon_delimited_string, absl::StrFormat("%02X", byte)); + } + return colon_delimited_string; +} + +ByteArray BluetoothUtils::FromString(absl::string_view bluetooth_mac_address) { + std::string bt_mac_address(bluetooth_mac_address); + + // Remove the colon delimiters. + bt_mac_address.erase( + std::remove(bt_mac_address.begin(), bt_mac_address.end(), ':'), + bt_mac_address.end()); + + // If the bluetooth mac address is invalid (wrong size), return a null byte + // array. + if (bt_mac_address.length() != kBluetoothMacAddressLength * 2) { + return ByteArray(); + } + + // Convert to bytes. If MAC Address bytes are unset, return a null byte array. + auto bt_mac_address_string(absl::HexStringToBytes(bt_mac_address)); + auto bt_mac_address_bytes = + ByteArray(bt_mac_address_string.data(), bt_mac_address_string.size()); + if (IsBluetoothMacAddressUnset(bt_mac_address_bytes)) { + return ByteArray(); + } + return bt_mac_address_bytes; +} + +bool BluetoothUtils::IsBluetoothMacAddressUnset( + const ByteArray& bluetooth_mac_address_bytes) { + for (int i = 0; i < bluetooth_mac_address_bytes.size(); i++) { + if (bluetooth_mac_address_bytes.data()[i] != 0) { + return false; + } + } + return true; +} + +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/base/bluetooth_utils.h b/cpp/platform_v2/base/bluetooth_utils.h new file mode 100644 index 00000000..a8a8a20f --- /dev/null +++ b/cpp/platform_v2/base/bluetooth_utils.h @@ -0,0 +1,32 @@ +#ifndef PLATFORM_V2_BASE_BLUETOOTH_UTILS_H_ +#define PLATFORM_V2_BASE_BLUETOOTH_UTILS_H_ + +#include "platform_v2/base/byte_array.h" +#include "absl/strings/string_view.h" + +namespace location { +namespace nearby { + +class BluetoothUtils { + public: + static constexpr int kBluetoothMacAddressLength = 6; + + // Converts a Bluetooth MAC address from byte array to String format. Returns + // empty if input byte array is not of correct format. + // e.g. {-84, 55, 67, -68, -87, 40} -> "AC:37:43:BC:A9:28". + static std::string ToString(const ByteArray& bluetooth_mac_address); + + // Converts a Bluetooth MAC address from String format to byte array. Returns + // empty if input string is not of correct format. + // e.g. "AC:37:43:BC:A9:28" -> {-84, 55, 67, -68, -87, 40}. + static ByteArray FromString(absl::string_view bluetooth_mac_address); + + // Checks if a Bluetooth MAC address is zero for every byte. + static bool IsBluetoothMacAddressUnset( + const ByteArray& bluetooth_mac_address); +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_BASE_BLUETOOTH_UTILS_H_ diff --git a/cpp/platform_v2/base/bluetooth_utils_test.cc b/cpp/platform_v2/base/bluetooth_utils_test.cc new file mode 100644 index 00000000..7cc6f53e --- /dev/null +++ b/cpp/platform_v2/base/bluetooth_utils_test.cc @@ -0,0 +1,75 @@ +#include "platform_v2/base/bluetooth_utils.h" + +#include "gtest/gtest.h" + +namespace location { +namespace nearby { + +constexpr absl::string_view kBluetoothMacAddress{"00:00:E6:88:64:13"}; +constexpr char kBluetoothMacAddressBytes[] = {0x00, 0x00, 0xe6, + 0x88, 0x64, 0x13}; + +TEST(BluetoothUtilsTest, ToStringWorks) { + ByteArray bt_mac_address_bytes{ + kBluetoothMacAddressBytes, sizeof(kBluetoothMacAddressBytes)}; + + auto bt_mac_address = BluetoothUtils::ToString(bt_mac_address_bytes); + + EXPECT_EQ(kBluetoothMacAddress, bt_mac_address); +} + +TEST(BluetoothUtilsTest, FromStringWorks) { + ByteArray bt_mac_address_bytes{ + kBluetoothMacAddressBytes, sizeof(kBluetoothMacAddressBytes)}; + + auto bt_mac_address_bytes_result = + BluetoothUtils::FromString(kBluetoothMacAddress); + + EXPECT_EQ(bt_mac_address_bytes, bt_mac_address_bytes_result); +} + +TEST(BluetoothUtilsTest, InvalidBytesReturnsEmptyString) { + std::string string_result; + + char bad_bt_mac_address_1[] = {0x02, 0x20, 0x00}; + ByteArray bad_bt_mac_address_bytes_1{bad_bt_mac_address_1, + sizeof(bad_bt_mac_address_1)}; + string_result = BluetoothUtils::ToString(bad_bt_mac_address_bytes_1); + EXPECT_TRUE(string_result.empty()); + + char bad_bt_mac_address_2[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + ByteArray bad_bt_mac_address_bytes_2{bad_bt_mac_address_2, + sizeof(bad_bt_mac_address_2)}; + string_result = BluetoothUtils::ToString(bad_bt_mac_address_bytes_2); + EXPECT_TRUE(string_result.empty()); + + char bad_bt_mac_address_3[] = {0x11, 0x22, 0x33, 0x44, 0x55, + 0x66, 0x77, 0x88, 0x99}; + ByteArray bad_bt_mac_address_bytes_3{bad_bt_mac_address_3, + sizeof(bad_bt_mac_address_3)}; + string_result = BluetoothUtils::ToString(bad_bt_mac_address_bytes_3); + EXPECT_TRUE(string_result.empty()); +} + +TEST(BluetoothUtilsTest, InvalidStringReturnsEmptyByteArray) { + ByteArray bytes_result; + + std::string bad_bt_mac_address_1 = "022:00"; + bytes_result = BluetoothUtils::FromString(bad_bt_mac_address_1); + EXPECT_TRUE(bytes_result.Empty()); + + std::string bad_bt_mac_address_2 = "22:00:11:33:77:aa::bb::99"; + bytes_result = BluetoothUtils::FromString(bad_bt_mac_address_2); + EXPECT_TRUE(bytes_result.Empty()); + + std::string bad_bt_mac_address_3 = "00:00:00:00:00:00"; + bytes_result = BluetoothUtils::FromString(bad_bt_mac_address_3); + EXPECT_TRUE(bytes_result.Empty()); + + std::string bad_bt_mac_address_4 = "BLUETOOTHCHIP"; + bytes_result = BluetoothUtils::FromString(bad_bt_mac_address_4); + EXPECT_TRUE(bytes_result.Empty()); +} + +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/base/byte_array.h b/cpp/platform_v2/base/byte_array.h index df84edb9..1cdaf118 100644 --- a/cpp/platform_v2/base/byte_array.h +++ b/cpp/platform_v2/base/byte_array.h @@ -74,7 +74,7 @@ class ByteArray { // Moves string out of temporary ByteArray, allowing for a zero-copy // operation. - explicit operator std::string() const&& { return std::move(data_); } + explicit operator std::string() && { return std::move(data_); } private: std::string data_; diff --git a/cpp/platform_v2/base/medium_environment.cc b/cpp/platform_v2/base/medium_environment.cc index 8d1cbece..8687703e 100644 --- a/cpp/platform_v2/base/medium_environment.cc +++ b/cpp/platform_v2/base/medium_environment.cc @@ -5,6 +5,7 @@ #include #include +#include "platform_v2/api/ble.h" #include "platform_v2/api/bluetooth_adapter.h" #include "platform_v2/api/bluetooth_classic.h" #include "platform_v2/api/wifi_lan.h" @@ -42,6 +43,7 @@ void MediumEnvironment::Reset() { NEARBY_LOG(INFO, "MediumEnvironment::Reset()"); bluetooth_adapters_.clear(); bluetooth_mediums_.clear(); + ble_mediums_.clear(); wifi_lan_mediums_.clear(); }); Sync(); @@ -154,6 +156,48 @@ void MediumEnvironment::OnBluetoothDeviceStateChanged( } } +api::BluetoothDevice* MediumEnvironment::FindBluetoothDevice( + const std::string& mac_address) { + api::BluetoothDevice* device = nullptr; + CountDownLatch latch(1); + RunOnMediumEnvironmentThread([this, &device, &latch, &mac_address](){ + for (auto& item : bluetooth_mediums_) { + auto* adapter = item.second.adapter; + if (!adapter) continue; + if (adapter->GetMacAddress() == mac_address) { + device = bluetooth_adapters_[adapter]; + break; + } + } + latch.CountDown(); + }); + latch.Await(); + return device; +} + +void MediumEnvironment::OnBlePeripheralStateChanged( + BleMediumContext& info, api::BlePeripheral& peripheral, + const std::string& service_id, bool enabled) { + if (!enabled_) return; + NEARBY_LOG(INFO, + "G3 OnBleServiceStateChanged [peripheral impl=%p]; context=%p; " + "service_id=%s; notify=%d", + &peripheral, &info, service_id.c_str(), + enable_notifications_.load()); + if (!enable_notifications_) return; + RunOnMediumEnvironmentThread([&info, enabled, &peripheral, service_id]() { + NEARBY_LOG(INFO, + "G3 [Run] OnBlePeripheralStateChanged [peripheral impl=%p]; " + "context=%p; service_id=%s; enabled=%d", + &peripheral, &info, service_id.c_str(), enabled); + if (enabled) { + info.discovery_callback.peripheral_discovered_cb(peripheral, service_id); + } else { + info.discovery_callback.peripheral_lost_cb(peripheral, service_id); + } + }); +} + void MediumEnvironment::OnWifiLanServiceStateChanged( WifiLanMediumContext& info, api::WifiLanService& service, const std::string& service_id, bool enabled) { @@ -164,6 +208,10 @@ void MediumEnvironment::OnWifiLanServiceStateChanged( &service, &info, service_id.c_str(), enable_notifications_.load()); if (!enable_notifications_) return; RunOnMediumEnvironmentThread([&info, enabled, &service, service_id]() { + NEARBY_LOG(INFO, + "G3 [Run] OnWifiLanServiceStateChanged [service impl=%p]; " + "context=%p; service_id=%s; enabled=%d", + &service, &info, service_id.c_str(), enabled); auto service_id_context = info.services.find(service_id); if (service_id_context == info.services.end()) return; @@ -246,6 +294,125 @@ void MediumEnvironment::UnregisterBluetoothMedium( }); } +void MediumEnvironment::RegisterBleMedium(api::BleMedium& medium) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium]() { + ble_mediums_.insert({&medium, BleMediumContext{}}); + NEARBY_LOG(INFO, "Registered: medium=%p", &medium); + }); +} + +void MediumEnvironment::UpdateBleMediumForAdvertising( + api::BleMedium& medium, api::BlePeripheral& peripheral, + const std::string& service_id, bool enabled) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium, &peripheral, service_id, + enabled]() { + auto item = ble_mediums_.find(&medium); + if (item == ble_mediums_.end()) { + NEARBY_LOG(INFO, + "UpdateBleMediumForAdvertising failed. There is no medium " + "registered."); + return; + } + auto& context = item->second; + context.ble_peripheral = &peripheral; + context.advertising = enabled; + NEARBY_LOG(INFO, + "Update Ble medium for advertising: this=%p; medium=%p; " + "service_id=%s; name=%s; enabled=%d; ", + this, &medium, service_id.c_str(), peripheral.GetName().c_str(), + enabled); + for (auto& medium_info : ble_mediums_) { + auto& local_medium = medium_info.first; + auto& info = medium_info.second; + // Do not send notification to the same medium. + if (local_medium == &medium) continue; + OnBlePeripheralStateChanged(info, peripheral, service_id, enabled); + } + }); +} + +void MediumEnvironment::UpdateBleMediumForScanning( + api::BleMedium& medium, const std::string& service_id, + BleDiscoveredPeripheralCallback callback, bool enabled) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium, service_id, + callback = std::move(callback), enabled]() { + auto item = ble_mediums_.find(&medium); + if (item == ble_mediums_.end()) { + NEARBY_LOG(INFO, + "UpdateBleMediumFoScanning failed. There is no medium " + "registered."); + return; + } + auto& context = item->second; + context.discovery_callback = std::move(callback); + NEARBY_LOG(INFO, + "Update Ble medium for scanning: this=%p; medium=%p; " + "service_id=%s; enabled=%d ;", + this, &medium, service_id.c_str(), enabled); + for (auto& medium_info : ble_mediums_) { + auto& local_medium = medium_info.first; + auto& info = medium_info.second; + // Do not send notification to the same medium. + if (local_medium == &medium) continue; + // Search advertising mediums and send notification. + if (info.advertising && enabled) { + OnBlePeripheralStateChanged(context, *(info.ble_peripheral), service_id, + enabled); + } + } + }); +} + +void MediumEnvironment::UpdateBleMediumForAcceptedConnection( + api::BleMedium& medium, const std::string& service_id, + BleAcceptedConnectionCallback callback) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium, service_id, + callback = std::move(callback)]() { + auto item = ble_mediums_.find(&medium); + if (item == ble_mediums_.end()) { + NEARBY_LOG( + INFO, "Update Ble medium failed. There is no medium registered."); + return; + } + auto& context = item->second; + context.accepted_connection_callback = std::move(callback); + NEARBY_LOG(INFO, + "Update Ble medium for accepted callback: this=%p; " + "medium=%p; service_id=%s; ", + this, &medium, service_id.c_str()); + }); +} + +void MediumEnvironment::UnregisterBleMedium(api::BleMedium& medium) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium]() { + auto item = ble_mediums_.extract(&medium); + if (item.empty()) return; + NEARBY_LOG(INFO, "Unregistered Ble medium"); + }); +} + +void MediumEnvironment::CallBleAcceptedConnectionCallback( + api::BleMedium& medium, api::BleSocket& socket, + const std::string& service_id) { + if (!enabled_) return; + RunOnMediumEnvironmentThread([this, &medium, &socket, service_id]() { + auto item = ble_mediums_.find(&medium); + if (item == ble_mediums_.end()) { + NEARBY_LOG(INFO, + "Call AcceptedConnectionCallback failed.. There is no medium " + "registered."); + return; + } + auto& info = item->second; + info.accepted_connection_callback.accepted_cb(socket, service_id); + }); +} + void MediumEnvironment::RegisterWebRtcSignalingMessenger( absl::string_view self_id, OnSignalingMessageCallback callback) { if (!enabled_) return; @@ -437,5 +604,26 @@ void MediumEnvironment::CallWifiLanAcceptedConnectionCallback( }); } +api::WifiLanService* MediumEnvironment::FindWifiLanService( + const std::string& ip_address, int port) { + api::WifiLanService* remote_service = nullptr; + CountDownLatch latch(1); + RunOnMediumEnvironmentThread( + [this, &remote_service, &ip_address, port, &latch]() { + for (auto& item : wifi_lan_mediums_) { + auto* service = item.second.wifi_lan_service; + if (!service) continue; + auto addr = remote_service->GetServiceAddress(); + if (addr.first == ip_address && addr.second == port) { + remote_service = service; + break; + } + } + latch.CountDown(); + }); + latch.Await(); + return remote_service; +} + } // namespace nearby } // namespace location diff --git a/cpp/platform_v2/base/medium_environment.h b/cpp/platform_v2/base/medium_environment.h index 0464a598..a1a0f27e 100644 --- a/cpp/platform_v2/base/medium_environment.h +++ b/cpp/platform_v2/base/medium_environment.h @@ -33,6 +33,10 @@ class MediumEnvironment { public: using BluetoothDiscoveryCallback = api::BluetoothClassicMedium::DiscoveryCallback; + using BleDiscoveredPeripheralCallback = + api::BleMedium::DiscoveredPeripheralCallback; + using BleAcceptedConnectionCallback = + api::BleMedium::AcceptedConnectionCallback; using OnSignalingMessageCallback = api::WebRtcSignalingMessenger::OnSignalingMessageCallback; using WifiLanDiscoveredServiceCallback = @@ -103,6 +107,9 @@ class MediumEnvironment { // Removes medium-related info. This should correspond to device power off. void UnregisterBluetoothMedium(api::BluetoothClassicMedium& medium); + // Returns a Bluetooth Device object matching given mac address to nullptr. + api::BluetoothDevice* FindBluetoothDevice(const std::string& mac_address); + const EnvironmentConfig& GetEnvironmentConfig(); // Registers |callback| to receive messages sent to device with id |self_id|. @@ -116,6 +123,48 @@ class MediumEnvironment { // |peer_id|. void SendWebRtcSignalingMessage(absl::string_view peer_id, const ByteArray& message); + + // Adds medium-related info to allow for scanning/advertising to work. + // This provides acccess to this medium from other mediums, when protocol + // expects they should communicate. + void RegisterBleMedium(api::BleMedium& medium); + + // Updates advertising info to indicate the current medium is exposing + // advertising event. + void UpdateBleMediumForAdvertising(api::BleMedium& medium, + api::BlePeripheral& peripheral, + const std::string& service_id, + bool enabled); + + // Updates discovery callback info to allow for dispatch of discovery events. + // + // Invokes callback asynchronously when any changes happen to discoverable + // devices, or if the defice is turned off, whether or not it is discoverable, + // if it was ever reported as discoverable. + // + // This should be called when discoverable state changes. + // with user-specified callback when discovery is enabled, and with default + // (empty) callback otherwise. + void UpdateBleMediumForScanning(api::BleMedium& medium, + const std::string& service_id, + BleDiscoveredPeripheralCallback callback, + bool enabled); + + // Updates Accepted connection callback info to allow for dispatch of + // advertising events. + void UpdateBleMediumForAcceptedConnection( + api::BleMedium& medium, const std::string& service_id, + BleAcceptedConnectionCallback callback); + + // Removes medium-related info. This should correspond to device power off. + void UnregisterBleMedium(api::BleMedium& medium); + + // Call back when advertising has created the server socket and is ready for + // connect. + void CallBleAcceptedConnectionCallback(api::BleMedium& medium, + api::BleSocket& socket, + const std::string& service_id); + // Adds medium-related info to allow for discovery/advertising to work. // This provides acccess to this medium from other mediums, when protocol // expects they should communicate. @@ -123,9 +172,10 @@ class MediumEnvironment { // Updates advertising info to indicate the current medium is exposing // advertising event. - void UpdateWifiLanMediumForAdvertising( - api::WifiLanMedium& medium, api::WifiLanService& service, - const std::string& service_id, bool enabled); + void UpdateWifiLanMediumForAdvertising(api::WifiLanMedium& medium, + api::WifiLanService& service, + const std::string& service_id, + bool enabled); // Updates discovery callback info to allow for dispatch of discovery events. // @@ -155,6 +205,10 @@ class MediumEnvironment { api::WifiLanSocket& socket, const std::string& service_id); + // Returns WiFi LAN service matching IP address and port, or nullptr. + api::WifiLanService* FindWifiLanService(const std::string& ip_address, + int port); + private: struct BluetoothMediumContext { BluetoothDiscoveryCallback callback; @@ -163,6 +217,13 @@ class MediumEnvironment { absl::flat_hash_map devices; }; + struct BleMediumContext { + BleDiscoveredPeripheralCallback discovery_callback; + BleAcceptedConnectionCallback accepted_connection_callback; + api::BlePeripheral* ble_peripheral = nullptr; + bool advertising = false; + }; + struct WifiLanServiceIdContext { WifiLanDiscoveredServiceCallback discovery_callback; WifiLanAcceptedConnectionCallback accepted_connection_callback; @@ -187,6 +248,10 @@ class MediumEnvironment { api::BluetoothAdapter::ScanMode mode, bool enabled); + void OnBlePeripheralStateChanged(BleMediumContext& info, + api::BlePeripheral& peripheral, + const std::string& service_id, bool enabled); + void OnWifiLanServiceStateChanged(WifiLanMediumContext& info, api::WifiLanService& service, const std::string& service_id, @@ -207,6 +272,8 @@ class MediumEnvironment { absl::flat_hash_map bluetooth_mediums_; + absl::flat_hash_map ble_mediums_; + // Maps peer id to callback for receiving signaling messages. absl::flat_hash_map webrtc_signaling_callback_; diff --git a/cpp/platform_v2/impl/g3/BUILD b/cpp/platform_v2/impl/g3/BUILD index ae64a943..4dd926da 100644 --- a/cpp/platform_v2/impl/g3/BUILD +++ b/cpp/platform_v2/impl/g3/BUILD @@ -39,12 +39,14 @@ cc_library( name = "comm", testonly = True, srcs = [ + "ble.cc", "bluetooth_adapter.cc", "bluetooth_classic.cc", "webrtc.cc", "wifi_lan.cc", ], hdrs = [ + "ble.h", "bluetooth_adapter.h", "bluetooth_classic.h", "webrtc.h", @@ -76,9 +78,7 @@ cc_library( srcs = [ "crypto.cc", ], - visibility = [ - "//platform_v2/g3:__pkg__", - ], + visibility = ["//visibility:private"], deps = [ "//platform_v2/api:types", "//platform_v2/base", @@ -94,7 +94,6 @@ cc_library( "platform.cc", ], visibility = [ - "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", "//core_v2:__subpackages__", "//platform_v2:__subpackages__", ], diff --git a/cpp/platform_v2/impl/g3/ble.cc b/cpp/platform_v2/impl/g3/ble.cc new file mode 100644 index 00000000..9b143494 --- /dev/null +++ b/cpp/platform_v2/impl/g3/ble.cc @@ -0,0 +1,341 @@ +#include "platform_v2/impl/g3/ble.h" + +#include +#include +#include + +#include "platform_v2/api/ble.h" +#include "platform_v2/base/logging.h" +#include "platform_v2/base/medium_environment.h" +#include "absl/synchronization/mutex.h" + +namespace location { +namespace nearby { +namespace g3 { + +BleSocket::~BleSocket() { + absl::MutexLock lock(&mutex_); + DoClose(); +} + +void BleSocket::Connect(BleSocket& other) { + absl::MutexLock lock(&mutex_); + remote_socket_ = &other; + input_ = other.output_; +} + +InputStream& BleSocket::GetInputStream() { + auto* remote_socket = GetRemoteSocket(); + CHECK(remote_socket != nullptr); + return remote_socket->GetLocalInputStream(); +} + +OutputStream& BleSocket::GetOutputStream() { + return GetLocalOutputStream(); +} + +BleSocket* BleSocket::GetRemoteSocket() { + absl::MutexLock lock(&mutex_); + return remote_socket_; +} + +bool BleSocket::IsConnected() const { + absl::MutexLock lock(&mutex_); + return IsConnectedLocked(); +} + +bool BleSocket::IsClosed() const { + absl::MutexLock lock(&mutex_); + return closed_; +} + +Exception BleSocket::Close() { + absl::MutexLock lock(&mutex_); + DoClose(); + return {Exception::kSuccess}; +} + +BlePeripheral* BleSocket::GetRemotePeripheral() { + absl::MutexLock lock(&mutex_); + return peripheral_; +} + +void BleSocket::DoClose() { + if (!closed_) { + remote_socket_ = nullptr; + output_->GetOutputStream().Close(); + output_->GetInputStream().Close(); + if (IsConnectedLocked()) { + input_->GetOutputStream().Close(); + input_->GetInputStream().Close(); + } + closed_ = true; + } +} + +bool BleSocket::IsConnectedLocked() const { return input_ != nullptr; } + +InputStream& BleSocket::GetLocalInputStream() { + absl::MutexLock lock(&mutex_); + return output_->GetInputStream(); +} + +OutputStream& BleSocket::GetLocalOutputStream() { + absl::MutexLock lock(&mutex_); + return output_->GetOutputStream(); +} + +std::unique_ptr BleServerSocket::Accept( + BlePeripheral* peripheral) { + absl::MutexLock lock(&mutex_); + if (closed_) return {}; + while (pending_sockets_.empty()) { + cond_.Wait(&mutex_); + if (closed_) break; + } + if (closed_) return {}; + auto* remote_socket = + pending_sockets_.extract(pending_sockets_.begin()).value(); + CHECK(remote_socket); + auto local_socket = std::make_unique(peripheral); + local_socket->Connect(*remote_socket); + remote_socket->Connect(*local_socket); + cond_.SignalAll(); + return local_socket; +} + +bool BleServerSocket::Connect(BleSocket& socket) { + absl::MutexLock lock(&mutex_); + if (closed_) return false; + if (socket.IsConnected()) { + NEARBY_LOG(ERROR, + "Failed to connect to Ble 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 BleServerSocket::SetCloseNotifier(std::function notifier) { + absl::MutexLock lock(&mutex_); + close_notifier_ = std::move(notifier); +} + +BleServerSocket::~BleServerSocket() { + absl::MutexLock lock(&mutex_); + DoClose(); +} + +Exception BleServerSocket::Close() { + absl::MutexLock lock(&mutex_); + return DoClose(); +} + +Exception BleServerSocket::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}; +} + +BleMedium::BleMedium(api::BluetoothAdapter& adapter) + : adapter_(static_cast(&adapter)) { + adapter_->SetBleMedium(this); + auto& env = MediumEnvironment::Instance(); + env.RegisterBleMedium(*this); +} + +BleMedium::~BleMedium() { + adapter_->SetBleMedium(nullptr); + auto& env = MediumEnvironment::Instance(); + env.UnregisterBleMedium(*this); + + StopAdvertising(advertising_info_.service_id); + StopScanning(scanning_info_.service_id); + + accept_loops_runner_.Shutdown(); + NEARBY_LOG(INFO, "BleMedium dtor advertising_accept_thread_running_ = %d", + acceptance_thread_running_.load()); + // If acceptance thread is still running, wait to finish. + if (acceptance_thread_running_) { + while (acceptance_thread_running_) { + CountDownLatch latch(1); + close_accept_loops_runner_.Execute([&latch]() { latch.CountDown(); }); + latch.Await(); + } + } +} + +bool BleMedium::StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes) { + NEARBY_LOGS(INFO) << "G3 Ble StartAdvertising: service_id=" << service_id + << ", advertisement bytes=" << advertisement_bytes.data() + << "(" << advertisement_bytes.size() << ")"; + auto& env = MediumEnvironment::Instance(); + auto& peripheral = adapter_->GetPeripheral(); + peripheral.SetAdvertisementBytes(service_id, advertisement_bytes); + env.UpdateBleMediumForAdvertising(*this, peripheral, service_id, true); + + absl::MutexLock lock(&mutex_); + if (server_socket_ != nullptr) server_socket_.release(); + server_socket_ = std::make_unique(); + + acceptance_thread_running_.exchange(true); + accept_loops_runner_.Execute([&env, this, service_id]() mutable { + if (!accept_loops_runner_.InShutdown()) { + while (true) { + auto client_socket = + server_socket_->Accept(&(this->adapter_->GetPeripheral())); + if (client_socket == nullptr) break; + env.CallBleAcceptedConnectionCallback(*this, *(client_socket.release()), + service_id); + } + } + acceptance_thread_running_.exchange(false); + }); + advertising_info_.service_id = service_id; + return true; +} + +bool BleMedium::StopAdvertising(const std::string& service_id) { + NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising: service_id=" << service_id; + { + absl::MutexLock lock(&mutex_); + if (advertising_info_.Empty()) { + NEARBY_LOGS(INFO) << "G3 Ble StopAdvertising: Can't stop advertising " + "because we never started advertising."; + return false; + } + advertising_info_.Clear(); + } + + auto& env = MediumEnvironment::Instance(); + env.UpdateBleMediumForAdvertising(*this, adapter_->GetPeripheral(), + service_id, false); + accept_loops_runner_.Shutdown(); + if (server_socket_ == nullptr) { + NEARBY_LOGS(ERROR) << "G3 Ble StopAdvertising: Failed to find Ble Server " + "socket: service_id=" + << service_id; + // Fall through for server socket not found. + return true; + } + + if (!server_socket_->Close().Ok()) { + NEARBY_LOGS(INFO) + << "G3 Ble StopAdvertising: Failed to close Ble server socket for " + << service_id; + return false; + } + return true; +} + +bool BleMedium::StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback) { + NEARBY_LOGS(INFO) << "G3 Ble StartScanning: service_id=" << service_id; + auto& env = MediumEnvironment::Instance(); + env.UpdateBleMediumForScanning(*this, service_id, std::move(callback), true); + { + absl::MutexLock lock(&mutex_); + scanning_info_.service_id = service_id; + } + return true; +} + +bool BleMedium::StopScanning(const std::string& service_id) { + NEARBY_LOGS(INFO) << "G3 Ble StopScanning: service_id=" << service_id; + { + absl::MutexLock lock(&mutex_); + if (scanning_info_.Empty()) { + NEARBY_LOGS(INFO) << "G3 Ble StopDiscovery: Can't stop scanning because " + "we never started scanning."; + return false; + } + scanning_info_.Clear(); + } + + auto& env = MediumEnvironment::Instance(); + env.UpdateBleMediumForScanning(*this, service_id, {}, false); + return true; +} + +bool BleMedium::StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) { + NEARBY_LOGS(INFO) << "G3 Ble StartAcceptingConnections: service_id=" + << service_id; + auto& env = MediumEnvironment::Instance(); + env.UpdateBleMediumForAcceptedConnection(*this, service_id, callback); + return true; +} + +bool BleMedium::StopAcceptingConnections(const std::string& service_id) { + NEARBY_LOGS(INFO) << "G3 Ble StopAcceptingConnections: service_id=" + << service_id; + auto& env = MediumEnvironment::Instance(); + env.UpdateBleMediumForAcceptedConnection(*this, service_id, {}); + return true; +} + +std::unique_ptr BleMedium::Connect( + api::BlePeripheral& remote_peripheral, const std::string& service_id) { + NEARBY_LOG(INFO, + "G3 Ble Connect [self]: medium=%p, adapter=%p, peripheral=%p, " + "service_id=%s", + this, &GetAdapter(), &GetAdapter().GetPeripheral(), + service_id.c_str()); + // First, find an instance of remote medium, that exposed this peripheral. + auto& adapter = static_cast(remote_peripheral).GetAdapter(); + auto* medium = static_cast(adapter.GetBleMedium()); + + if (!medium) return {}; // Can't find medium. Bail out. + + BleServerSocket* remote_server_socket = nullptr; + NEARBY_LOG(INFO, + "G3 Ble Connect [peer]: medium=%p, adapter=%p, peripheral=%p, " + "service_id=%s", + medium, &adapter, &remote_peripheral, service_id.c_str()); + // Then, find our server socket context in this medium. + { + absl::MutexLock medium_lock(&medium->mutex_); + remote_server_socket = medium->server_socket_.get(); + if (remote_server_socket == nullptr) { + NEARBY_LOGS(ERROR) + << "G3 Ble Connect: Failed to find Ble Server socket: service_id=" + << service_id; + return {}; + } + } + + BlePeripheral peripheral = static_cast(remote_peripheral); + auto socket = std::make_unique(&peripheral); + // Finally, Request to connect to this socket. + if (!remote_server_socket->Connect(*socket)) { + NEARBY_LOGS(ERROR) << "G3 Ble Connect: Failed to connect to existing Ble " + "Server socket: service_id=" + << service_id; + return {}; + } + + NEARBY_LOG(INFO, "G3 Ble Connect: connected: socket=%p", socket.get()); + return socket; +} + +} // namespace g3 +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/impl/g3/ble.h b/cpp/platform_v2/impl/g3/ble.h new file mode 100644 index 00000000..5ea80a55 --- /dev/null +++ b/cpp/platform_v2/impl/g3/ble.h @@ -0,0 +1,213 @@ +#ifndef PLATFORM_V2_IMPL_G3_BLE_H_ +#define PLATFORM_V2_IMPL_G3_BLE_H_ + +#include +#include + +#include "platform_v2/api/ble.h" +#include "platform_v2/base/byte_array.h" +#include "platform_v2/base/input_stream.h" +#include "platform_v2/base/output_stream.h" +#include "platform_v2/impl/g3/bluetooth_adapter.h" +#include "platform_v2/impl/g3/bluetooth_classic.h" +#include "platform_v2/impl/g3/multi_thread_executor.h" +#include "platform_v2/impl/g3/pipe.h" +#include "absl/container/flat_hash_map.h" +#include "absl/container/flat_hash_set.h" +#include "absl/strings/escaping.h" +#include "absl/synchronization/mutex.h" + +namespace location { +namespace nearby { +namespace g3 { + +class BleMedium; + +class BleSocket : public api::BleSocket { + public: + BleSocket() = default; + explicit BleSocket(BlePeripheral* peripheral) : peripheral_(peripheral) {} + ~BleSocket() override; + + // Connect to another BleSocket, to form a functional low-level channel. + // from this point on, and until Close is called, connection exists. + void Connect(BleSocket& other) ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns the InputStream of this connected BleSocket. + InputStream& GetInputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns the OutputStream of this connected BleSocket. + // This stream is for local side to write. + OutputStream& GetOutputStream() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns address of a remote BleSocket or nullptr. + BleSocket* 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_); + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() override ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns valid BlePeripheral pointer if there is a connection, and + // nullptr otherwise. + BlePeripheral* GetRemotePeripheral() override + ABSL_LOCKS_EXCLUDED(mutex_); + + private: + void DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + // 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. + std::shared_ptr output_ {new Pipe}; + std::shared_ptr input_; + mutable absl::Mutex mutex_; + BlePeripheral* peripheral_; + BleSocket* remote_socket_ ABSL_GUARDED_BY(mutex_) = nullptr; + bool closed_ ABSL_GUARDED_BY(mutex_) = false; +}; + +class BleServerSocket { + public: + ~BleServerSocket(); + + // 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 BleSocket to the server side. + // If not null, returned socket is connected to its remote (client-side) peer. + std::unique_ptr Accept(BlePeripheral* peripheral) + 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. + // Returns true, if socket is successfully connected. + bool Connect(BleSocket& socket) ABSL_LOCKS_EXCLUDED(mutex_); + + // Called by the server side of a connection before passing ownership of + // BleServerSocker 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() ABSL_LOCKS_EXCLUDED(mutex_); + + private: + Exception DoClose() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); + + absl::Mutex mutex_; + absl::CondVar cond_; + 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 BLE medium. +class BleMedium : public api::BleMedium { + public: + explicit BleMedium(api::BluetoothAdapter& adapter); + ~BleMedium() override; + + // Returns true once the Ble advertising has been initiated. + bool StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes) override + ABSL_LOCKS_EXCLUDED(mutex_); + bool StopAdvertising(const std::string& service_id) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true once the Ble scanning has been initiated. + bool StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true once Ble scanning for service_id is well and truly + // stopped; after this returns, there must be no more invocations of the + // DiscoveredPeripheralCallback passed in to StartScanning() for service_id. + bool StopScanning(const std::string& service_id) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Returns true once Ble socket connection requests to service_id can be + // accepted. + bool StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) + override ABSL_LOCKS_EXCLUDED(mutex_); + bool StopAcceptingConnections(const std::string& service_id) override + ABSL_LOCKS_EXCLUDED(mutex_); + + // Connects to existing remote Ble peripheral. + // + // On success, returns a new BleSocket. + // On error, returns nullptr. + std::unique_ptr Connect( + api::BlePeripheral& remote_peripheral, + const std::string& service_id) override ABSL_LOCKS_EXCLUDED(mutex_); + + BluetoothAdapter& GetAdapter() { return *adapter_; } + + private: + static constexpr int kMaxConcurrentAcceptLoops = 5; + + struct AdvertisingInfo { + bool Empty() const { return service_id.empty(); } + void Clear() { service_id.clear(); } + + std::string service_id; + }; + + struct ScanningInfo { + bool Empty() const { return service_id.empty(); } + void Clear() { service_id.clear(); } + + std::string service_id; + }; + + absl::Mutex mutex_; + BluetoothAdapter* adapter_; // Our device adapter; read-only. + + // A thread pool dedicated to running all the accept loops from + // StartAdvertising(). + MultiThreadExecutor accept_loops_runner_{kMaxConcurrentAcceptLoops}; + std::atomic_bool acceptance_thread_running_ = false; + + // A thread pool dedicated to wait to complete the accept_loops_runner_. + MultiThreadExecutor close_accept_loops_runner_{kMaxConcurrentAcceptLoops}; + + // A server socket is established when start advertising. + std::unique_ptr server_socket_; + AdvertisingInfo advertising_info_ ABSL_GUARDED_BY(mutex_); + ScanningInfo scanning_info_ ABSL_GUARDED_BY(mutex_); +}; + +} // namespace g3 +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_IMPL_G3_BLE_H_ diff --git a/cpp/platform_v2/impl/g3/bluetooth_adapter.cc b/cpp/platform_v2/impl/g3/bluetooth_adapter.cc index 748513b7..877747ee 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_adapter.cc +++ b/cpp/platform_v2/impl/g3/bluetooth_adapter.cc @@ -3,21 +3,57 @@ #include #include "platform_v2/base/medium_environment.h" +#include "platform_v2/base/prng.h" #include "platform_v2/impl/g3/bluetooth_classic.h" namespace location { namespace nearby { namespace g3 { +BlePeripheral::BlePeripheral(BluetoothAdapter* adapter) : adapter_(*adapter) {} + +std::string BlePeripheral::GetName() const { return adapter_.GetName(); } + +ByteArray BlePeripheral::GetAdvertisementBytes( + const std::string& service_id) const { + return advertisement_bytes_; +} + +void BlePeripheral::SetAdvertisementBytes( + const std::string& service_id, const ByteArray& advertisement_bytes) { + advertisement_bytes_ = advertisement_bytes; +} + BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter) : adapter_(*adapter) {} std::string BluetoothDevice::GetName() const { return adapter_.GetName(); } +std::string BluetoothDevice::GetMacAddress() const { + return adapter_.GetMacAddress(); +} + +BluetoothAdapter::BluetoothAdapter() { + std::string mac_address; + mac_address.resize(6); + int64_t raw_mac_addr = Prng().NextInt64(); + mac_address[0] = static_cast(raw_mac_addr >> 40); + mac_address[1] = static_cast(raw_mac_addr >> 32); + mac_address[2] = static_cast(raw_mac_addr >> 24); + mac_address[3] = static_cast(raw_mac_addr >> 16); + mac_address[4] = static_cast(raw_mac_addr >> 8); + mac_address[5] = static_cast(raw_mac_addr >> 0); + SetMacAddress(mac_address); +} BluetoothAdapter::~BluetoothAdapter() { SetStatus(Status::kDisabled); } -void BluetoothAdapter::SetMedium(api::BluetoothClassicMedium* medium) { - medium_ = medium; +void BluetoothAdapter::SetBluetoothClassicMedium( + api::BluetoothClassicMedium* medium) { + bluetooth_classic_medium_ = medium; +} + +void BluetoothAdapter::SetBleMedium(api::BleMedium* medium) { + ble_medium_ = medium; } bool BluetoothAdapter::SetStatus(Status status) { diff --git a/cpp/platform_v2/impl/g3/bluetooth_adapter.h b/cpp/platform_v2/impl/g3/bluetooth_adapter.h index 8ce2b719..71220a9a 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_adapter.h +++ b/cpp/platform_v2/impl/g3/bluetooth_adapter.h @@ -3,6 +3,7 @@ #include +#include "platform_v2/api/ble.h" #include "platform_v2/api/bluetooth_adapter.h" #include "platform_v2/api/bluetooth_classic.h" #include "platform_v2/impl/g3/single_thread_executor.h" @@ -17,6 +18,28 @@ namespace g3 { // BluetoothDevice and BluetoothAdapter have a mutual dependency. class BluetoothAdapter; +// Opaque wrapper over a Ble peripheral. Must contain enough data about a +// particular Ble device to connect to its GATT server. +class BlePeripheral : public api::BlePeripheral { + public: + ~BlePeripheral() override = default; + + std::string GetName() const override; + ByteArray GetAdvertisementBytes(const std::string& service_id) const override; + void SetAdvertisementBytes(const std::string& service_id, + const ByteArray& advertisement_bytes); + BluetoothAdapter& GetAdapter() { return adapter_; } + + private: + // Only BluetoothAdapter may instantiate BlePeripheral. + friend class BluetoothAdapter; + + explicit BlePeripheral(BluetoothAdapter* adapter); + + BluetoothAdapter& adapter_; + ByteArray advertisement_bytes_; +}; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. class BluetoothDevice : public api::BluetoothDevice { public: @@ -24,6 +47,7 @@ class BluetoothDevice : public api::BluetoothDevice { // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() std::string GetName() const override; + std::string GetMacAddress() const override; BluetoothAdapter& GetAdapter() { return adapter_; } private: @@ -41,7 +65,7 @@ class BluetoothAdapter : public api::BluetoothAdapter { using Status = api::BluetoothAdapter::Status; using ScanMode = api::BluetoothAdapter::ScanMode; - explicit BluetoothAdapter() = default; + BluetoothAdapter(); ~BluetoothAdapter() override; // Synchronously sets the status of the BluetoothAdapter to 'status', and @@ -68,15 +92,30 @@ class BluetoothAdapter : public api::BluetoothAdapter { // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#setName(java.lang.String) bool SetName(absl::string_view name) override ABSL_LOCKS_EXCLUDED(mutex_); + // Returns BT MAC address assigned to this adapter. + std::string GetMacAddress() const override { return mac_address_; } + BluetoothDevice& GetDevice() { return device_; } - void SetMedium(api::BluetoothClassicMedium* medium); - api::BluetoothClassicMedium* GetMedium() { return medium_; } + void SetBluetoothClassicMedium(api::BluetoothClassicMedium* medium); + api::BluetoothClassicMedium* GetBluetoothClassicMedium() { + return bluetooth_classic_medium_; + } + + BlePeripheral& GetPeripheral() { return peripheral_; } + + void SetBleMedium(api::BleMedium* medium); + api::BleMedium* GetBleMedium() { return ble_medium_; } + + void SetMacAddress(std::string& mac_address) { mac_address_ = mac_address; } private: mutable absl::Mutex mutex_; BluetoothDevice device_{this}; - api::BluetoothClassicMedium* medium_ = nullptr; + BlePeripheral peripheral_{this}; + api::BluetoothClassicMedium* bluetooth_classic_medium_ = nullptr; + api::BleMedium* ble_medium_ = nullptr; + std::string mac_address_; 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 index f0226452..a0c040d3 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_classic.cc +++ b/cpp/platform_v2/impl/g3/bluetooth_classic.cc @@ -34,9 +34,7 @@ bool BluetoothSocket::IsClosed() const { return closed_; } -bool BluetoothSocket::IsConnectedLocked() const { - return input_ != nullptr; -} +bool BluetoothSocket::IsConnectedLocked() const { return input_ != nullptr; } InputStream& BluetoothSocket::GetInputStream() { auto* remote_socket = GetRemoteSocket(); @@ -163,13 +161,13 @@ Exception BluetoothServerSocket::DoClose() { BluetoothClassicMedium::BluetoothClassicMedium(api::BluetoothAdapter& adapter) // TODO(apolyudov): implement and use downcast<> with static assertions. : adapter_(static_cast(&adapter)) { - adapter_->SetMedium(this); + adapter_->SetBluetoothClassicMedium(this); auto& env = MediumEnvironment::Instance(); env.RegisterBluetoothMedium(*this, GetAdapter()); } BluetoothClassicMedium::~BluetoothClassicMedium() { - adapter_->SetMedium(nullptr); + adapter_->SetBluetoothClassicMedium(nullptr); auto& env = MediumEnvironment::Instance(); env.UnregisterBluetoothMedium(*this); } @@ -193,7 +191,8 @@ std::unique_ptr BluetoothClassicMedium::ConnectToService( 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()); + auto* medium = + static_cast(adapter.GetBluetoothClassicMedium()); if (!medium) return {}; // Adapter is not bound to medium. Bail out. @@ -241,6 +240,12 @@ BluetoothClassicMedium::ListenForService(const std::string& service_name, return socket; } +api::BluetoothDevice* BluetoothClassicMedium::FindRemoteDevice( + const std::string& mac_address) { + auto& env = MediumEnvironment::Instance(); + return env.FindBluetoothDevice(mac_address); +} + } // 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 index ede548b7..8d199863 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_classic.h +++ b/cpp/platform_v2/impl/g3/bluetooth_classic.h @@ -82,7 +82,7 @@ class BluetoothSocket : public api::BluetoothSocket { // 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. - std::shared_ptr output_ {new Pipe}; + std::shared_ptr output_{new Pipe}; std::shared_ptr input_; mutable absl::Mutex mutex_; BluetoothAdapter* adapter_ = nullptr; // Our Adapter. Read only. @@ -207,6 +207,9 @@ class BluetoothClassicMedium : public api::BluetoothClassicMedium { const std::string& service_name, const std::string& service_uuid) override ABSL_LOCKS_EXCLUDED(mutex_); + api::BluetoothDevice* FindRemoteDevice( + const std::string& mac_address) override; + private: absl::Mutex mutex_; BluetoothAdapter* adapter_; // Our device adapter; read-only. diff --git a/cpp/platform_v2/impl/g3/platform.cc b/cpp/platform_v2/impl/g3/platform.cc index 31d17c6a..8392e7d2 100644 --- a/cpp/platform_v2/impl/g3/platform.cc +++ b/cpp/platform_v2/impl/g3/platform.cc @@ -5,7 +5,6 @@ #include "platform_v2/api/atomic_boolean.h" #include "platform_v2/api/atomic_reference.h" -#include "platform_v2/api/ble.h" #include "platform_v2/api/ble_v2.h" #include "platform_v2/api/bluetooth_adapter.h" #include "platform_v2/api/bluetooth_classic.h" @@ -21,6 +20,7 @@ #include "platform_v2/base/medium_environment.h" #include "platform_v2/impl/g3/atomic_boolean.h" #include "platform_v2/impl/g3/atomic_reference.h" +#include "platform_v2/impl/g3/ble.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" @@ -112,7 +112,7 @@ ImplementationPlatform::CreateBluetoothClassicMedium( std::unique_ptr ImplementationPlatform::CreateBleMedium( api::BluetoothAdapter& adapter) { - return std::unique_ptr(); + return absl::make_unique(adapter); } std::unique_ptr ImplementationPlatform::CreateBleV2Medium( diff --git a/cpp/platform_v2/impl/g3/wifi_lan.cc b/cpp/platform_v2/impl/g3/wifi_lan.cc index e310c76d..9afb97d5 100644 --- a/cpp/platform_v2/impl/g3/wifi_lan.cc +++ b/cpp/platform_v2/impl/g3/wifi_lan.cc @@ -7,6 +7,7 @@ #include "platform_v2/api/wifi_lan.h" #include "platform_v2/base/logging.h" #include "platform_v2/base/medium_environment.h" +#include "platform_v2/base/prng.h" #include "absl/synchronization/mutex.h" namespace location { @@ -85,7 +86,8 @@ OutputStream& WifiLanSocket::GetLocalOutputStream() { return output_->GetOutputStream(); } -std::unique_ptr WifiLanServerSocket::Accept() { +std::unique_ptr WifiLanServerSocket::Accept( + WifiLanService* service) { absl::MutexLock lock(&mutex_); if (closed_) return {}; while (pending_sockets_.empty()) { @@ -96,7 +98,7 @@ std::unique_ptr WifiLanServerSocket::Accept() { auto* remote_socket = pending_sockets_.extract(pending_sockets_.begin()).value(); CHECK(remote_socket); - auto local_socket = std::make_unique(); + auto local_socket = std::make_unique(service); local_socket->Connect(*remote_socket); remote_socket->Connect(*local_socket); cond_.SignalAll(); @@ -155,6 +157,15 @@ Exception WifiLanServerSocket::DoClose() { WifiLanMedium::WifiLanMedium() { service_.SetMedium(this); + std::string ip_address; + ip_address.resize(4); + uint32_t raw_ip_addr = Prng().NextUint32(); + uint16_t port = Prng().NextUint32(); + ip_address[0] = static_cast(raw_ip_addr >> 24); + ip_address[1] = static_cast(raw_ip_addr >> 16); + ip_address[2] = static_cast(raw_ip_addr >> 8); + ip_address[3] = static_cast(raw_ip_addr >> 0); + service_.SetServiceAddress(ip_address, port); auto& env = MediumEnvironment::Instance(); env.RegisterWifiLanMedium(*this); } @@ -167,8 +178,7 @@ WifiLanMedium::~WifiLanMedium() { StopAdvertising(advertising_info_.service_id); StopDiscovery(discovering_info_.service_id); - NEARBY_LOG(INFO, - "WifiLanMedium dtor advertising_accept_thread_running_ = %d", + NEARBY_LOG(INFO, "WifiLanMedium dtor advertising_accept_thread_running_ = %d", acceptance_thread_running_.load()); // If acceptance thread is still running, wait to finish. if (acceptance_thread_running_) { @@ -186,6 +196,7 @@ bool WifiLanMedium::StartAdvertising(const std::string& service_id, "G3 WifiLan StartAdvertising: service_id=%s, service_info_name=%s", service_id.c_str(), service_info_name.c_str()); auto& env = MediumEnvironment::Instance(); + service_.SetName(service_info_name); env.UpdateWifiLanMediumForAdvertising(*this, service_, service_id, true); absl::MutexLock lock(&mutex_); @@ -196,10 +207,10 @@ bool WifiLanMedium::StartAdvertising(const std::string& service_id, accept_loops_runner_.Execute([&env, this, service_id]() mutable { if (!accept_loops_runner_.InShutdown()) { while (true) { - auto client_socket = server_socket_->Accept(); + auto client_socket = server_socket_->Accept(&service_); if (client_socket == nullptr) break; - env.CallWifiLanAcceptedConnectionCallback(*this, *client_socket, - service_id); + env.CallWifiLanAcceptedConnectionCallback( + *this, *(client_socket.release()), service_id); } } acceptance_thread_running_.exchange(false); @@ -227,8 +238,8 @@ bool WifiLanMedium::StopAdvertising(const std::string& service_id) { accept_loops_runner_.Shutdown(); if (server_socket_ == nullptr) { NEARBY_LOGS(ERROR) << "G3 WifiLan StopAdvertising: failed to find WifiLan " - "Server socket: service_id=" - << service_id; + "Server socket: service_id=" + << service_id; // Fall through for server socket not found. return true; } @@ -296,8 +307,11 @@ bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) { std::unique_ptr WifiLanMedium::Connect( api::WifiLanService& remote_service, const std::string& service_id) { - NEARBY_LOG(INFO, "G3 WifiLan Connect: medium=%p, service=%p, service_id=%s", - this, &service_, service_id.c_str()); + NEARBY_LOG(INFO, + "G3 WifiLan Connect: medium=%p, service=%p, service_info_name=%s, " + "service_id=%s", + this, &service_, remote_service.GetName().c_str(), + service_id.c_str()); // First, find an instance of remote medium, that exposed this service. auto* medium = static_cast(remote_service).GetMedium(); @@ -305,8 +319,10 @@ std::unique_ptr WifiLanMedium::Connect( WifiLanServerSocket* remote_server_socket = nullptr; NEARBY_LOG(INFO, - "G3 WifiLan Connect [peer]: medium=%p, service=%p, service_id=%s", - medium, &remote_service, service_id.c_str()); + "G3 WifiLan Connect [peer]: medium=%p, service=%p, " + "service_info_name=%s, service_id=%s", + medium, &remote_service, remote_service.GetName().c_str(), + service_id.c_str()); // Then, find our server socket context in this medium. { absl::MutexLock medium_lock(&medium->mutex_); @@ -321,7 +337,8 @@ std::unique_ptr WifiLanMedium::Connect( } } - auto socket = std::make_unique(); + WifiLanService service = static_cast(remote_service); + auto socket = std::make_unique(&service); // Finally, Request to connect to this socket. if (!remote_server_socket->Connect(*socket)) { NEARBY_LOG(ERROR, @@ -335,6 +352,12 @@ std::unique_ptr WifiLanMedium::Connect( return socket; } +api::WifiLanService* WifiLanMedium::FindRemoteService( + const std::string& ip_address, int port) { + auto& env = MediumEnvironment::Instance(); + return env.FindWifiLanService(ip_address, port); +} + } // namespace g3 } // namespace nearby } // namespace location diff --git a/cpp/platform_v2/impl/g3/wifi_lan.h b/cpp/platform_v2/impl/g3/wifi_lan.h index 7bc0e0dd..c6aa8292 100644 --- a/cpp/platform_v2/impl/g3/wifi_lan.h +++ b/cpp/platform_v2/impl/g3/wifi_lan.h @@ -3,6 +3,7 @@ #include #include +#include #include "platform_v2/api/wifi_lan.h" #include "platform_v2/base/byte_array.h" @@ -32,13 +33,23 @@ class WifiLanService : public api::WifiLanService { service_info_name_ = std::move(service_info_name); } std::string GetName() const override { return service_info_name_; } + std::pair GetServiceAddress() const override { + return std::make_pair(ip_address_, port_); + } void SetMedium(WifiLanMedium* medium) { medium_ = medium; } WifiLanMedium* GetMedium() { return medium_; } + void SetServiceAddress(const std::string& ip_address, int port) { + ip_address_ = ip_address; + port_ = port; + } + private: std::string service_info_name_; WifiLanMedium* medium_ = nullptr; + std::string ip_address_; + int port_; }; class WifiLanSocket : public api::WifiLanSocket { @@ -94,7 +105,7 @@ class WifiLanSocket : public api::WifiLanSocket { // 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. - std::shared_ptr output_ {new Pipe}; + std::shared_ptr output_{new Pipe}; std::shared_ptr input_; mutable absl::Mutex mutex_; WifiLanService* service_; @@ -116,7 +127,8 @@ class WifiLanServerSocket { // Called by the server side of a connection. // Returns WifiLanSocket to the server side. // If not null, returned socket is connected to its remote (client-side) peer. - std::unique_ptr Accept() ABSL_LOCKS_EXCLUDED(mutex_); + std::unique_ptr Accept(WifiLanService* service) + ABSL_LOCKS_EXCLUDED(mutex_); // Blocks until either: // - connection is available, or @@ -186,6 +198,9 @@ class WifiLanMedium : public api::WifiLanMedium { api::WifiLanService& remote_service, const std::string& service_id) override ABSL_LOCKS_EXCLUDED(mutex_); + api::WifiLanService* FindRemoteService(const std::string& ip_address, + int port) override; + private: static constexpr int kMaxConcurrentAcceptLoops = 5; diff --git a/cpp/platform_v2/impl/shared/BUILD b/cpp/platform_v2/impl/shared/BUILD index 9787bf31..87115901 100644 --- a/cpp/platform_v2/impl/shared/BUILD +++ b/cpp/platform_v2/impl/shared/BUILD @@ -20,9 +20,7 @@ cc_library( hdrs = [ "posix_condition_variable.h", ], - visibility = [ - "//platform_v2/impl:__subpackages__", - ], + visibility = ["//visibility:private"], deps = [ ":posix_mutex", "//platform_v2/api:types", diff --git a/cpp/platform_v2/public/BUILD b/cpp/platform_v2/public/BUILD index 59902e1e..6bd9ad66 100644 --- a/cpp/platform_v2/public/BUILD +++ b/cpp/platform_v2/public/BUILD @@ -45,10 +45,12 @@ cc_library( cc_library( name = "comm", srcs = [ + "ble.cc", "bluetooth_classic.cc", "wifi_lan.cc", ], hdrs = [ + "ble.h", "bluetooth_adapter.h", "bluetooth_classic.h", "webrtc.h", @@ -91,6 +93,7 @@ cc_test( srcs = [ "atomic_boolean_test.cc", "atomic_reference_test.cc", + "ble_test.cc", "bluetooth_adapter_test.cc", "bluetooth_classic_test.cc", "cancelable_alarm_test.cc", @@ -115,6 +118,7 @@ cc_test( "//platform_v2/base:test_util", "//platform_v2/impl/g3", # build_cleaner: keep "//testing/base/public:gunit_main", + "//absl/strings", "//absl/synchronization", "//absl/time", ], diff --git a/cpp/platform_v2/public/ble.cc b/cpp/platform_v2/public/ble.cc new file mode 100644 index 00000000..7161eb7e --- /dev/null +++ b/cpp/platform_v2/public/ble.cc @@ -0,0 +1,127 @@ +#include "platform_v2/public/ble.h" + +#include "platform_v2/public/logging.h" +#include "platform_v2/public/mutex_lock.h" + +namespace location { +namespace nearby { + +bool BleMedium::StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes) { + return impl_->StartAdvertising(service_id, advertisement_bytes); +} + +bool BleMedium::StopAdvertising(const std::string& service_id) { + return impl_->StopAdvertising(service_id); +} + +bool BleMedium::StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback) { + { + MutexLock lock(&mutex_); + discovered_peripheral_callback_ = std::move(callback); + peripherals_.clear(); + } + return impl_->StartScanning( + service_id, + { + .peripheral_discovered_cb = + [this](api::BlePeripheral& peripheral, + const std::string& service_id) { + MutexLock lock(&mutex_); + auto pair = peripherals_.emplace( + &peripheral, absl::make_unique()); + auto& context = *pair.first->second; + if (!pair.second) { + NEARBY_LOG(INFO, + "Discovering (again) peripheral=%p, impl=%p, " + "peripheral name=%s", + &context.peripheral, &peripheral, + peripheral.GetName().c_str()); + } else { + context.peripheral = BlePeripheral(&peripheral); + NEARBY_LOG(INFO, + "Discovering peripheral=%p, impl=%p, " + "peripheral name=%s", + &context.peripheral, &peripheral, + peripheral.GetName().c_str()); + discovered_peripheral_callback_.peripheral_discovered_cb( + context.peripheral, service_id); + } + }, + .peripheral_lost_cb = + [this](api::BlePeripheral& peripheral, + const std::string& service_id) { + MutexLock lock(&mutex_); + if (peripherals_.empty()) return; + auto context = peripherals_.find(&peripheral); + if (context == peripherals_.end()) return; + NEARBY_LOG(INFO, "Removing peripheral=%p, impl=%p", + &(context->second->peripheral), &peripheral); + discovered_peripheral_callback_.peripheral_lost_cb( + context->second->peripheral, service_id); + }, + }); +} + +bool BleMedium::StopScanning(const std::string& service_id) { + { + MutexLock lock(&mutex_); + discovered_peripheral_callback_ = {}; + peripherals_.clear(); + NEARBY_LOG(INFO, "Ble Scanning disabled: impl=%p", &GetImpl()); + } + return impl_->StopScanning(service_id); +} + +bool BleMedium::StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback) { + { + MutexLock lock(&mutex_); + accepted_connection_callback_ = std::move(callback); + } + return impl_->StartAcceptingConnections( + service_id, + { + .accepted_cb = + [this](api::BleSocket& socket, const std::string& service_id) { + MutexLock lock(&mutex_); + auto pair = sockets_.emplace( + &socket, absl::make_unique()); + auto& context = *pair.first->second; + if (!pair.second) { + NEARBY_LOG(INFO, "Accepting (again) socket=%p, impl=%p", + &context.socket, &socket); + } else { + context.socket = BleSocket(&socket); + NEARBY_LOG(INFO, "Accepting socket=%p, impl=%p", + &context.socket, &socket); + } + accepted_connection_callback_.accepted_cb(context.socket, + service_id); + }, + }); +} + +bool BleMedium::StopAcceptingConnections(const std::string& service_id) { + { + MutexLock lock(&mutex_); + accepted_connection_callback_ = {}; + sockets_.clear(); + NEARBY_LOG(INFO, "Ble accepted connection disabled: impl=%p", &GetImpl()); + } + return impl_->StopAcceptingConnections(service_id); +} + +BleSocket BleMedium::Connect(BlePeripheral& peripheral, + const std::string& service_id) { + { + MutexLock lock(&mutex_); + NEARBY_LOG(INFO, "BleMedium::Connect: peripheral=%p [impl=%p]", &peripheral, + &peripheral.GetImpl()); + } + return BleSocket(impl_->Connect(peripheral.GetImpl(), service_id)); +} + +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/public/ble.h b/cpp/platform_v2/public/ble.h new file mode 100644 index 00000000..5cb89f08 --- /dev/null +++ b/cpp/platform_v2/public/ble.h @@ -0,0 +1,146 @@ +#ifndef PLATFORM_V2_PUBLIC_BLE_H_ +#define PLATFORM_V2_PUBLIC_BLE_H_ + +#include "platform_v2/api/ble.h" +#include "platform_v2/api/platform.h" +#include "platform_v2/base/byte_array.h" +#include "platform_v2/base/input_stream.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 { + +class BleSocket final { + public: + BleSocket() = default; + BleSocket(const BleSocket&) = default; + BleSocket& operator=(const BleSocket&) = default; + explicit BleSocket(api::BleSocket* socket) : impl_(socket) {} + explicit BleSocket(std::unique_ptr socket) + : impl_(socket.release()) {} + ~BleSocket() = default; + + // Returns the InputStream of the BleSocket. + // On error, returned stream will report Exception::kIo on any operation. + // + // The returned object is not owned by the caller, and can be invalidated once + // the BleSocket object is destroyed. + InputStream& GetInputStream() { return impl_->GetInputStream(); } + + // Returns the OutputStream of the BleSocket. + // On error, returned stream will report Exception::kIo on any operation. + // + // The returned object is not owned by the caller, and can be invalidated once + // the BleSocket object is destroyed. + OutputStream& GetOutputStream() { return impl_->GetOutputStream(); } + + // Returns Exception::kIo on error, Exception::kSuccess otherwise. + Exception Close() { return impl_->Close(); } + + BlePeripheral GetRemotePeripheral() { + return BlePeripheral(impl_->GetRemotePeripheral()); + } + + // 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 BleMedium::Connect + // 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 BleSocket object is + // itself valid. Typically BleSocket lifetime matches duration of the + // connection, and is controlled by end user, since they hold the instance. + api::BleSocket& GetImpl() { return *impl_; } + + private: + std::shared_ptr impl_; +}; + +// Container of operations that can be performed over the BLE medium. +class BleMedium final { + public: + using Platform = api::ImplementationPlatform; + struct DiscoveredPeripheralCallback { + std::function + peripheral_discovered_cb = + DefaultCallback(); + std::function + peripheral_lost_cb = + DefaultCallback(); + }; + struct ScanningInfo { + BlePeripheral peripheral; + }; + + struct AcceptedConnectionCallback { + std::function + accepted_cb = DefaultCallback(); + }; + struct AcceptedConnectionInfo { + BleSocket socket; + }; + + explicit BleMedium(BluetoothAdapter& adapter) + : impl_(Platform::CreateBleMedium(adapter.GetImpl())), + adapter_(adapter) {} + ~BleMedium() = default; + + // Returns true once the BLE advertising has been initiated. + bool StartAdvertising(const std::string& service_id, + const ByteArray& advertisement_bytes); + bool StopAdvertising(const std::string& service_id); + + // Returns true once the BLE scan has been initiated. + bool StartScanning(const std::string& service_id, + DiscoveredPeripheralCallback callback); + + // Returns true once BLE scanning for service_id is well and truly stopped; + // after this returns, there must be no more invocations of the + // DiscoveredPeripheralCallback passed in to StartScanning() for service_id. + bool StopScanning(const std::string& service_id); + + // Returns true once BLE socket connection requests to service_id can be + // accepted. + bool StartAcceptingConnections(const std::string& service_id, + AcceptedConnectionCallback callback); + bool StopAcceptingConnections(const std::string& service_id); + + // Returns a new BleSocket. On Success, BleSocket::IsValid() + // returns true. + BleSocket Connect(BlePeripheral& peripheral, const std::string& service_id); + + bool IsValid() const { return impl_ != nullptr; } + + api::BleMedium& GetImpl() { return *impl_; } + BluetoothAdapter& GetAdapter() { return adapter_; } + + private: + Mutex mutex_; + std::unique_ptr impl_; + BluetoothAdapter& adapter_; + absl::flat_hash_map> + peripherals_ ABSL_GUARDED_BY(mutex_); + absl::flat_hash_map> + sockets_ ABSL_GUARDED_BY(mutex_); + DiscoveredPeripheralCallback discovered_peripheral_callback_ + ABSL_GUARDED_BY(mutex_); + AcceptedConnectionCallback accepted_connection_callback_ + ABSL_GUARDED_BY(mutex_); +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_PUBLIC_BLE_H_ diff --git a/cpp/platform_v2/public/ble_test.cc b/cpp/platform_v2/public/ble_test.cc new file mode 100644 index 00000000..d1fcf653 --- /dev/null +++ b/cpp/platform_v2/public/ble_test.cc @@ -0,0 +1,189 @@ +#include "platform_v2/public/ble.h" + +#include + +#include "platform_v2/base/medium_environment.h" +#include "platform_v2/public/count_down_latch.h" +#include "platform_v2/public/logging.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace location { +namespace nearby { +namespace { + +constexpr absl::Duration kWaitDuration = absl::Milliseconds(1000); +constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; +constexpr absl::string_view kAdvertisementString{"\x0a\x0b\x0c\x0d"}; + +class BleMediumTest : public ::testing::Test { + protected: + using DiscoveredPeripheralCallback = BleMedium::DiscoveredPeripheralCallback; + using AcceptedConnectionCallback = BleMedium::AcceptedConnectionCallback; + + BleMediumTest() { env_.Stop(); } + + MediumEnvironment& env_{MediumEnvironment::Instance()}; +}; + +TEST_F(BleMediumTest, ConstructorDestructorWorks) { + env_.Start(); + BluetoothAdapter adapter_a_; + BluetoothAdapter adapter_b_; + BleMedium ble_a{adapter_a_}; + BleMedium ble_b{adapter_b_}; + + // Make sure we can create functional mediums. + ASSERT_TRUE(ble_a.IsValid()); + ASSERT_TRUE(ble_b.IsValid()); + + // Make sure we can create 2 distinct mediums. + EXPECT_NE(&ble_a.GetImpl(), &ble_b.GetImpl()); + env_.Stop(); +} + +TEST_F(BleMediumTest, CanStartAdvertising) { + env_.Start(); + BluetoothAdapter adapter_a_; + BluetoothAdapter adapter_b_; + BleMedium ble_a{adapter_a_}; + BleMedium ble_b{adapter_b_}; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch found_latch(1); + + ble_a.StartAdvertising(service_id, advertisement_bytes); + + EXPECT_TRUE(ble_b.StartScanning( + service_id, DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BlePeripheral& peripheral, + const std::string& service_id) { + found_latch.CountDown(); + }, + })); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopAdvertising(service_id)); + EXPECT_TRUE(ble_b.StopScanning(service_id)); + env_.Stop(); +} + +TEST_F(BleMediumTest, CanStartScanning) { + env_.Start(); + BluetoothAdapter adapter_a_; + BluetoothAdapter adapter_b_; + BleMedium ble_a{adapter_a_}; + BleMedium ble_b{adapter_b_}; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + + ble_a.StartScanning(service_id, + DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BlePeripheral& peripheral, + const std::string& service_id) { + found_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch](BlePeripheral& peripheral, + const std::string& service_id) { + lost_latch.CountDown(); + }, + }); + EXPECT_TRUE(ble_b.StartAdvertising(service_id, advertisement_bytes)); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_b.StopAdvertising(service_id)); + EXPECT_TRUE(lost_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning(service_id)); + env_.Stop(); +} + +TEST_F(BleMediumTest, CanStopDiscovery) { + env_.Start(); + BluetoothAdapter adapter_a_; + BluetoothAdapter adapter_b_; + BleMedium ble_a{adapter_a_}; + BleMedium ble_b{adapter_b_}; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch found_latch(1); + CountDownLatch lost_latch(1); + + ble_a.StartScanning(service_id, + DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch](BlePeripheral& peripheral, + const std::string& service_id) { + found_latch.CountDown(); + }, + .peripheral_lost_cb = + [&lost_latch](BlePeripheral& peripheral, + const std::string& service_id) { + lost_latch.CountDown(); + }, + }); + EXPECT_TRUE(ble_b.StartAdvertising(service_id, advertisement_bytes)); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(ble_a.StopScanning(service_id)); + EXPECT_TRUE(ble_b.StopAdvertising(service_id)); + EXPECT_FALSE(lost_latch.Await(kWaitDuration).result()); + env_.Stop(); +} + +TEST_F(BleMediumTest, CanStartAcceptingConnectionsAndConnect) { + env_.Start(); + BluetoothAdapter adapter_a_; + BluetoothAdapter adapter_b_; + BleMedium ble_a{adapter_a_}; + BleMedium ble_b{adapter_b_}; + std::string service_id(kServiceID); + ByteArray advertisement_bytes{std::string(kAdvertisementString)}; + CountDownLatch found_latch(1); + CountDownLatch accepted_latch(1); + + BlePeripheral* discovered_peripheral = nullptr; + ble_a.StartScanning( + service_id, + DiscoveredPeripheralCallback{ + .peripheral_discovered_cb = + [&found_latch, &discovered_peripheral]( + BlePeripheral& peripheral, const std::string& service_id) { + NEARBY_LOG(INFO, "Peripheral discovered: %s, %p", + peripheral.GetName().c_str(), &peripheral); + discovered_peripheral = &peripheral; + found_latch.CountDown(); + }, + }); + ble_b.StartAdvertising(service_id, advertisement_bytes); + ble_b.StartAcceptingConnections( + service_id, + AcceptedConnectionCallback{ + .accepted_cb = [&accepted_latch](BleSocket socket, + const std::string& service_id) { + NEARBY_LOG(INFO, "Connection accepted: socket=%p, service_id=%s", + &socket, service_id.c_str()); + accepted_latch.CountDown(); + }}); + EXPECT_TRUE(found_latch.Await(kWaitDuration).result()); + + BleSocket socket_a; + EXPECT_FALSE(socket_a.IsValid()); + { + SingleThreadExecutor client_executor; + client_executor.Execute( + [&ble_a, &socket_a, discovered_peripheral, &service_id]() { + socket_a = ble_a.Connect(*discovered_peripheral, service_id); + }); + } + EXPECT_TRUE(accepted_latch.Await(kWaitDuration).result()); + EXPECT_TRUE(socket_a.IsValid()); + ble_b.StopAdvertising(service_id); + ble_a.StopScanning(service_id); + env_.Stop(); +} + +} // namespace +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/public/bluetooth_adapter.h b/cpp/platform_v2/public/bluetooth_adapter.h index beaaf4d3..1baa6751 100644 --- a/cpp/platform_v2/public/bluetooth_adapter.h +++ b/cpp/platform_v2/public/bluetooth_adapter.h @@ -11,6 +11,29 @@ namespace location { namespace nearby { +// Opaque wrapper over a BLE peripheral. Must contain enough data about a +// particular BLE peripheral to connect to its GATT server. +class BlePeripheral final { + public: + BlePeripheral() = default; + BlePeripheral(const BlePeripheral&) = default; + BlePeripheral& operator=(const BlePeripheral&) = default; + explicit BlePeripheral(api::BlePeripheral* peripheral) : impl_(peripheral) {} + ~BlePeripheral() = default; + + std::string GetName() const { return impl_->GetName(); } + + ByteArray GetAdvertisementBytes(const std::string& service_id) const { + return impl_->GetAdvertisementBytes(service_id); + } + + api::BlePeripheral& GetImpl() { return *impl_; } + bool IsValid() const { return impl_ != nullptr; } + + private: + api::BlePeripheral* impl_; +}; + // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. class BluetoothDevice final { public: diff --git a/cpp/platform_v2/public/bluetooth_classic.h b/cpp/platform_v2/public/bluetooth_classic.h index 459d74b1..420e0684 100644 --- a/cpp/platform_v2/public/bluetooth_classic.h +++ b/cpp/platform_v2/public/bluetooth_classic.h @@ -187,6 +187,9 @@ class BluetoothClassicMedium final { api::BluetoothClassicMedium& GetImpl() { return *impl_; } BluetoothAdapter& GetAdapter() { return adapter_; } + BluetoothDevice FindRemoteDevice(const std::string& mac_address) { + return BluetoothDevice(impl_->FindRemoteDevice(mac_address)); + } private: Mutex mutex_; diff --git a/cpp/platform_v2/public/wifi_lan.cc b/cpp/platform_v2/public/wifi_lan.cc index f5882f7e..9a1e0240 100644 --- a/cpp/platform_v2/public/wifi_lan.cc +++ b/cpp/platform_v2/public/wifi_lan.cc @@ -6,9 +6,8 @@ namespace location { namespace nearby { -bool WifiLanMedium::StartAdvertising( - const std::string& service_id, - const std::string& service_info_name) { +bool WifiLanMedium::StartAdvertising(const std::string& service_id, + const std::string& service_info_name) { return impl_->StartAdvertising(service_id, service_info_name); } @@ -39,13 +38,13 @@ bool WifiLanMedium::StartDiscovery(const std::string& service_id, "service_info_name=%s", &context.service, &service, service.GetName().c_str()); - return; + } else { + context.service = WifiLanService(&service); + NEARBY_LOG( + INFO, + "Discovering service=%p, impl=%p, service_info_name=%s", + &context.service, &service, service.GetName().c_str()); } - context.service = WifiLanService(&service); - NEARBY_LOG( - INFO, - "Discovering service=%p, impl=%p, service_info_name=%s", - &context.service, &service, service.GetName().c_str()); discovered_service_callback_.service_discovered_cb( context.service, service_id); }, @@ -54,12 +53,12 @@ bool WifiLanMedium::StartDiscovery(const std::string& service_id, const std::string& service_id) { MutexLock lock(&mutex_); if (services_.empty()) return; - auto item = services_.extract(&service); - auto& context = *item.mapped(); + auto context = services_.find(&service); + if (context == services_.end()) return; NEARBY_LOG(INFO, "Removing service=%p, impl=%p", - &context.service, &service); - discovered_service_callback_.service_lost_cb(context.service, - service_id); + &(context->second->service), &service); + discovered_service_callback_.service_lost_cb( + context->second->service, service_id); }, }); } @@ -93,8 +92,8 @@ bool WifiLanMedium::StartAcceptingConnections( if (!pair.second) { NEARBY_LOG(INFO, "Accepting (again) socket=%p, impl=%p", &context.socket, &socket); - context.socket = WifiLanSocket(&socket); } else { + context.socket = WifiLanSocket(&socket); NEARBY_LOG(INFO, "Accepting socket=%p, impl=%p", &context.socket, &socket); } @@ -117,10 +116,17 @@ bool WifiLanMedium::StopAcceptingConnections(const std::string& service_id) { WifiLanSocket WifiLanMedium::Connect(WifiLanService& service, const std::string& service_id) { - NEARBY_LOG(INFO, "WifiLanMedium::Connect: service=%p [impl=%p]", &service, - &service.GetImpl()); + NEARBY_LOG( + INFO, + "WifiLanMedium::Connect: service=%p [impl=%p, service_info_name=%s]", + &service, &service.GetImpl(), service.GetName().c_str()); return WifiLanSocket(impl_->Connect(service.GetImpl(), service_id)); } +WifiLanService WifiLanMedium::FindRemoteService(const std::string& ip_address, + int port) { + return WifiLanService(impl_->FindRemoteService(ip_address, port)); +} + } // namespace nearby } // namespace location diff --git a/cpp/platform_v2/public/wifi_lan.h b/cpp/platform_v2/public/wifi_lan.h index c94ac1b8..fa6ba565 100644 --- a/cpp/platform_v2/public/wifi_lan.h +++ b/cpp/platform_v2/public/wifi_lan.h @@ -140,6 +140,8 @@ class WifiLanMedium final { api::WifiLanMedium& GetImpl() { return *impl_; } + WifiLanService FindRemoteService(const std::string& ip_address, int port); + private: Mutex mutex_; std::unique_ptr impl_; diff --git a/cpp/platform_v2/public/wifi_lan_test.cc b/cpp/platform_v2/public/wifi_lan_test.cc index 8a701efa..2e89e09f 100644 --- a/cpp/platform_v2/public/wifi_lan_test.cc +++ b/cpp/platform_v2/public/wifi_lan_test.cc @@ -7,13 +7,14 @@ #include "platform_v2/public/logging.h" #include "gmock/gmock.h" #include "gtest/gtest.h" +#include "absl/strings/string_view.h" namespace location { namespace nearby { namespace { constexpr absl::string_view kServiceID{"com.google.location.nearby.apps.test"}; -constexpr absl::string_view kServiceName{"service name"}; +constexpr absl::string_view kServiceInfoName{"Simulated service info name"}; class WifiLanMediumTest : public ::testing::Test { protected: @@ -44,10 +45,10 @@ TEST_F(WifiLanMediumTest, CanStartAdvertising) { WifiLanMedium wifi_a; WifiLanMedium wifi_b; std::string service_id(kServiceID); - std::string service_name{kServiceName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch found_latch(1); - wifi_a.StartAdvertising(service_id, service_name); + wifi_a.StartAdvertising(service_id, service_info_name); EXPECT_TRUE(wifi_b.StartDiscovery( service_id, DiscoveredServiceCallback{ @@ -68,7 +69,7 @@ TEST_F(WifiLanMediumTest, CanStartDiscovery) { WifiLanMedium wifi_a; WifiLanMedium wifi_b; std::string service_id(kServiceID); - std::string service_name{kServiceName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch found_latch(1); CountDownLatch lost_latch(1); @@ -76,16 +77,16 @@ TEST_F(WifiLanMediumTest, CanStartDiscovery) { DiscoveredServiceCallback{ .service_discovered_cb = [&found_latch](WifiLanService& service, - const std::string& service_id) { + absl::string_view service_id) { found_latch.CountDown(); }, .service_lost_cb = [&lost_latch](WifiLanService& service, - const std::string& service_id) { + absl::string_view service_id) { lost_latch.CountDown(); }, }); - EXPECT_TRUE(wifi_b.StartAdvertising(service_id, service_name)); + EXPECT_TRUE(wifi_b.StartAdvertising(service_id, service_info_name)); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); EXPECT_TRUE(wifi_b.StopAdvertising(service_id)); EXPECT_TRUE(lost_latch.Await(absl::Milliseconds(1000)).result()); @@ -98,7 +99,7 @@ TEST_F(WifiLanMediumTest, CanStopDiscovery) { WifiLanMedium wifi_a; WifiLanMedium wifi_b; std::string service_id(kServiceID); - std::string service_name{kServiceName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch found_latch(1); CountDownLatch lost_latch(1); @@ -106,16 +107,16 @@ TEST_F(WifiLanMediumTest, CanStopDiscovery) { DiscoveredServiceCallback{ .service_discovered_cb = [&found_latch](WifiLanService& service, - const std::string& service_id) { + absl::string_view service_id) { found_latch.CountDown(); }, .service_lost_cb = [&lost_latch](WifiLanService& service, - const std::string& service_id) { + absl::string_view service_id) { lost_latch.CountDown(); }, }); - EXPECT_TRUE(wifi_b.StartAdvertising(service_id, service_name)); + EXPECT_TRUE(wifi_b.StartAdvertising(service_id, service_info_name)); EXPECT_TRUE(found_latch.Await(absl::Milliseconds(1000)).result()); EXPECT_TRUE(wifi_a.StopDiscovery(service_id)); EXPECT_TRUE(wifi_b.StopAdvertising(service_id)); @@ -128,7 +129,7 @@ TEST_F(WifiLanMediumTest, CanStartAcceptingConnectionsAndConnect) { WifiLanMedium wifi_a; WifiLanMedium wifi_b; std::string service_id(kServiceID); - std::string service_name{kServiceName}; + std::string service_info_name{kServiceInfoName}; CountDownLatch found_latch(1); CountDownLatch accepted_latch(1); @@ -145,7 +146,7 @@ TEST_F(WifiLanMediumTest, CanStartAcceptingConnectionsAndConnect) { found_latch.CountDown(); }, }); - wifi_b.StartAdvertising(service_id, service_name); + wifi_b.StartAdvertising(service_id, service_info_name); wifi_b.StartAcceptingConnections( service_id, AcceptedConnectionCallback{ @@ -168,6 +169,7 @@ TEST_F(WifiLanMediumTest, CanStartAcceptingConnectionsAndConnect) { } EXPECT_TRUE(accepted_latch.Await(absl::Milliseconds(1000)).result()); EXPECT_TRUE(socket_a.IsValid()); + wifi_b.StopAcceptingConnections(service_id); wifi_b.StopAdvertising(service_id); wifi_a.StopDiscovery(service_id); env_.Stop(); diff --git a/proto/connections/offline_wire_formats.proto b/proto/connections/offline_wire_formats.proto index 7bce87eb..c7169827 100644 --- a/proto/connections/offline_wire_formats.proto +++ b/proto/connections/offline_wire_formats.proto @@ -193,6 +193,11 @@ message BandwidthUpgradeNegotiationFrame { optional int32 frequency = 4; } + // Accompanies Medium.WEB_RTC + message WebRtcCredentials { + optional string peer_id = 1; + } + optional Medium medium = 1; // Exactly one of the following fields will be set. @@ -201,6 +206,7 @@ message BandwidthUpgradeNegotiationFrame { optional BluetoothCredentials bluetooth_credentials = 4; optional WifiAwareCredentials wifi_aware_credentials = 5; optional WifiDirectCredentials wifi_direct_credentials = 6; + optional WebRtcCredentials web_rtc_credentials = 8; // Disable Encryption for this upgrade medium to improve throughput. optional bool supports_disabling_encryption = 7; diff --git a/proto/connections_enums.proto b/proto/connections_enums.proto index 99729d7f..3fd7dc8b 100644 --- a/proto/connections_enums.proto +++ b/proto/connections_enums.proto @@ -162,7 +162,7 @@ enum PayloadStatus { REMOTE_CANCELLATION = 8; } -// next_id: 16 +// next_id: 17 // Result of an upgrade attempt. enum BandwidthUpgradeResult { UNKNOWN_BANDWIDTH_UPGRADE_RESULT = 0; @@ -208,9 +208,12 @@ enum BandwidthUpgradeResult { // Error during setting up WIFI Direct. WIFI_DIRECT_MEDIUM_ERROR = 15; + + // Error during setting up WebRTC. + WEB_RTC_MEDIUM_ERROR = 16; } -// next_id: 34 +// next_id: 35 // The stage at which an error occurred. enum BandwidthUpgradeErrorStage { UNKNOWN_BANDWIDTH_UPGRADE_ERROR_STAGE = 0; @@ -293,4 +296,6 @@ enum BandwidthUpgradeErrorStage { // WEB_RTC // Creating the WEB_RTC EndpointChannel WEB_RTC_SOCKET_CREATION = 33; + // On the incoming side, listening for incoming WebRTC connections. + WEB_RTC_LISTEN_INCOMING = 34; } diff --git a/proto/error_code_enums.proto b/proto/error_code_enums.proto index 0464f602..0eeb5080 100644 --- a/proto/error_code_enums.proto +++ b/proto/error_code_enums.proto @@ -83,8 +83,12 @@ enum CommonError { // Others error, the error happens when user cancel the flow, it's not a // real failure. FLOW_CANCELED = 5; + // Developing error, an unexpect call that the medium not ready, need to do + // something before this call. e.g. call WifiAwareImpli#connectToSocket but + // never join network before this call. + UNEXPECTED_CALL = 6; - // Reserved 5 to 30 + // Reserved 7 to 30 } // The error for event START_ADVERTISING. The range between 31 and 99. @@ -161,7 +165,27 @@ enum StartDiscoveringError { // System error, failed to acquire WifiAwareSession ACQUIRE_WIFI_AWARE_SESSION_FOR_DISCOVERING_FAILED = 40; - // Next ID :40 + // Next ID :41 +} + +// The error for event START_LISTENING_INCOMING_CONNECTION. The range between 31 +// and 99. +enum StartListeningIncomingConnectionError { + // Developing error, this service ID already requested, should not request it + // again without stop accepting. + DUPLICATE_ACCEPTING_CONNECTION_REQUESTED = 31; + // System error, failed to open a GATT server for listening incoming GATT + // connection. + OPEN_GATT_SERVER_FAILED = 32; + // System error, failed to accept the incoming GATT connection + ACCEPT_GATT_CONNECTION_FAILED = 33; + // System error, failed to accept the incoming L2CAP connection + ACCEPT_L2CAP_CONNECTION_FAILED = 34; + // Network error, wait the GATT connection ready after the connection + // established but never. + CREATE_GATT_SERVER_SOCKET_NOT_READY = 35; + + // Next ID :36 } // The error for event CONNECT. The range between 31 and 99. @@ -194,10 +218,22 @@ enum ConnectError { // Network error, failed to change connection for data transferring on GATT // connection. GATT_SWITCH_TO_DATA_TRANSFERRING_FAILED = 39; + // System error, failed to establish connection + ESTABLISH_CONNECTION_FAILED = 40; + // Developing error, this connection already established, should not request + // it again. + DUPLICATE_CONNECTION_REQUESTED = 41; + // Network error, the connection lost. + CONNECTION_LOST = 42; + // Network error, failed to connect to the network. e.g. an aware network, + // hotspot or a direct network. + CONNECT_TO_NETWORK_FAILED = 43; + + // Next ID :44 } enum Description { - reserved 28; + reserved 28, 29; UNKNOWN = 0; NULL_SERVICE_ID = 1; @@ -227,7 +263,6 @@ enum Description { NULL_NFC_TAG = 25; FEATURE_NFC_NOT_SUPPORTED = 26; FEATURE_NFC_HOST_CARD_EMULATION_NOT_SUPPORTED = 27; - WITHOUT_CONNECTED_WIFI_NETWOR = 29; MULTICAST_NOT_SUPPORTED = 30; NSD_NOT_ENABLED = 31; INVALID_PORT_NUMBER = 32; @@ -271,4 +306,64 @@ enum Description { SET_CONNECTION_PRIORITY_INTERRUPTED = 70; UNKNOWN_IO_EXCEPTION = 71; READ_CHARACTERISTIC_FAILED = 72; + WIFI_HOTSPOT_ENABLED = 73; + AWARE_UNAVAILABLE = 74; + IN_BLACK_LIST = 75; + FEATURE_WIFI_NOT_SUPPORTED = 76; + NULL_WIFI_MANAGER = 77; + SOCKET_CLOSED = 78; + SOCKET_ALREADY_CONNECTED = 79; + NFC_TECH_NOT_SUPPORTED = 80; + NFC_SERVICE_DIED = 81; + BIND_NFC_SERVICE_FAILED = 82; + NFC_CREATE_SOCKET_FAILED = 83; + NULL_WIFI_AWARE_PEER = 84; + NETWORK_ALREADY_JOINED = 85; + JOIN_AWARE_NETWORK_CANCELLED = 86; + NETWORK_UNAVAILABLE = 87; + WITHOUT_ACTIVE_AWARE_NETWORK = 88; + WITHOUT_JOINED_AWARE_NETWORK = 89; + CONNET_TO_SOCKET_CANCELLED = 90; + NULL_SSID = 91; + NULL_PASSWORD = 92; + FEATURE_WIFI_DIRECT_NOT_SUPPORTED = 93; + NULL_WIFI_P2P_MANAGER = 94; + P2P_GROUP_FORMED = 95; + ACQUIRE_P2P_CHANNEL_FAILED = 96; + P2P_UNSUPPORTED = 97; + INTERNAL_ERROR = 98; + BUSY = 99; + REFLECTION_ERROR = 100; + NETWORK_ERROR_EHOSTUNREACH = 101; + NETWORK_ERROR_ENETUNREACH = 102; + ADD_NETWORK_FAILED = 103; + UPDATE_NETWORK_FAILED = 104; + ALREADY_IN_PROGRESS = 105; + INVALID_ARGS = 106; + NOT_AUTHORIZED = 107; + INVALID_NETWORK_ID = 108; + WIFI_MANAGER_ENABLE_NETWORK_FAILED = 109; + WIFI_MANAGER_RECONNECT_FAILED = 110; + WITHOUT_ACTIVE_NETWORK = 111; + WEBRTC_CONNECTION_FLOW_EXIST = 112; + NULL_DROID_GUARD_RESULT = 113; + TACHYON_SIGNALING_MESSENGER_EXIST = 114; + TACHYON_ALREADY_START_RECEIVE_MESSAGE = 115; + TACHYON_RECEIVE_MESSAGE_FAILED = 116; + TACHYON_RECEIVE_MESSAGE_INTERRUPTED = 117; + TACHYON_RECEIVE_MESSAGE_EXECUTION_EXCEPTION = 118; + TACHYON_RECEIVE_MESSAGE_TIMEOUT = 119; + TACHYON_RECEIVE_MESSAGE_AUTH_EXCEPTION = 120; + TACHYON_RECEIVE_MESSAGE_STATUS_EXCEPTION = 121; + TACHYON_SEND_MESSAGE_AUTH_EXCEPTION = 122; + TACHYON_SEND_MESSAGE_STATUS_EXCEPTION = 123; + TACHYON_GET_ICE_SERVER_AUTH_EXCEPTION = 124; + TACHYON_GET_ICE_SERVER_STATUS_EXCEPTION = 125; + EMPTY_TACHYON_ICE_SERVER = 126; + POTENTIAL_WEBRTC_LIB_LOADING_FAILURE = 127; + UNEXPECTED_GATT_DESCRIPTOR = 128; + FAIL_TO_RECEIVE_L2CAP_PACKET = 129; + WITHOUT_PSM_VALUE = 130; + SOCKET_BIND_LISTEN_FAILED = 131; + UNEXPECTED_PACKET_CONTENT = 132; } diff --git a/proto/magic_pair_enums.proto b/proto/magic_pair_enums.proto index 506c116a..8cd616ec 100644 --- a/proto/magic_pair_enums.proto +++ b/proto/magic_pair_enums.proto @@ -44,6 +44,9 @@ message MagicPairEvent { // Parsing something (e.g. BR/EDR Handover data) failed. PARSE_EXCEPTION = 6; + + // A failure at MDH. + MDH_REMOTE_EXCEPTION = 7; } enum BrEdrHandoverErrorCode { diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 962d8e82..442d2d4b 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -138,6 +138,12 @@ enum EventType { // Receiver removes quick settings tile. REMOVE_QUICK_SETTINGS_TILE = 37; + + // Receiver phone consent clicked. + LAUNCH_PHONE_CONSENT = 38; + + // Receiver taps quick settings tile. + TAP_QUICK_SETTINGS_TILE = 39; } // Event category to differentiate whether this comes from sender or receiver, @@ -181,6 +187,19 @@ enum AttachmentTransmissionStatus { COMPLETE_ATTACHMENT_TRANSMISSION_STATUS = 1; CANCELED_ATTACHMENT_TRANSMISSION_STATUS = 2; FAILED_ATTACHMENT_TRANSMISSION_STATUS = 3; + REJECTED_ATTACHMENT = 4; + TIMED_OUT_ATTACHMENT = 5; + AWAITING_REMOTE_ACCEPTANCE_FAILED_ATTACHMENT = 6; + NOT_ENOUGH_SPACE_ATTACHMENT = 7; + FAILED_NO_TRANSFER_UPDATE_CALLBACK = 8; + MEDIA_UNAVAILABLE_ATTACHMENT = 9; + UNSUPPORTED_ATTACHMENT_TYPE_ATTACHMENT = 10; + NO_ATTACHMENT_FOUND = 11; + FAILED_NO_SHARE_TARGET_ENDPOINT = 12; + FAILED_PAIRED_KEYHANDSHAKE = 13; + FAILED_NULL_CONNECTION = 14; + FAILED_NO_PAYLOAD = 15; + FAILED_WRITE_INTRODUCTION = 16; } // The status of advertising and discovering sessions. Used by