diff --git a/cpp/core/BUILD b/cpp/core/BUILD index f0bb59b5..9491718c 100644 --- a/cpp/core/BUILD +++ b/cpp/core/BUILD @@ -51,6 +51,7 @@ cc_library( "//platform:utils", "//platform/api", "//platform/impl/g3", + "//platform/impl/shared:file", "//platform/impl/shared/sample:sample_wifi_medium", "//platform/port:string", ], diff --git a/cpp/core/check_compilation.cc b/cpp/core/check_compilation.cc index 7bca2966..584c6cb4 100644 --- a/cpp/core/check_compilation.cc +++ b/cpp/core/check_compilation.cc @@ -7,7 +7,7 @@ #include "core/status.h" #include "platform/api/platform.h" #include "platform/byte_array.h" -#include "platform/file_impl.h" +#include "platform/impl/shared/file_impl.h" #include "platform/impl/shared/sample/sample_wifi_medium.h" #include "platform/port/string.h" #include "platform/ptr.h" diff --git a/cpp/core/internal/internal_payload_factory.cc b/cpp/core/internal/internal_payload_factory.cc index c7cd5a45..dd5c6cdb 100644 --- a/cpp/core/internal/internal_payload_factory.cc +++ b/cpp/core/internal/internal_payload_factory.cc @@ -4,10 +4,11 @@ #include "core/payload.h" #include "platform/api/condition_variable.h" +#include "platform/api/input_file.h" #include "platform/api/lock.h" +#include "platform/api/output_file.h" #include "platform/byte_array.h" #include "platform/exception.h" -#include "platform/file_impl.h" #include "platform/pipe.h" namespace location { @@ -287,10 +288,9 @@ Ptr InternalPayloadFactory::createIncoming( } case PayloadTransferFrame::PayloadHeader::FILE: { - const std::string payload_path = Platform::getPayloadPath(payload_id); - Ptr input_file = MakePtr(new InputFileImpl( - payload_path, payload_transfer_frame.payload_header().total_size())); - Ptr output_file = MakePtr(new OutputFileImpl(payload_path)); + Ptr output_file = Platform::createOutputFile(payload_id); + Ptr input_file = Platform::createInputFile( + payload_id, payload_transfer_frame.payload_header().total_size()); ConstPtr payload = MakeConstPtr( new Payload(payload_id, MakeConstPtr(new Payload::File(input_file)))); return MakePtr(new IncomingFileInternalPayload( diff --git a/cpp/core/internal/mediums/uuid.h b/cpp/core/internal/mediums/uuid.h index 5742e6c4..5a7a95e7 100644 --- a/cpp/core/internal/mediums/uuid.h +++ b/cpp/core/internal/mediums/uuid.h @@ -17,17 +17,17 @@ namespace connections { template class UUID { public: - explicit UUID(const string& data); + explicit UUID(const std::string& data); UUID(std::int64_t most_sig_bits, std::int64_t least_sig_bits); ~UUID(); // Returns the canonical textual representation // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Format) of the // UUID. - string str(); + std::string str(); private: - string data_; + std::string data_; }; } // namespace connections diff --git a/cpp/core/internal/mediums/webrtc/BUILD b/cpp/core/internal/mediums/webrtc/BUILD index 5ab6e446..56cf5608 100644 --- a/cpp/core/internal/mediums/webrtc/BUILD +++ b/cpp/core/internal/mediums/webrtc/BUILD @@ -7,7 +7,7 @@ cc_library( deps = [ "//platform:utils", "//platform/api", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:libjingle_peerconnection_api", ], ) @@ -20,7 +20,7 @@ cc_test( "//platform/api", "//platform/impl/g3", # buildcleaner: keep "//testing/base/public:gunit_main", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:libjingle_peerconnection_api", ], ) @@ -45,7 +45,7 @@ cc_library( ":peer_id", "//platform:types", "//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:libjingle_peerconnection_api", ], ) @@ -72,6 +72,6 @@ cc_test( "//platform/impl/g3", # buildcleaner: keep "//net/proto2/public:proto2", "//testing/base/public:gunit_main", - "//webrtc/files/stable/webrtc/pc:peerconnection", # buildcleaner: keep + "//webrtc/pc:peerconnection", # buildcleaner: keep ], ) diff --git a/cpp/core/internal/mediums/webrtc/signaling_frames.h b/cpp/core/internal/mediums/webrtc/signaling_frames.h index fec7046c..fb885a58 100644 --- a/cpp/core/internal/mediums/webrtc/signaling_frames.h +++ b/cpp/core/internal/mediums/webrtc/signaling_frames.h @@ -7,7 +7,7 @@ #include "platform/byte_array.h" #include "platform/ptr.h" #include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h" -#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" +#include "webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core/internal/mediums/webrtc/webrtc_socket.h b/cpp/core/internal/mediums/webrtc/webrtc_socket.h index 5a55e9d9..d0ec4104 100644 --- a/cpp/core/internal/mediums/webrtc/webrtc_socket.h +++ b/cpp/core/internal/mediums/webrtc/webrtc_socket.h @@ -6,7 +6,7 @@ #include "platform/api/output_stream.h" #include "platform/api/socket.h" #include "platform/pipe.h" -#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" +#include "webrtc/api/data_channel_interface.h" namespace location { namespace nearby { diff --git a/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc b/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc index 503b8cd8..be83d9f1 100644 --- a/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc +++ b/cpp/core/internal/mediums/webrtc/webrtc_socket_test.cc @@ -5,7 +5,7 @@ #include "platform/ptr.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" +#include "webrtc/api/data_channel_interface.h" namespace location { namespace nearby { diff --git a/cpp/core/strategy.cc b/cpp/core/strategy.cc index dfa1637c..6101a3e2 100644 --- a/cpp/core/strategy.cc +++ b/cpp/core/strategy.cc @@ -25,7 +25,7 @@ bool Strategy::isValid() const { return kP2PStar == *this || kP2PCluster == *this || kP2PPointToPoint == *this; } -string Strategy::getName() const { +std::string Strategy::getName() const { if (Strategy::kP2PCluster == *this) { return "P2P_CLUSTER"; } else if (Strategy::kP2PStar == *this) { diff --git a/cpp/core_v2/BUILD b/cpp/core_v2/BUILD index 12a7a8fe..eed3c011 100644 --- a/cpp/core_v2/BUILD +++ b/cpp/core_v2/BUILD @@ -12,8 +12,9 @@ cc_library( deps = [ ":core_types", "//core_v2/internal", - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//absl/strings", "//absl/time", "//absl/types:span", @@ -38,8 +39,9 @@ cc_library( ], deps = [ "//platform_v2/base", - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//absl/strings", "//absl/types:variant", ], @@ -62,9 +64,10 @@ cc_test( "//core_v2/internal", "//core_v2/internal:internal_test", "//platform_v2/base", - "//platform_v2/impl/g3", - "//platform_v2/public", + "//platform_v2/impl/g3", # build_cleaner: keep + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//testing/base/public:gunit_main", "//absl/strings", "//absl/time", diff --git a/cpp/core_v2/internal/BUILD b/cpp/core_v2/internal/BUILD index e375d8a1..3a1f78d5 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", + "bluetooth_device_name.cc", "client_proxy.cc", "encryption_runner.cc", "endpoint_channel_manager.cc", @@ -16,6 +17,7 @@ cc_library( "base_endpoint_channel.h", "base_pcp_handler.h", "ble_advertisement.h", + "bluetooth_device_name.h", "client_proxy.h", "encryption_runner.h", "endpoint_channel.h", @@ -36,8 +38,9 @@ cc_library( "//core_v2:core_types", "//proto/connections:offline_wire_formats_portable_proto", "//platform_v2/base", - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//proto:connections_enums_portable_proto", "//securegcm:ukey2", "//absl/base:core_headers", @@ -71,6 +74,7 @@ cc_test( "base_endpoint_channel_test.cc", "base_pcp_handler_test.cc", "ble_advertisement_test.cc", + "bluetooth_device_name_test.cc", "client_proxy_test.cc", "encryption_runner_test.cc", "endpoint_channel_manager_test.cc", @@ -87,8 +91,9 @@ cc_test( "//proto/connections:offline_wire_formats_portable_proto", "//platform_v2/base", "//platform_v2/impl/g3", # build_cleaner: keep - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//proto:connections_enums_portable_proto", "//securegcm:ukey2", "//testing/base/public:gunit", diff --git a/cpp/core_v2/internal/base_pcp_handler.cc b/cpp/core_v2/internal/base_pcp_handler.cc index 99482b77..e57801e6 100644 --- a/cpp/core_v2/internal/base_pcp_handler.cc +++ b/cpp/core_v2/internal/base_pcp_handler.cc @@ -18,6 +18,9 @@ namespace location { namespace nearby { namespace connections { +using ::location::nearby::proto::connections::Medium; +using ::securegcm::UKey2Handshake; + BasePcpHandler::BasePcpHandler(EndpointManager* endpoint_manager, EndpointChannelManager* channel_manager) : endpoint_manager_(endpoint_manager), channel_manager_(channel_manager) {} @@ -75,7 +78,7 @@ Status BasePcpHandler::StartDiscovery(ClientProxy* client, const DiscoveryListener& listener) { Future response; RunOnPcpHandlerThread( - [this, client, service_id, options, listener, &response]() { + [this, client, service_id, options, &listener, &response]() { // Ask the implementation to attempt to start discovery. auto result = StartDiscoveryImpl(client, service_id, options); if (!result.status.Ok()) { @@ -138,6 +141,897 @@ void BasePcpHandler::RunOnPcpHandlerThread(Runnable runnable) { serial_executor_.Execute(std::move(runnable)); } +EncryptionRunner::ResultListener BasePcpHandler::GetResultListener() { + return { + .on_success_cb = + [this](const string& endpoint_id, + std::unique_ptr ukey2, + const string& auth_token, const ByteArray& raw_auth_token) { + RunOnPcpHandlerThread([this, endpoint_id, + raw_ukey2 = ukey2.release(), auth_token, + raw_auth_token]() mutable { + OnEncryptionSuccessRunnable( + endpoint_id, std::unique_ptr(raw_ukey2), + auth_token, raw_auth_token); + }); + }, + .on_failure_cb = + [this](const string& endpoint_id, EndpointChannel* channel) { + RunOnPcpHandlerThread([this, endpoint_id, channel]() { + OnEncryptionFailureRunnable(endpoint_id, channel); + }); + }, + }; +} + +void BasePcpHandler::OnEncryptionSuccessRunnable( + const string& endpoint_id, std::unique_ptr ukey2, + const string& auth_token, const ByteArray& raw_auth_token) { + // Quick fail if we've been removed from pending connections while we were + // busy running UKEY2. + auto it = pending_connections_.find(endpoint_id); + if (it == pending_connections_.end()) { + NEARBY_LOG(INFO, + "Connection not found on UKEY negotination complete; id=%s", + endpoint_id.c_str()); + return; + } + + BasePcpHandler::PendingConnectionInfo& connection_info = it->second; + + if (!ukey2) { + // Fail early, if there is no crypto context. + ProcessPreConnectionResultFailure(connection_info.client, endpoint_id); + return; + } + + connection_info.SetCryptoContext(std::move(ukey2)); + NEARBY_LOG(INFO, "Register encrypted connection; wait for response; id=%s", + endpoint_id.c_str()); + + // Set ourselves up so that we receive all acceptance/rejection messages + handle_ = endpoint_manager_->RegisterFrameProcessor( + V1Frame::CONNECTION_RESPONSE, this); + + // Now we register our endpoint so that we can listen for both sides to + // accept. + endpoint_manager_->RegisterEndpoint( + connection_info.client, endpoint_id, + { + .remote_endpoint_name = connection_info.remote_endpoint_name, + .authentication_token = auth_token, + .raw_authentication_token = raw_auth_token, + .is_incoming_connection = connection_info.is_incoming, + }, + std::move(connection_info.channel), connection_info.listener); + + if (connection_info.result != nullptr) { + NEARBY_LOG(INFO, "Connection established; Finalising future OK"); + connection_info.result->Set({Status::kSuccess}); + connection_info.result = nullptr; + } +} + +void BasePcpHandler::OnEncryptionFailureRunnable( + const string& endpoint_id, EndpointChannel* endpoint_channel) { + auto it = pending_connections_.find(endpoint_id); + if (it == pending_connections_.end()) { + NEARBY_LOG(INFO, + "Connection not found on UKEY negotination complete; id=%s", + endpoint_id.c_str()); + return; + } + + BasePcpHandler::PendingConnectionInfo& info = it->second; + // We had a bug here, caused by a race with EncryptionRunner. We now verify + // the EndpointChannel to avoid it. In a simultaneous connection, we clean + // up one of the two EndpointChannels and then update our pendingConnections + // with the winning channel's state. Closing a channel that was in the + // middle of EncryptionRunner would trigger onEncryptionFailed, and, since + // the map had already updated with the winning EndpointChannel, we closed + // it too by accident. + if (*endpoint_channel != *info.channel) { + NEARBY_LOG( + INFO, "Not destroying channel [mismatch]: passed=%s; expected=%s", + endpoint_channel->GetName().c_str(), info.channel->GetName().c_str()); + return; + } + + ProcessPreConnectionInitiationFailure(endpoint_id, info.channel.get(), + {Status::kEndpointIoError}, + info.result.get()); + info.result.reset(); +} + +Status BasePcpHandler::RequestConnection(ClientProxy* client, + const string& endpoint_id, + const ConnectionRequestInfo& info) { + Future result; + RunOnPcpHandlerThread([this, client, &info, endpoint_id, &result]() { + absl::Time start_time = SystemClock::ElapsedRealtime(); + + // If we already have a pending connection, then we shouldn't allow any more + // outgoing connections to this endpoint. + if (pending_connections_.count(endpoint_id)) { + NEARBY_LOG(INFO, "Connection already exists: id=%s", endpoint_id.c_str()); + result.Set({Status::kAlreadyConnectedToEndpoint}); + return; + } + + // If our child class says we can't send any more outgoing connections, + // listen to them. + if (ShouldEnforceTopologyConstraints() && + !CanSendOutgoingConnection(client)) { + NEARBY_LOG(INFO, "Outgoing connection not allowed: id=%s", + endpoint_id.c_str()); + result.Set({Status::kOutOfOrderApiCall}); + return; + } + + auto endpoint = GetDiscoveredEndpoint(endpoint_id); + if (endpoint == nullptr) { + NEARBY_LOG(INFO, "Discovered endpoint not found: id=%s", + endpoint_id.c_str()); + result.Set({Status::kEndpointUnknown}); + return; + } + + auto connect_impl_result = ConnectImpl(client, endpoint); + std::unique_ptr channel = + std::move(connect_impl_result.endpoint_channel); + + if (channel == nullptr) { + NEARBY_LOG(INFO, "Endpoint channel not available: id=%s", + endpoint_id.c_str()); + ProcessPreConnectionInitiationFailure( + endpoint_id, channel.get(), connect_impl_result.status, &result); + return; + } + + NEARBY_LOG(INFO, "Sending connection request: id=%s", endpoint_id.c_str()); + // Generate the nonce to use for this connection. + std::int32_t nonce = prng_.NextInt32(); + + // 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, + GetConnectionMediumsByPriority()); + if (!write_exception.Ok()) { + NEARBY_LOG(INFO, "Failed to send connection request: id=%s", + endpoint_id.c_str()); + ProcessPreConnectionInitiationFailure( + endpoint_id, channel.get(), {Status::kEndpointIoError}, &result); + return; + } + + NEARBY_LOG(INFO, "adding connection to pending set: id=%s", + endpoint_id.c_str()); + + // 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 + // connection if needed. + EndpointChannel* endpoint_channel = + pending_connections_ + .emplace(endpoint_id, + PendingConnectionInfo{ + .client = client, + .remote_endpoint_name = endpoint->endpoint_name, + .nonce = nonce, + .is_incoming = false, + .start_time = start_time, + .listener = info.listener, + .result = MakeSwapper(&result), + .channel = std::move(channel), + }) + .first->second.channel.get(); + + 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. + 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, ")"), + client->GetClientId(), &result); + NEARBY_LOG(INFO, "Wait is complete: id=%s; status=%d", endpoint_id.c_str(), + status.value); + return status; +} + +BasePcpHandler::DiscoveredEndpoint* BasePcpHandler::GetDiscoveredEndpoint( + const string& endpoint_id) { + auto it = discovered_endpoints_.find(endpoint_id); + if (it == discovered_endpoints_.end()) { + return nullptr; + } + return it->second.get(); +} + +void BasePcpHandler::PendingConnectionInfo::SetCryptoContext( + std::unique_ptr ukey2) { + this->ukey2 = std::move(ukey2); +} + +bool BasePcpHandler::HasOutgoingConnections(ClientProxy* client) const { + for (const auto& item : pending_connections_) { + auto& connection = item.second; + if (!connection.is_incoming) { + return true; + } + } + return client->GetNumOutgoingConnections() > 0; +} + +bool BasePcpHandler::HasIncomingConnections(ClientProxy* client) const { + for (const auto& item : pending_connections_) { + auto& connection = item.second; + if (connection.is_incoming) { + return true; + } + } + return client->GetNumIncomingConnections() > 0; +} + +bool BasePcpHandler::CanSendOutgoingConnection(ClientProxy* client) const { + return true; +} + +bool BasePcpHandler::CanReceiveIncomingConnection(ClientProxy* client) const { + return true; +} + +Exception BasePcpHandler::WriteConnectionRequestFrame( + EndpointChannel* endpoint_channel, const string& local_endpoint_id, + const string& local_endpoint_name, std::int32_t nonce, + const std::vector& supported_mediums) { + return endpoint_channel->Write(parser::ForConnectionRequest( + local_endpoint_id, local_endpoint_name, nonce, supported_mediums)); +} + +void BasePcpHandler::ProcessPreConnectionInitiationFailure( + const string& endpoint_id, EndpointChannel* channel, Status status, + Future* result) { + if (channel != nullptr) { + channel->Close(); + } + + pending_connections_.erase(endpoint_id); + + if (result != nullptr) { + NEARBY_LOG(INFO, "Connection failed; aborting future"); + result->Set(status); + } +} + +void BasePcpHandler::ProcessPreConnectionResultFailure( + ClientProxy* client, const string& endpoint_id) { + auto item = pending_connections_.extract(endpoint_id); + endpoint_manager_->DiscardEndpoint(client, endpoint_id); + client->OnConnectionRejected(endpoint_id, {Status::kError}); +} + +bool BasePcpHandler::ShouldEnforceTopologyConstraints() const { + // Topology constraints only matter for the advertiser. + // For discoverers, we'll always enforce them. + if (advertising_options_.strategy.IsNone()) { + return true; + } + + return advertising_options_.enforce_topology_constraints; +} + +bool BasePcpHandler::AutoUpgradeBandwidth() const { + if (advertising_options_.strategy.IsNone()) { + return true; + } + + return advertising_options_.auto_upgrade_bandwidth; +} + +Status BasePcpHandler::AcceptConnection( + ClientProxy* client, const string& endpoint_id, + const PayloadListener& payload_listener) { + Future response; + RunOnPcpHandlerThread( + [this, client, endpoint_id, payload_listener, &response]() { + NEARBY_LOG(INFO, "AcceptConnection: id=%s", endpoint_id.c_str()); + if (!pending_connections_.count(endpoint_id)) { + NEARBY_LOG(INFO, "AcceptConnection: no pending connection for id=%s", + endpoint_id.c_str()); + response.Set({Status::kEndpointUnknown}); + return; + } + auto& connection_info = pending_connections_[endpoint_id]; + + // By this point in the flow, connection_info.channel has been + // nulled out because ownership of that EndpointChannel was passed on to + // EndpointChannelManager via a call to + // EndpointManager::registerEndpoint(), so we now need to get access to + // the EndpointChannel from the authoritative owner. + std::shared_ptr channel = + channel_manager_->GetChannelForEndpoint(endpoint_id); + if (channel == nullptr) { + NEARBY_LOG( + ERROR, + "Channel destroyed before Accept; bring down connection: id=%s", + endpoint_id.c_str()); + ProcessPreConnectionResultFailure(client, endpoint_id); + response.Set({Status::kEndpointUnknown}); + return; + } + + Exception write_exception = + channel->Write(parser::ForConnectionResponse(Status::kSuccess)); + if (!write_exception.Ok()) { + NEARBY_LOG(INFO, "AcceptConnection: failed to send response: id=%s", + endpoint_id.c_str()); + ProcessPreConnectionResultFailure(client, endpoint_id); + response.Set({Status::kEndpointIoError}); + return; + } + + NEARBY_LOG(INFO, "AcceptConnection: accepting locally: id=%s", + endpoint_id.c_str()); + connection_info.LocalEndpointAcceptedConnection(endpoint_id, + payload_listener); + EvaluateConnectionResult(client, endpoint_id, + false /* can_close_immediately */); + response.Set({Status::kSuccess}); + }); + + return WaitForResult(absl::StrCat("acceptConnection(", endpoint_id, ")"), + client->GetClientId(), &response); +} + +Status BasePcpHandler::RejectConnection(ClientProxy* client, + const string& endpoint_id) { + Future response; + RunOnPcpHandlerThread([this, client, endpoint_id, &response]() { + NEARBY_LOG(INFO, "RejectConnection: id=%s", endpoint_id.c_str()); + if (!pending_connections_.count(endpoint_id)) { + NEARBY_LOG(INFO, "RejectConnection: no pending connection for id=%s", + endpoint_id.c_str()); + response.Set({Status::kEndpointUnknown}); + return; + } + auto& connection_info = pending_connections_[endpoint_id]; + + // By this point in the flow, connection_info->endpoint_channel_ has been + // nulled out because ownership of that EndpointChannel was passed on to + // EndpointChannelManager via a call to + // EndpointManager::registerEndpoint(), so we now need to get access to the + // EndpointChannel from the authoritative owner. + std::shared_ptr channel = + channel_manager_->GetChannelForEndpoint(endpoint_id); + if (channel == nullptr) { + NEARBY_LOG( + ERROR, + "Channel destroyed before Reject; bring down connection: id=%s", + endpoint_id.c_str()); + ProcessPreConnectionResultFailure(client, endpoint_id); + response.Set({Status::kEndpointUnknown}); + return; + } + + Exception write_exception = channel->Write( + parser::ForConnectionResponse(Status::kConnectionRejected)); + if (!write_exception.Ok()) { + NEARBY_LOG(INFO, "RejectConnection: failed to send response: id=%s", + endpoint_id.c_str()); + ProcessPreConnectionResultFailure(client, endpoint_id); + response.Set({Status::kEndpointIoError}); + return; + } + + NEARBY_LOG(INFO, "RejectConnection: rejecting locally: id=%s", + endpoint_id.c_str()); + connection_info.LocalEndpointRejectedConnection(endpoint_id); + EvaluateConnectionResult(client, endpoint_id, + false /* can_close_immediately */); + response.Set({Status::kSuccess}); + }); + + return WaitForResult(absl::StrCat("rejectConnection(", endpoint_id, ")"), + client->GetClientId(), &response); +} + +// proto::connections::Medium BasePcpHandler::GetBandwidthUpgradeMedium() { +// return bandwidth_upgrade_medium_.Get(); +//} + +void BasePcpHandler::OnIncomingFrame(const OfflineFrame& frame, + const string& endpoint_id, + ClientProxy* client, + proto::connections::Medium medium) { + CountDownLatch latch(1); + RunOnPcpHandlerThread([this, client, endpoint_id, frame, &latch]() { + NEARBY_LOG(INFO, "OnConnectionResponse: id=%s", endpoint_id.c_str()); + + if (client->HasRemoteEndpointResponded(endpoint_id)) { + NEARBY_LOG(INFO, "OnConnectionResponse: already handled; id=%s", + endpoint_id.c_str()); + return; + } + + const ConnectionResponseFrame& connection_response = + frame.v1().connection_response(); + + if (connection_response.status() == Status::kSuccess) { + NEARBY_LOG(INFO, "OnConnectionResponse: remote accepted; id=%s", + endpoint_id.c_str()); + client->RemoteEndpointAcceptedConnection(endpoint_id); + } else { + NEARBY_LOG(INFO, + "OnConnectionResponse: remote rejected; id=%s; status=%d", + endpoint_id.c_str(), connection_response.status()); + client->RemoteEndpointRejectedConnection(endpoint_id); + } + + EvaluateConnectionResult(client, endpoint_id, + /* can_close_immediately= */ true); + + latch.CountDown(); + }); + WaitForLatch("OnIncomingFrame()", &latch); +} + +void BasePcpHandler::OnEndpointDisconnect(ClientProxy* client, + const string& endpoint_id, + CountDownLatch* barrier) { + RunOnPcpHandlerThread([this, client, endpoint_id, barrier]() { + auto item = pending_alarms_.find(endpoint_id); + if (item != pending_alarms_.end()) { + auto& alarm = item->second; + alarm.Cancel(); + pending_alarms_.erase(item); + } + ProcessPreConnectionResultFailure(client, endpoint_id); + barrier->CountDown(); + }); +} + +ConnectionOptions BasePcpHandler::GetConnectionOptions() const { + return advertising_options_; +} + +void BasePcpHandler::OnEndpointFound( + ClientProxy* client, + std::unique_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; + + 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)); + } 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)); + } + } +} + +void BasePcpHandler::OnEndpointLost( + ClientProxy* client, const BasePcpHandler::DiscoveredEndpoint& endpoint) { + // Look up the DiscoveredEndpoint we have in our cache. + const auto* discovered_endpoint = + GetDiscoveredEndpoint(endpoint.endpoint_id); + if (discovered_endpoint == nullptr) { + NEARBY_LOG(INFO, "No previous endpoint (nothing to lose): id=%s", + endpoint.endpoint_id.c_str()); + 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) { + NEARBY_LOG(INFO, "Previous endpoint name mismatch; passed=%s; expected=%s", + endpoint.endpoint_name.c_str(), + discovered_endpoint->endpoint_name.c_str()); + return; + } + + auto item = discovered_endpoints_.extract(endpoint.endpoint_id); + client->OnEndpointLost(endpoint.service_id, endpoint.endpoint_id); +} + +bool BasePcpHandler::IsPreferred( + const BasePcpHandler::DiscoveredEndpoint& new_endpoint, + const BasePcpHandler::DiscoveredEndpoint& old_endpoint) { + std::vector mediums = + GetConnectionMediumsByPriority(); + // As we iterate through the list of mediums, we see if we run into the new + // endpoint's medium or the old endpoint's medium first. + for (const auto& medium : mediums) { + if (medium == new_endpoint.medium) { + // The new endpoint's medium came first. It's preferred! + return true; + } + + if (medium == old_endpoint.medium) { + // The old endpoint's medium came first. Stick with the old endpoint! + return false; + } + } + NEARBY_LOG(FATAL, "Failed to determine preferred medium; bailing out"); + return false; +} + +Exception BasePcpHandler::OnIncomingConnection( + ClientProxy* client, const string& remote_device_name, + std::unique_ptr channel, + proto::connections::Medium medium) { + absl::Time start_time = SystemClock::ElapsedRealtime(); + + // Fixes an NPE in ClientProxy.OnConnectionResult. 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()) { + NEARBY_LOG(WARNING, + "Ignoring incoming connection because client 0x%" PRIX64 + " is no longer advertising.", + client->GetClientId()); + return {Exception::kIo}; + } + + // Endpoints connecting to us will always tell us about themselves first. + ExceptionOr wrapped_frame = + ReadConnectionRequestFrame(channel.get()); + + if (!wrapped_frame.ok()) { + if (wrapped_frame.exception()) { + NEARBY_LOG( + ERROR, + "Failed to parse incoming connection request; client_id=0x%" PRIX64 + "; device=%s", + client->GetClientId(), remote_device_name.c_str()); + ProcessPreConnectionInitiationFailure("", channel.get(), {Status::kError}, + nullptr); + return {Exception::kSuccess}; + } + return wrapped_frame.GetException(); + } + + OfflineFrame& frame = wrapped_frame.result(); + const ConnectionRequestFrame& connection_request = + frame.v1().connection_request(); + NEARBY_LOG(ERROR, + "Incoming connection request; client_id=0x%" PRIX64 + "; device=%s; id=%s", + client->GetClientId(), remote_device_name.c_str(), + connection_request.endpoint_id().c_str()); + if (client->IsConnectedToEndpoint(connection_request.endpoint_id())) { + return {Exception::kIo}; + } + + // If we've already sent out a connection request to this endpoint, then this + // is where we need to decide which connection to break. + if (BreakTie(client, connection_request.endpoint_id(), + connection_request.nonce(), channel.get())) { + return {Exception::kSuccess}; + } + + // If our child class says we can't accept any more incoming connections, + // listen to them. + if (ShouldEnforceTopologyConstraints() && + !CanReceiveIncomingConnection(client)) { + return {Exception::kIo}; + } + + // The ConnectionRequest frame has two fields that both contain the + // 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(); + + // 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 connection if needed. + auto* owned_channel = + pending_connections_ + .emplace(connection_request.endpoint_id(), + PendingConnectionInfo{ + .client = client, + .remote_endpoint_name = endpoint_name, + .nonce = connection_request.nonce(), + .is_incoming = true, + .start_time = start_time, + .listener = advertising_listener_, + .supported_mediums = + parser::ConnectionRequestMediumsToMediums( + connection_request), + .channel = std::move(channel), + }) + .first->second.channel.get(); + + // Next, we'll set up encryption. + encryption_runner_.StartServer(client, connection_request.endpoint_id(), + owned_channel, GetResultListener()); + return {Exception::kSuccess}; +} + +bool BasePcpHandler::BreakTie(ClientProxy* client, const string& endpoint_id, + std::int32_t incoming_nonce, + EndpointChannel* endpoint_channel) { + auto it = pending_connections_.find(endpoint_id); + if (it != pending_connections_.end()) { + BasePcpHandler::PendingConnectionInfo& info = it->second; + + NEARBY_LOG(INFO, "BreakTie: id=%s", endpoint_id.c_str()); + // Break the lowest connection. In the (extremely) rare case of a tie, break + // both. + if (info.nonce > incoming_nonce) { + // Our connection won! Clean up their connection. + endpoint_channel->Close(); + + NEARBY_LOG(INFO, "BreakTie: We won; id=%s", endpoint_id.c_str()); + return true; + } else if (info.nonce < incoming_nonce) { + // Aw, we lost. Clean up our connection, and then we'll let their + // connection continue on. + ProcessTieBreakLoss(client, endpoint_id, &info); + + NEARBY_LOG(INFO, "BreakTie: We lost; id=%s", endpoint_id.c_str()); + } else { + // Oh. Huh. We both lost. Well, that's awkward. We'll clean up both and + // just force the devices to retry. + endpoint_channel->Close(); + + ProcessTieBreakLoss(client, endpoint_id, &info); + + NEARBY_LOG(INFO, "BreakTie: Both lost; id=%s", endpoint_id.c_str()); + return true; + } + } + + return false; +} + +void BasePcpHandler::ProcessTieBreakLoss( + ClientProxy* client, const string& endpoint_id, + BasePcpHandler::PendingConnectionInfo* info) { + ProcessPreConnectionInitiationFailure(endpoint_id, info->channel.get(), + {Status::kEndpointIoError}, + info->result.get()); + info->result = nullptr; + ProcessPreConnectionResultFailure(client, endpoint_id); +} + +void BasePcpHandler::InitiateBandwidthUpgrade( + ClientProxy* client, const string& endpoint_id, + const std::vector& supported_mediums) { + // When we successfully connect to a remote endpoint and a bandwidth upgrade + // medium has not yet been decided, we'll pick the highest bandwidth medium + // supported by both us and the remote endpoint. Once we pick a medium, all + // future connections will use it too. eg. If we chose Wifi LAN, we'll attempt + // to upgrade the 2nd, 3rd, etc remote endpoints with Wifi LAN even if they're + // on a different network (or had a better medium). This is a quick and easy + // way to prevent mediums, like Wifi Hotspot, from interfering with active + // connections (although it's suboptimal for bandwidth throughput). When all + // endpoints disconnect, we reset the bandwidth upgrade medium. + if (bandwidth_upgrade_medium_.Get() == + proto::connections::Medium::UNKNOWN_MEDIUM) { + bandwidth_upgrade_medium_.Set(ChooseBestUpgradeMedium(supported_mediums)); + } + + if (AutoUpgradeBandwidth() && (bandwidth_upgrade_medium_.Get() != + proto::connections::Medium::UNKNOWN_MEDIUM)) { + // TODO(apolyudov): Bring bandwidth upgrade back, when it is ready. + // bandwidth_upgrade_->InitiateBandwidthUpgradeForEndpoint( + // client, endpoint_id, bandwidth_upgrade_medium_.Get()); + } +} + +proto::connections::Medium BasePcpHandler::ChooseBestUpgradeMedium( + const std::vector& their_supported_mediums) { + // If the remote side did not report their supported mediums, choose an + // appropriate default. + std::vector their_mediums = + their_supported_mediums; + if (their_supported_mediums.empty()) { + their_mediums.push_back(GetDefaultUpgradeMedium()); + } + + // Otherwise, pick the best medium we support. + std::vector my_mediums = + GetConnectionMediumsByPriority(); + for (const auto& my_medium : my_mediums) { + for (const auto& their_medium : their_mediums) { + if (my_medium == their_medium) { + return my_medium; + } + } + } + + return proto::connections::Medium::UNKNOWN_MEDIUM; +} + +void BasePcpHandler::EvaluateConnectionResult(ClientProxy* client, + const string& endpoint_id, + bool can_close_immediately) { + // Short-circuit immediately if we're not in an actionable state yet. We will + // be called again once the other side has made their decision. + if (!client->IsConnectionAccepted(endpoint_id) && + !client->IsConnectionRejected(endpoint_id)) { + if (!client->HasLocalEndpointResponded(endpoint_id)) { + NEARBY_LOG(INFO, "ConnectionResult: local client did not respond; id=%s", + endpoint_id.c_str()); + } else if (!client->HasRemoteEndpointResponded(endpoint_id)) { + NEARBY_LOG(INFO, "ConnectionResult: remote client did not respond; id=%s", + endpoint_id.c_str()); + } + return; + } + + // Clean up the endpoint channel from our list of 'pending' connections. It's + // no longer pending. + auto it = pending_connections_.find(endpoint_id); + if (it == pending_connections_.end()) { + NEARBY_LOG(INFO, "No pending connection to evaluate; id=%s", + endpoint_id.c_str()); + return; + } + + auto pair = pending_connections_.extract(it); + BasePcpHandler::PendingConnectionInfo& connection_info = pair.mapped(); + bool is_connection_accepted = client->IsConnectionAccepted(endpoint_id); + + Status response_code; + if (is_connection_accepted) { + NEARBY_LOG(INFO, "Pending connection accepted; id=%s", endpoint_id.c_str()); + response_code = {Status::kSuccess}; + + // Both sides have accepted, so we can now start talking over encrypted + // channels + // Now, after both parties accepted connection (presumably after verifying & + // matching security tokens), we are allowed to extract the shared key. + auto ukey2 = std::move(connection_info.ukey2); + bool succeeded = ukey2->VerifyHandshake(); + CHECK(succeeded); // If this fails, it's a UKEY2 protocol bug. + auto context = ukey2->ToConnectionContext(); + assert(context); // there is no way how this can fail, if Verify succeeded. + // If it did, it's a UKEY2 protocol bug. + + channel_manager_->EncryptChannelForEndpoint(endpoint_id, + std::move(context)); + } else { + NEARBY_LOG(INFO, "Pending connection rejected; id=%s", endpoint_id.c_str()); + response_code = {Status::kConnectionRejected}; + } + + // Invoke the client callback to let it know of the connection result. + if (response_code.Ok()) { + client->OnConnectionAccepted(endpoint_id); + } else { + client->OnConnectionRejected(endpoint_id, response_code); + } + + // If the connection failed, clean everything up and short circuit. + if (!is_connection_accepted) { + // Clean up the channel in EndpointManager if it's no longer required. + if (can_close_immediately) { + endpoint_manager_->DiscardEndpoint(client, endpoint_id); + } else { + pending_alarms_.emplace( + endpoint_id, + CancelableAlarm( + "BasePcpHandler.evaluateConnectionResult() delayed close", + [this, client, endpoint_id]() { + endpoint_manager_->DiscardEndpoint(client, endpoint_id); + }, + kRejectedConnectionCloseDelay, &alarm_executor_)); + } + + return; + } + + // Kick off the bandwidth upgrade for incoming connections. + if (connection_info.is_incoming) { + InitiateBandwidthUpgrade(client, endpoint_id, + connection_info.supported_mediums); + } +} + +ExceptionOr BasePcpHandler::ReadConnectionRequestFrame( + EndpointChannel* endpoint_channel) { + if (endpoint_channel == nullptr) { + return ExceptionOr(Exception::kIo); + } + + // To avoid a device connecting but never sending their introductory frame, we + // time out the connection after a certain amount of time. + CancelableAlarm timeout_alarm( + absl::StrCat("PcpHandler(", this->GetStrategy().GetName(), + ")::ReadConnectionRequestFrame"), + [endpoint_channel]() { endpoint_channel->Close(); }, + kConnectionRequestReadTimeout, &alarm_executor_); + // Do a blocking read to try and find the ConnectionRequestFrame + ExceptionOr wrapped_bytes = endpoint_channel->Read(); + timeout_alarm.Cancel(); + + if (!wrapped_bytes.ok()) { + return ExceptionOr(wrapped_bytes.exception()); + } + + ByteArray bytes = std::move(wrapped_bytes.result()); + ExceptionOr wrapped_frame = parser::FromBytes(bytes); + if (wrapped_frame.GetException().Raised(Exception::kInvalidProtocolBuffer)) { + return ExceptionOr(Exception::kIo); + } + + OfflineFrame& frame = wrapped_frame.result(); + if (V1Frame::CONNECTION_REQUEST != parser::GetFrameType(frame)) { + return ExceptionOr(Exception::kIo); + } + + return wrapped_frame; +} + +///////////////////// BasePcpHandler::PendingConnectionInfo /////////////////// + +BasePcpHandler::PendingConnectionInfo::~PendingConnectionInfo() { + if (result != nullptr) { + NEARBY_LOG(INFO, "Future was not set; destroying info"); + result->Set({Status::kError}); + } + + if (channel != nullptr) { + channel->Close(proto::connections::DisconnectionReason::SHUTDOWN); + } + + // Destroy crypto context now; for some reason, crypto context destructor + // segfaults if it is not destroyed here. + this->ukey2.reset(); +} + +void BasePcpHandler::PendingConnectionInfo::LocalEndpointAcceptedConnection( + const string& endpoint_id, const PayloadListener& payload_listener) { + client->LocalEndpointAcceptedConnection(endpoint_id, payload_listener); +} + +void BasePcpHandler::PendingConnectionInfo::LocalEndpointRejectedConnection( + const string& endpoint_id) { + client->LocalEndpointRejectedConnection(endpoint_id); +} + } // namespace connections } // namespace nearby } // namespace location diff --git a/cpp/core_v2/internal/base_pcp_handler.h b/cpp/core_v2/internal/base_pcp_handler.h index e4df32f3..1d9dd32b 100644 --- a/cpp/core_v2/internal/base_pcp_handler.h +++ b/cpp/core_v2/internal/base_pcp_handler.h @@ -81,9 +81,8 @@ class BasePcpHandler : public PcpHandler, BasePcpHandler(BasePcpHandler&&) = delete; BasePcpHandler& operator=(BasePcpHandler&&) = delete; - // We have been asked by the client to start advertising. Once we successfully - // start advertising, we'll change the ClientProxy's state. - // ConnectionListener (info.listener) will be notified in case of any event. + // Starts advertising. Once successfully started, changes ClientProxy's state. + // 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, @@ -91,58 +90,52 @@ class BasePcpHandler : public PcpHandler, const ConnectionOptions& options, const ConnectionRequestInfo& info) override; - // If Advertising is active, stop it, and change CLientProxy state, - // otherwise do nothing. + // Stops Advertising is active, and changes CLientProxy state, + // otherwise does nothing. void StopAdvertising(ClientProxy* client_proxy) override; - // Start discovery of endpoints that may be advertising. - // Update ClientProxy state once discovery started. + // 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, const std::string& service_id, const ConnectionOptions& options, const DiscoveryListener& listener) override; - // If Discovery is active, stop it, and change CLientProxy state, - // otherwise do nothing. + // Stops Discovery if it is active, and changes CLientProxy state, + // otherwise does nothing. void StopDiscovery(ClientProxy* client_proxy) override; - // If remote endpoint has been successfully discovered, request it to form a - // connection, update state on ClientProxy. + // Requests a newly discoveered remote endpoint it to form a connection. + // Updates state on ClientProxy. Status RequestConnection(ClientProxy* client_proxy, const std::string& endpoint_id, - const ConnectionRequestInfo& info) override { - return Status{Status::kError}; - } + const ConnectionRequestInfo& info) override; - // Either party may call this to accept connection on their part. + // Called by either party to accept connection on their part. // Until both parties call it, connection will not reach a data phase. - // Update state in ClientProxy. + // Updates state in ClientProxy. Status AcceptConnection(ClientProxy* client_proxy, const std::string& endpoint_id, - const PayloadListener& payload_listener) override { - return Status{Status::kError}; - } + const PayloadListener& payload_listener) override; - // Either party may call this to accept connection on their part. + // Called by either party to reject connection on their part. // If either party does call it, connection will terminate. - // Update state in ClientProxy. + // Updates state in ClientProxy. Status RejectConnection(ClientProxy* client_proxy, - const std::string& endpoint_id) override { - return Status{Status::kError}; - } + const std::string& endpoint_id) override; // @EndpointManagerReaderThread void OnIncomingFrame(const OfflineFrame& frame, const std::string& endpoint_id, ClientProxy* client, - proto::connections::Medium medium) override {} + proto::connections::Medium medium) override; // Called when an endpoint disconnects while we're waiting for both sides to // approve/reject the connection. // @EndpointManagerThread void OnEndpointDisconnect(ClientProxy* client_proxy, const std::string& endpoint_id, - CountDownLatch* barrier) override {} + CountDownLatch* barrier) override; protected: // The result of a call to startAdvertisingImpl() or startDiscoveryImpl(). @@ -156,14 +149,11 @@ class BasePcpHandler : public PcpHandler, // Represents an endpoint that we've discovered. Typically, the implementation // will know how to connect to this endpoint if asked. (eg. It holds on to a // BluetoothDevice) - class DiscoveredEndpoint { - public: - virtual ~DiscoveredEndpoint() = default; - - virtual std::string GetEndpointId() const = 0; - virtual std::string GetEndpointName() const = 0; - virtual std::string GetServiceId() const = 0; - virtual proto::connections::Medium GetMedium() const = 0; + struct DiscoveredEndpoint { + std::string endpoint_id; + std::string endpoint_name; + std::string service_id; + proto::connections::Medium medium; }; struct ConnectImplResult { @@ -183,13 +173,19 @@ class BasePcpHandler : public PcpHandler, // @PcpHandlerThread void OnEndpointLost(ClientProxy* client_proxy, - const DiscoveredEndpoint* endpoint); + const DiscoveredEndpoint& endpoint); Exception OnIncomingConnection( ClientProxy* client_proxy, const std::string& remote_device_name, 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 CanSendOutgoingConnection(ClientProxy* client_proxy) const; + virtual bool CanReceiveIncomingConnection(ClientProxy* client_proxy) const; + // @PcpHandlerThread virtual StartOperationResult StartAdvertisingImpl( ClientProxy* client_proxy, const std::string& service_id, @@ -218,6 +214,74 @@ class BasePcpHandler : public PcpHandler, EndpointChannelManager* channel_manager_; private: + struct PendingConnectionInfo { + PendingConnectionInfo() = default; + PendingConnectionInfo(PendingConnectionInfo&& other) = default; + PendingConnectionInfo& operator=(PendingConnectionInfo&&) = default; + ~PendingConnectionInfo(); + + // Passes crypto context that we acquired in DH session for temporary + // ownership here. + void SetCryptoContext(std::unique_ptr ukey2); + + // Pass Accept notification to client. + void LocalEndpointAcceptedConnection( + const std::string& endpoint_id, + const PayloadListener& payload_listener); + + // Pass Reject notification to client. + void LocalEndpointRejectedConnection(const std::string& endpoint_id); + + // 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; + std::int32_t nonce = 0; + bool is_incoming = false; + absl::Time start_time {absl::InfinitePast()}; + // Client callbacks. Always valid. + ConnectionListener listener; + + // Only set for outgoing connections. If set, we must call + // result->Set() when connection is established, or rejected. + Swapper> result = nullptr; + + // Only (possibly) vector for incoming connections. + std::vector supported_mediums; + + // Keep track of a channel before we pass it to EndpointChannelManager. + std::unique_ptr channel; + + // Crypto context; initially empty; established first thing after channel + // creation by running UKey2 session. While it is in progress, we keep track + // of channel ourselves. Once it is done, we pass channel over to + // EndpointChannelManager. We keep crypto context until connection is + // accepted. Crypto context is passed over to channel_manager_ before + // switching to connected state, where Payload may be exchanged. + std::unique_ptr ukey2; + }; + + // @EncryptionRunnerThread + // Called internally when DH session has negotiated a key successfully. + void OnEncryptionSuccessImpl(const std::string& endpoint_id, + std::unique_ptr ukey2, + const std::string& auth_token, + const ByteArray& raw_auth_token); + + // @EncryptionRunnerThread + // Called internally when DH session was not able to negotiate a key. + void OnEncryptionFailureImpl(const std::string& endpoint_id, + EndpointChannel* channel); + + EncryptionRunner::ResultListener GetResultListener(); + + void OnEncryptionSuccessRunnable( + const std::string& endpoint_id, + std::unique_ptr ukey2, + const std::string& auth_token, const ByteArray& raw_auth_token); + void OnEncryptionFailureRunnable(const std::string& endpoint_id, + EndpointChannel* endpoint_channel); + static Exception WriteConnectionRequestFrame( EndpointChannel* endpoint_channel, const std::string& local_endpoint_id, const std::string& local_endpoint_name, std::int32_t nonce, @@ -236,6 +300,25 @@ class BasePcpHandler : public PcpHandler, bool IsPreferred(const BasePcpHandler::DiscoveredEndpoint& new_endpoint, const BasePcpHandler::DiscoveredEndpoint& old_endpoint); + // Returns true, if connection party should respect the specified topology. + bool ShouldEnforceTopologyConstraints() const; + + // Returns true, if connection party should attempt to upgrade itself to + // use a higher bandwidth medium, if it is available. + bool AutoUpgradeBandwidth() const; + + // Returns true if the incoming connection should be killed. This only + // happens when an incoming connection arrives while we have an outgoing + // connection to the same endpoint and we need to stop one connection. + bool BreakTie(ClientProxy* client, const std::string& endpoint_id, + std::int32_t incoming_nonce, EndpointChannel* channel); + // 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, + PendingConnectionInfo* info); + // Called when an incoming connection has been accepted by both sides. // // @param client_proxy The client @@ -285,6 +368,12 @@ class BasePcpHandler : public PcpHandler, ScheduledExecutor alarm_executor_; SingleThreadExecutor serial_executor_; + // A map of endpoint id -> PendingConnectionInfo. Entries in this map imply + // that there is an active connection to the endpoint and we're waiting for + // both sides to accept before allowing payloads through. Once the fate of + // the connection is decided (either accepted or rejected), it should be + // removed from this map. + absl::flat_hash_map pending_connections_; // A map of endpoint id -> DiscoveredEndpoint. absl::flat_hash_map> discovered_endpoints_; diff --git a/cpp/core_v2/internal/base_pcp_handler_test.cc b/cpp/core_v2/internal/base_pcp_handler_test.cc index 756ea76b..8da33159 100644 --- a/cpp/core_v2/internal/base_pcp_handler_test.cc +++ b/cpp/core_v2/internal/base_pcp_handler_test.cc @@ -64,8 +64,18 @@ class MockPcpHandler : public BasePcpHandler { using BasePcpHandler::DiscoveredEndpoint; using BasePcpHandler::StartOperationResult; - MOCK_METHOD(Strategy, GetStrategy, (), (override)); - MOCK_METHOD(Pcp, GetPcp, (), (override)); + MOCK_METHOD(Strategy, GetStrategy, (), (const override)); + MOCK_METHOD(Pcp, GetPcp, (), (const override)); + + MOCK_METHOD(bool, HasOutgoingConnections, (ClientProxy * client), + (const, override)); + MOCK_METHOD(bool, HasIncomingConnections, (ClientProxy * client), + (const, override)); + + MOCK_METHOD(bool, CanSendOutgoingConnection, (ClientProxy * client), + (const, override)); + MOCK_METHOD(bool, CanReceiveIncomingConnection, (ClientProxy * client), + (const, override)); MOCK_METHOD(StartOperationResult, StartAdvertisingImpl, (ClientProxy * client, const string& service_id, @@ -91,18 +101,12 @@ class MockPcpHandler : public BasePcpHandler { std::unique_ptr endpoint) { BasePcpHandler::OnEndpointFound(client, std::move(endpoint)); } - void OnEndpointLost(ClientProxy* client, DiscoveredEndpoint* endpoint) { + void OnEndpointLost(ClientProxy* client, const DiscoveredEndpoint& endpoint) { BasePcpHandler::OnEndpointLost(client, endpoint); } }; -class MockDiscoveredEndpoint final : public MockPcpHandler::DiscoveredEndpoint { - public: - MOCK_METHOD(std::string, GetEndpointId, (), (const override)); - MOCK_METHOD(std::string, GetEndpointName, (), (const override)); - MOCK_METHOD(std::string, GetServiceId, (), (const override)); - MOCK_METHOD(Medium, GetMedium, (), (const override)); -}; +using MockDiscoveredEndpoint = MockPcpHandler::DiscoveredEndpoint; class BasePcpHandlerTest : public ::testing::Test { protected: @@ -193,8 +197,7 @@ class BasePcpHandlerTest : public ::testing::Test { EXPECT_CALL(*channel_a, GetMedium).WillRepeatedly(Return(Medium::BLE)); EXPECT_CALL(*channel_a, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); - EXPECT_CALL(*channel_a, IsPaused) - .WillRepeatedly(Return(false)); + EXPECT_CALL(*channel_a, IsPaused).WillRepeatedly(Return(false)); EXPECT_CALL(*channel_b, Read()) .WillRepeatedly(Invoke( [channel = channel_b.get()]() { return channel->DoRead(); })); @@ -206,11 +209,56 @@ class BasePcpHandlerTest : public ::testing::Test { EXPECT_CALL(*channel_b, GetMedium).WillRepeatedly(Return(Medium::BLE)); EXPECT_CALL(*channel_b, GetLastReadTimestamp) .WillRepeatedly(Return(absl::Now())); - EXPECT_CALL(*channel_b, IsPaused) - .WillRepeatedly(Return(false)); + EXPECT_CALL(*channel_b, IsPaused).WillRepeatedly(Return(false)); return std::make_pair(std::move(channel_a), std::move(channel_b)); } + void RequestConnection(const std::string& endpoint_id, + std::unique_ptr channel_a, + MockEndpointChannel* channel_b, ClientProxy* client, + MockPcpHandler* pcp_handler) { + ConnectionRequestInfo info{ + .name = "ABCD", + .listener = connection_listener_, + }; + 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_unique(MockDiscoveredEndpoint{ + .endpoint_id = endpoint_id, + .endpoint_name = info.name, + .service_id = "service", + .medium = Medium::BLE, + })); + auto other_client = std::make_unique(); + + // Run peer crypto in advance, if channel_b is provided. + // Otherwise stay in not-encrypted state. + if (channel_b != nullptr) { + encryption_runner->StartServer(other_client.get(), endpoint_id, channel_b, + {}); + } + EXPECT_EQ(pcp_handler->RequestConnection(client, endpoint_id, info), + Status{Status::kSuccess}); + NEARBY_LOG(INFO, "Stopping Encryption Runner"); + } + Pipe pipe_a_; Pipe pipe_b_; MockConnectionListener mock_connection_listener_; @@ -281,6 +329,104 @@ TEST_F(BasePcpHandlerTest, StopDiscoveryChangesState) { EXPECT_FALSE(client->IsDiscovering()); } +TEST_F(BasePcpHandlerTest, RequestConnectionChangesState) { + std::string endpoint_id{"1234"}; + auto client = std::make_unique(); + auto ecm = std::make_unique(); + auto em = std::make_unique(ecm.get()); + auto pcp_handler = std::make_unique(em.get(), ecm.get()); + StartDiscovery(client.get(), pcp_handler.get()); + auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto& channel_b = channel_pair.second; + RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b.get(), + client.get(), pcp_handler.get()); + NEARBY_LOG(INFO, "RequestConnection complete"); + channel_b->Close(); +} + +TEST_F(BasePcpHandlerTest, AcceptConnectionChangesState) { + std::string endpoint_id{"1234"}; + auto client = std::make_unique(); + auto ecm = std::make_unique(); + auto em = std::make_unique(ecm.get()); + auto pcp_handler = std::make_unique(em.get(), ecm.get()); + StartDiscovery(client.get(), pcp_handler.get()); + auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto& channel_b = channel_pair.second; + RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b.get(), + client.get(), pcp_handler.get()); + NEARBY_LOG(INFO, "Attempting to accept connection: id=%s", + endpoint_id.c_str()); + EXPECT_EQ(pcp_handler->AcceptConnection(client.get(), endpoint_id, {}), + Status{Status::kSuccess}); + NEARBY_LOG(INFO, "Closing connection: id=%s", endpoint_id.c_str()); + channel_b->Close(); +} + +TEST_F(BasePcpHandlerTest, RejectConnectionChangesState) { + std::string endpoint_id{"1234"}; + auto client = std::make_unique(); + auto ecm = std::make_unique(); + auto em = std::make_unique(ecm.get()); + auto pcp_handler = std::make_unique(em.get(), ecm.get()); + StartDiscovery(client.get(), pcp_handler.get()); + auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + 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.get(), pcp_handler.get()); + NEARBY_LOG(INFO, "Attempting to reject connection: id=%s", + endpoint_id.c_str()); + EXPECT_EQ(pcp_handler->RejectConnection(client.get(), endpoint_id), + Status{Status::kSuccess}); + NEARBY_LOG(INFO, "Closing connection: id=%s", endpoint_id.c_str()); + channel_b->Close(); +} + +TEST_F(BasePcpHandlerTest, OnIncomingFrameChangesState) { + std::string endpoint_id{"1234"}; + auto client = std::make_unique(); + auto ecm = std::make_unique(); + auto em = std::make_unique(ecm.get()); + auto pcp_handler = std::make_unique(em.get(), ecm.get()); + StartDiscovery(client.get(), pcp_handler.get()); + auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + auto& channel_b = channel_pair.second; + RequestConnection(endpoint_id, std::move(channel_pair.first), channel_b.get(), + client.get(), pcp_handler.get()); + NEARBY_LOG(INFO, "Attempting to accept connection: id=%s", + endpoint_id.c_str()); + EXPECT_CALL(mock_connection_listener_.accepted_cb, Call).Times(1); + EXPECT_EQ(pcp_handler->AcceptConnection(client.get(), endpoint_id, {}), + Status{Status::kSuccess}); + NEARBY_LOG(INFO, "Simulating remote accept: id=%s", endpoint_id.c_str()); + auto frame = + parser::FromBytes(parser::ForConnectionResponse(Status::kSuccess)); + pcp_handler->OnIncomingFrame(frame.result(), endpoint_id, client.get(), + Medium::BLE); + NEARBY_LOG(INFO, "Closing connection: id=%s", endpoint_id.c_str()); + channel_b->Close(); +} + +TEST_F(BasePcpHandlerTest, OnEndpointDisconnectChangesState) { + std::string endpoint_id{"1234"}; + auto client = std::make_unique(); + auto ecm = std::make_unique(); + auto em = std::make_unique(ecm.get()); + auto pcp_handler = std::make_unique(em.get(), ecm.get()); + StartDiscovery(client.get(), pcp_handler.get()); + auto channel_pair = SetupConnection(pipe_a_, pipe_b_); + 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.get(), pcp_handler.get()); + NEARBY_LOG(INFO, "Simulating disconnect event: id=%s", endpoint_id.c_str()); + CountDownLatch latch(1); + pcp_handler->OnEndpointDisconnect(client.get(), endpoint_id, &latch); + channel_b->Close(); + EXPECT_TRUE(latch.Await(absl::Milliseconds(5000)).result()); +} + } // namespace } // namespace connections } // namespace nearby diff --git a/cpp/core_v2/internal/ble_advertisement.h b/cpp/core_v2/internal/ble_advertisement.h index 2a86082e..885261bc 100644 --- a/cpp/core_v2/internal/ble_advertisement.h +++ b/cpp/core_v2/internal/ble_advertisement.h @@ -47,24 +47,21 @@ class BleAdvertisement { const std::string& endpoint_name, const std::string& bluetooth_mac_address); explicit BleAdvertisement(const ByteArray& ble_advertisement_bytes); - ~BleAdvertisement() = default; - BleAdvertisement(const BleAdvertisement&) = default; BleAdvertisement& operator=(const BleAdvertisement&) = default; BleAdvertisement(BleAdvertisement&&) = default; BleAdvertisement& operator=(BleAdvertisement&&) = default; + ~BleAdvertisement() = default; explicit operator ByteArray() const; - inline bool IsValid() const { return !endpoint_id_.empty(); } - inline Version GetVersion() const { return version_; } - inline Pcp GetPcp() const { return pcp_; } - inline ByteArray GetServiceIdHash() const{ return service_id_hash_; } - inline std::string GetEndpointId() const { return endpoint_id_; } - inline std::string GetEndpointName() const { return endpoint_name_; } - inline std::string GetBluetoothMacAddress() const { - return bluetooth_mac_address_; - } + bool IsValid() const { return !endpoint_id_.empty(); } + 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_; } + std::string GetBluetoothMacAddress() const { return bluetooth_mac_address_; } private: std::uint32_t ComputeEndpointNameLength( diff --git a/cpp/core_v2/internal/ble_advertisement_test.cc b/cpp/core_v2/internal/ble_advertisement_test.cc index 9ff3ffea..d2fd5228 100644 --- a/cpp/core_v2/internal/ble_advertisement_test.cc +++ b/cpp/core_v2/internal/ble_advertisement_test.cc @@ -9,21 +9,19 @@ namespace { const BleAdvertisement::Version kVersion = BleAdvertisement::Version::kV1; const Pcp kPcp = Pcp::kP2pCluster; -const char kServiceIDHashBytes[] = {0x0A, 0x0B, 0x0C}; +const char kServiceIDHashBytes[] = "\x0a\x0b\x0c"; const char kEndPointID[] = "AB12"; const char kEndpointName[] = "How much wood can a woodchuck chuck if a wood chuck would chuck wood?"; const char kBluetoothMacAddress[] = "00:00:E6:88:64:13"; TEST(BleAdvertisementTest, ConstructionWorks) { - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); - auto is_valid = ble_advertisement.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; - EXPECT_TRUE(is_valid); + 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()); @@ -35,14 +33,12 @@ TEST(BleAdvertisementTest, ConstructionWorks) { TEST(BleAdvertisementTest, ConstructionWorksWithEmptyEndpointName) { std::string empty_endpoint_name; - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - empty_endpoint_name, kBluetoothMacAddress); - auto is_valid = ble_advertisement.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, empty_endpoint_name, + kBluetoothMacAddress}; - EXPECT_TRUE(is_valid); + 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()); @@ -52,16 +48,14 @@ TEST(BleAdvertisementTest, ConstructionWorksWithEmptyEndpointName) { } TEST(BleAdvertisementTest, ConstructionWorksWithEmojiEndpointName) { - std::string emoji_endpoint_name("\u0001F450 \u0001F450"); + std::string emoji_endpoint_name{"\u0001F450 \u0001F450"}; - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - emoji_endpoint_name, kBluetoothMacAddress); - auto is_valid = ble_advertisement.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, emoji_endpoint_name, + kBluetoothMacAddress}; - EXPECT_TRUE(is_valid); + 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()); @@ -74,70 +68,56 @@ TEST(BleAdvertisementTest, ConstructionFailsWithLongEndpointName) { std::string long_endpoint_name(BleAdvertisement::kMaxEndpointNameLength + 1, 'x'); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - long_endpoint_name, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, long_endpoint_name, + kBluetoothMacAddress}; - auto is_valid = ble_advertisement.IsValid(); - - EXPECT_FALSE(is_valid); + EXPECT_FALSE(ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionFailsWithBadVersion) { auto bad_version = static_cast(666); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(bad_version, kPcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{bad_version, kPcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; - auto is_valid = ble_advertisement.IsValid(); - - EXPECT_FALSE(is_valid); + EXPECT_FALSE(ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionFailsWithBadPCP) { auto bad_pcp = static_cast(666); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, bad_pcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, bad_pcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; - auto is_valid = ble_advertisement.IsValid(); - - EXPECT_FALSE(is_valid); + EXPECT_FALSE(ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionSucceedsWithEmptyBluetoothMacAddress) { std::string empty_bluetooth_mac_address = ""; - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, empty_bluetooth_mac_address); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + empty_bluetooth_mac_address}; - auto is_valid = ble_advertisement.IsValid(); - - EXPECT_TRUE(is_valid); + EXPECT_TRUE(ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionSucceedsWithInvalidBluetoothMacAddress) { std::string bad_bluetooth_mac_address = "022:00"; - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, bad_bluetooth_mac_address); - auto is_valid = ble_advertisement.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + bad_bluetooth_mac_address}; - EXPECT_TRUE(is_valid); + 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()); @@ -148,17 +128,15 @@ TEST(BleAdvertisementTest, ConstructionSucceedsWithInvalidBluetoothMacAddress) { TEST(BleAdvertisementTest, ConstructionFromBytesWorks) { // Serialize good data into a good Ble Advertisement. - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto org_ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement org_ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; auto ble_advertisement_bytes = ByteArray(org_ble_advertisement); - auto ble_advertisement = BleAdvertisement(ble_advertisement_bytes); - auto is_valid = ble_advertisement.IsValid(); + BleAdvertisement ble_advertisement{ble_advertisement_bytes}; - EXPECT_TRUE(is_valid); + 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()); @@ -171,11 +149,10 @@ TEST(BleAdvertisementTest, ConstructionFromBytesWorks) { // in the future. TEST(BleAdvertisementTest, ConstructionFromLongLengthBytesWorks) { // Serialize good data into a good Ble Advertisement. - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; auto ble_advertisement_bytes = ByteArray(ble_advertisement); // Add bytes to the end of the valid Ble advertisement. @@ -187,10 +164,9 @@ TEST(BleAdvertisementTest, ConstructionFromLongLengthBytesWorks) { ble_advertisement_bytes.data(), ble_advertisement_bytes.size()); - auto long_ble_advertisement = BleAdvertisement(long_ble_advertisement_bytes); - auto is_valid = long_ble_advertisement.IsValid(); + BleAdvertisement long_ble_advertisement{long_ble_advertisement_bytes}; - EXPECT_TRUE(is_valid); + 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()); @@ -201,55 +177,47 @@ TEST(BleAdvertisementTest, ConstructionFromLongLengthBytesWorks) { } TEST(BleAdvertisementTest, ConstructionFromNullBytesFails) { - auto ble_advertisement = BleAdvertisement(ByteArray()); - auto is_valid = ble_advertisement.IsValid(); + BleAdvertisement ble_advertisement{ByteArray{}}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionFromShortLengthBytesFails) { // Serialize good data into a good Ble Advertisement. - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; auto ble_advertisement_bytes = ByteArray(ble_advertisement); // Shorten the valid Ble Advertisement. - auto short_ble_advertisement_bytes( - ByteArray(ble_advertisement_bytes.data(), - BleAdvertisement::kMinAdvertisementLength - 1)); + ByteArray short_ble_advertisement_bytes{ + ble_advertisement_bytes.data(), + BleAdvertisement::kMinAdvertisementLength - 1}; - auto short_ble_advertisement = - BleAdvertisement(short_ble_advertisement_bytes); - auto is_valid = short_ble_advertisement.IsValid(); + BleAdvertisement short_ble_advertisement{short_ble_advertisement_bytes}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(short_ble_advertisement.IsValid()); } TEST(BleAdvertisementTest, ConstructionFromByesWithWrongEndpointNameLengthFails) { // Serialize good data into a good Ble Advertisement. - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto ble_advertisement = - BleAdvertisement(kVersion, kPcp, service_id_hash, kEndPointID, - kEndpointName, kBluetoothMacAddress); + ByteArray service_id_hash{kServiceIDHashBytes}; + BleAdvertisement ble_advertisement{kVersion, kPcp, service_id_hash, + kEndPointID, kEndpointName, + kBluetoothMacAddress}; auto ble_advertisement_bytes = ByteArray(ble_advertisement); // Corrupt the EndpointNameLength bits. - std::string corrupt_ble_advertisement_string(ble_advertisement_bytes.data(), - ble_advertisement_bytes.size()); + auto corrupt_ble_advertisement_string = std::string(ble_advertisement_bytes); corrupt_ble_advertisement_string[8] ^= 0x0FF; auto corrupt_ble_advertisement_bytes = ByteArray(corrupt_ble_advertisement_string); - auto corrupt_ble_advertisement = - BleAdvertisement(corrupt_ble_advertisement_bytes); - auto is_valid = corrupt_ble_advertisement.IsValid(); + BleAdvertisement corrupt_ble_advertisement{corrupt_ble_advertisement_bytes}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(corrupt_ble_advertisement.IsValid()); } } // namespace diff --git a/cpp/core_v2/internal/bluetooth_device_name.cc b/cpp/core_v2/internal/bluetooth_device_name.cc new file mode 100644 index 00000000..857c9cf4 --- /dev/null +++ b/cpp/core_v2/internal/bluetooth_device_name.cc @@ -0,0 +1,187 @@ +#include "core_v2/internal/bluetooth_device_name.h" + +#include + +#include +#include + +#include "platform_v2/base/base64_utils.h" +#include "platform_v2/public/logging.h" + +namespace location { +namespace nearby { +namespace connections { + +// TODO(edwinwu): Define bitfield struct to replace pointer arithmetic for +// those bit parsing. + +BluetoothDeviceName::BluetoothDeviceName(Version version, Pcp pcp, + absl::string_view endpoint_id, + const ByteArray& service_id_hash, + absl::string_view endpoint_name) { + if (version != Version::kV1 || endpoint_id.empty() || + endpoint_id.length() != kEndpointIdLength || + service_id_hash.size() != kServiceIdHashLength) { + return; + } + switch (pcp) { + case Pcp::kP2pCluster: // Fall through + case Pcp::kP2pStar: // Fall through + case Pcp::kP2pPointToPoint: + break; + default: + return; + } + + version_ = version; + pcp_ = pcp; + endpoint_id_ = endpoint_id; + service_id_hash_ = service_id_hash; + endpoint_name_ = endpoint_name; +} + +BluetoothDeviceName::BluetoothDeviceName( + absl::string_view bluetooth_device_name_string) { + ByteArray bluetooth_device_name_bytes = + Base64Utils::Decode(bluetooth_device_name_string); + + if (bluetooth_device_name_bytes.Empty()) { + NEARBY_LOG( + INFO, + "Cannot deserialize BluetoothDeviceName: failed Base64 decoding of %s", + std::string(bluetooth_device_name_string).c_str()); + return; + } + + if (bluetooth_device_name_bytes.size() > kMaxBluetoothDeviceNameLength) { + NEARBY_LOG(INFO, + "Cannot deserialize BluetoothDeviceName: expecting max %d raw " + "bytes, got %" PRIu64, + kMaxBluetoothDeviceNameLength, + bluetooth_device_name_bytes.size()); + return; + } + + if (bluetooth_device_name_bytes.size() < kMinBluetoothDeviceNameLength) { + NEARBY_LOG(INFO, + "Cannot deserialize BluetoothDeviceName: expecting min %d raw " + "bytes, got %" PRIu64, + kMinBluetoothDeviceNameLength, + bluetooth_device_name_bytes.size()); + return; + } + + // The upper 3 bits are supposed to be the version. + version_ = static_cast( + (bluetooth_device_name_bytes.data()[0] & kVersionBitmask) >> 5); + const char* read_ptr = bluetooth_device_name_bytes.data(); + switch (version_) { + case Version::kV1: + // The lower 5 bits of the V1 payload are supposed to be the Pcp. + pcp_ = static_cast(*read_ptr & kPcpBitmask); + read_ptr++; + switch (pcp_) { + case Pcp::kP2pCluster: // Fall through + case Pcp::kP2pStar: // Fall through + case Pcp::kP2pPointToPoint: { + // The next 32 bits are supposed to be the endpoint_id. + endpoint_id_ = std::string(read_ptr, kEndpointIdLength); + read_ptr += kEndpointIdLength; + + // The next 24 bits are supposed to be the service_id_hash. + service_id_hash_ = ByteArray(read_ptr, kServiceIdHashLength); + read_ptr += kServiceIdHashLength; + + // The next 56 bits are supposed to be reserved, and can be left + // untouched. + read_ptr += kReservedLength; + + // The next 8 bits are supposed to be the length of the endpoint_name. + std::uint32_t expected_endpoint_name_length = + static_cast(*read_ptr & + kEndpointNameLengthBitmask); + read_ptr++; + + // Check that the stated endpoint_name_length is the same as what we + // received (based off of the length of bluetooth_device_name_bytes). + std::uint32_t actual_endpoint_name_length = + kMaxBluetoothDeviceNameLength - + bluetooth_device_name_bytes.size(); + if (actual_endpoint_name_length != expected_endpoint_name_length) { + NEARBY_LOG(INFO, + "Cannot deserialize BluetoothDeviceName: expected " + "endpointName to be %d bytes, got %d bytes", + expected_endpoint_name_length, + actual_endpoint_name_length); + + endpoint_id_.empty(); + return; + } + + endpoint_name_ = std::string{read_ptr, actual_endpoint_name_length}; + read_ptr += actual_endpoint_name_length; + } break; + + default: + // TODO(edwinwu): [ANALYTICIZE] This either represents corruption over + // the air, or older versions of GmsCore intermingling with newer + // ones. + NEARBY_LOG( + INFO, + "Cannot deserialize BluetoothDeviceName: unsupported V1 PCP %d", + pcp_); + break; + } + break; + + default: + // TODO(edwinwu): [ANALYTICIZE] This either represents corruption over + // the air, or older versions of GmsCore intermingling with newer ones. + NEARBY_LOG( + INFO, + "Cannot deserialize BluetoothDeviceName: unsupported Version %d", + version_); + break; + } +} + +BluetoothDeviceName::operator std::string() const { + if (!IsValid()) { + return ""; + } + + std::string usable_endpoint_name(endpoint_name_); + if (endpoint_name_.size() > kMaxEndpointNameLength) { + 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); + } + + std::string out; + + // The upper 3 bits are the Version. + auto version_and_pcp_byte = static_cast( + (static_cast(Version::kV1) << 5) & kVersionBitmask); + // The lower 5 bits are the PCP. + version_and_pcp_byte |= + static_cast(static_cast(pcp_) & kPcpBitmask); + // TODO(edwinwu): Change to StrCat to gain performance. + out.reserve(kMaxBluetoothDeviceNameLength - + (kMaxEndpointNameLength - usable_endpoint_name.length())); + out.append(1, version_and_pcp_byte); + out.append(endpoint_id_); + out.append(std::string(service_id_hash_)); + ByteArray reserverdBytes{kReservedLength}; + out.append(std::string(reserverdBytes)); + out.append(1, usable_endpoint_name.size()); + out.append(usable_endpoint_name); + + return Base64Utils::Encode(ByteArray{std::move(out)}); +} + +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/bluetooth_device_name.h b/cpp/core_v2/internal/bluetooth_device_name.h new file mode 100644 index 00000000..b92d433a --- /dev/null +++ b/cpp/core_v2/internal/bluetooth_device_name.h @@ -0,0 +1,73 @@ +#ifndef CORE_V2_INTERNAL_BLUETOOTH_DEVICE_NAME_H_ +#define CORE_V2_INTERNAL_BLUETOOTH_DEVICE_NAME_H_ + +#include + +#include "core_v2/internal/pcp.h" +#include "platform_v2/base/byte_array.h" +#include "absl/strings/string_view.h" + +namespace location { +namespace nearby { +namespace connections { + +// Represents the format of the Bluetooth device name used in Advertising + +// Discovery. +// +//

See go/nearby-offline-data-interchange-formats for the specification. +class BluetoothDeviceName { + public: + // Versions of the BluetoothDeviceName. + enum class Version { + kUndefined = 0, + kV1 = 1, + // Version is only allocated 3 bits in the BluetoothDeviceName, so this + // can never go beyond V7. + }; + + static constexpr int kServiceIdHashLength = 3; + + BluetoothDeviceName() = default; + BluetoothDeviceName(Version version, Pcp pcp, absl::string_view endpoint_id, + const ByteArray& service_id_hash, + absl::string_view endpoint_name); + explicit BluetoothDeviceName(absl::string_view bluetooth_device_name_string); + BluetoothDeviceName(const BluetoothDeviceName&) = default; + BluetoothDeviceName& operator=(const BluetoothDeviceName&) = default; + BluetoothDeviceName(BluetoothDeviceName&&) = default; + BluetoothDeviceName& operator=(BluetoothDeviceName&&) = default; + ~BluetoothDeviceName() = default; + + explicit operator std::string() const; + + bool IsValid() const { return !endpoint_id_.empty(); } + Version GetVersion() const { return version_; } + 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_; } + + private: + static constexpr int kMaxBluetoothDeviceNameLength = 147; + static constexpr int kEndpointIdLength = 4; + static constexpr int kReservedLength = 7; + static constexpr int kMaxEndpointNameLength = 131; + static constexpr int kMinBluetoothDeviceNameLength = + kMaxBluetoothDeviceNameLength - kMaxEndpointNameLength; + + static constexpr int kVersionBitmask = 0x0E0; + static constexpr int kPcpBitmask = 0x01F; + static constexpr int kEndpointNameLengthBitmask = 0x0FF; + + Version version_{Version::kUndefined}; + Pcp pcp_{Pcp::kUnknown}; + std::string endpoint_id_; + ByteArray service_id_hash_; + std::string endpoint_name_; +}; + +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_BLUETOOTH_DEVICE_NAME_H_ diff --git a/cpp/core_v2/internal/bluetooth_device_name_test.cc b/cpp/core_v2/internal/bluetooth_device_name_test.cc new file mode 100644 index 00000000..69196b46 --- /dev/null +++ b/cpp/core_v2/internal/bluetooth_device_name_test.cc @@ -0,0 +1,149 @@ +#include "core_v2/internal/bluetooth_device_name.h" + +#include +#include + +#include "platform_v2/base/base64_utils.h" +#include "gtest/gtest.h" + +namespace location { +namespace nearby { +namespace connections { +namespace { + +const BluetoothDeviceName::Version kVersion = BluetoothDeviceName::Version::kV1; +const Pcp kPcp = Pcp::kP2pCluster; +// TODO(edwinwu): Replace absl::string_view in other medium tests, too. +inline constexpr absl::string_view kEndPointID = "AB12"; +inline constexpr absl::string_view kServiceIDHashBytes = "\x0a\x0b\x0c"; +inline constexpr absl::string_view kEndPointName = "RAWK + ROWL!"; + +TEST(BluetoothDeviceNameTest, ConstructionWorks) { + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, kEndPointID, + service_id_hash, kEndPointName}; + + 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()); +} + +TEST(BluetoothDeviceNameTest, ConstructionWorksWithEmptyEndpointName) { + std::string empty_endpoint_name; + + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{ + kVersion, kPcp, kEndPointID, service_id_hash, empty_endpoint_name}; + + 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()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithBadVersion) { + auto bad_version = static_cast(666); + + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{bad_version, kPcp, kEndPointID, + service_id_hash, kEndPointName}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithBadPcp) { + auto bad_pcp = static_cast(666); + + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{kVersion, bad_pcp, kEndPointID, + service_id_hash, kEndPointName}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortEndpointId) { + std::string short_endpoint_id("AB1"); + + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, short_endpoint_id, + service_id_hash, kEndPointName}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithLongEndpointId) { + std::string long_endpoint_id("AB12X"); + + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, long_endpoint_id, + service_id_hash, kEndPointName}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortServiceIdHash) { + char short_service_id_hash_bytes[] = "\x0a\x0b"; + + ByteArray short_service_id_hash{short_service_id_hash_bytes}; + BluetoothDeviceName bluetooth_device_name{ + kVersion, kPcp, kEndPointID, short_service_id_hash, kEndPointName}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithLongServiceIdHash) { + char long_service_id_hash_bytes[] = "\x0a\x0b\x0c\x0d"; + + ByteArray long_service_id_hash{long_service_id_hash_bytes}; + BluetoothDeviceName bluetooth_device_name{ + kVersion, kPcp, kEndPointID, long_service_id_hash, kEndPointName}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithShortStringLength) { + char bluetooth_device_name_string[] = "X"; + + ByteArray bluetooth_device_name_bytes{bluetooth_device_name_string}; + BluetoothDeviceName bluetooth_device_name{ + Base64Utils::Encode(bluetooth_device_name_bytes)}; + + EXPECT_FALSE(bluetooth_device_name.IsValid()); +} + +TEST(BluetoothDeviceNameTest, ConstructionFailsWithWrongEndpointNameLength) { + // Serialize good data into a good Bluetooth Device Name. + ByteArray service_id_hash{kServiceIDHashBytes}; + BluetoothDeviceName bluetooth_device_name{kVersion, kPcp, kEndPointID, + service_id_hash, kEndPointName}; + auto bluetooth_device_name_string = std::string(bluetooth_device_name); + + // Base64-decode the good Bluetooth Device Name. + ByteArray bluetooth_device_name_bytes = + Base64Utils::Decode(bluetooth_device_name_string); + // Corrupt the EndpointNameLength bits (120-127) by reversing all of them. + std::string corrupt_string(bluetooth_device_name_bytes.data(), + bluetooth_device_name_bytes.size()); + corrupt_string[15] ^= 0x0FF; + // Base64-encode the corrupted bytes into a corrupt Bluetooth Device Name. + ByteArray corrupt_bluetooth_device_name_bytes{corrupt_string.data(), + corrupt_string.size()}; + std::string corrupt_bluetooth_device_name_string( + Base64Utils::Encode(corrupt_bluetooth_device_name_bytes)); + + // And deserialize the corrupt Bluetooth Device Name. + BluetoothDeviceName corrupt_bluetooth_device_name( + corrupt_bluetooth_device_name_string); + + EXPECT_TRUE(corrupt_bluetooth_device_name.IsValid()); +} + +} // namespace +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/BUILD b/cpp/core_v2/internal/mediums/BUILD index 5a33fe85..c15fc9bb 100644 --- a/cpp/core_v2/internal/mediums/BUILD +++ b/cpp/core_v2/internal/mediums/BUILD @@ -5,6 +5,7 @@ cc_library( "ble_advertisement.cc", "ble_advertisement_header.cc", "ble_packet.cc", + "bloom_filter.cc", "bluetooth_radio.cc", "uuid.cc", ], @@ -14,6 +15,7 @@ cc_library( "ble_advertisement_header.h", "ble_packet.h", "ble_peripheral.h", + "bloom_filter.h", "bluetooth_radio.h", "lost_entity_tracker.h", "uuid.h", @@ -23,12 +25,15 @@ cc_library( ], deps = [ "//platform_v2/base", - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//absl/container:flat_hash_map", "//absl/container:flat_hash_set", + "//absl/numeric:int128", "//absl/strings", "//absl/time", + "//smhasher:libmurmur3", ], ) @@ -41,7 +46,8 @@ cc_library( ], deps = [ "//platform_v2/base", - "//platform_v2/public", + "//platform_v2/public:comm", + "//platform_v2/public:types", ], ) @@ -53,6 +59,7 @@ cc_test( "ble_advertisement_test.cc", "ble_packet_test.cc", "ble_peripheral_test.cc", + "bloom_filter_test.cc", "bluetooth_radio_test.cc", "lost_entity_tracker_test.cc", "uuid_test.cc", @@ -62,8 +69,9 @@ cc_test( ":mediums", "//platform_v2/base", "//platform_v2/impl/g3", # build_cleaner: keep - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", + "//platform_v2/public:types", "//testing/base/public:gunit_main", "//absl/time", ], diff --git a/cpp/core_v2/internal/mediums/ble_advertisement_header.h b/cpp/core_v2/internal/mediums/ble_advertisement_header.h index aa8163df..bcec8d55 100644 --- a/cpp/core_v2/internal/mediums/ble_advertisement_header.h +++ b/cpp/core_v2/internal/mediums/ble_advertisement_header.h @@ -43,12 +43,11 @@ class BleAdvertisementHeader { const ByteArray &advertisement_hash); explicit BleAdvertisementHeader( const std::string &ble_advertisement_header_string); - ~BleAdvertisementHeader() = default; - BleAdvertisementHeader(const BleAdvertisementHeader &) = default; BleAdvertisementHeader &operator=(const BleAdvertisementHeader &) = default; BleAdvertisementHeader(BleAdvertisementHeader &&) = default; BleAdvertisementHeader &operator=(BleAdvertisementHeader &&) = default; + ~BleAdvertisementHeader() = default; // Produces an encoded binary string which can be decoded by the explicit // constructor. The returned string is empty if BleAdvertisementHeader is not diff --git a/cpp/core_v2/internal/mediums/ble_advertisement_header_test.cc b/cpp/core_v2/internal/mediums/ble_advertisement_header_test.cc index 30bfe536..36999641 100644 --- a/cpp/core_v2/internal/mediums/ble_advertisement_header_test.cc +++ b/cpp/core_v2/internal/mediums/ble_advertisement_header_test.cc @@ -16,11 +16,11 @@ constexpr char kServiceIDBloomFilter[] = constexpr char kAdvertisementHash[] = "\x0a\x0b\x0c\x0d"; TEST(BleAdvertisementHeaderTest, ConstructionWorks) { - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; EXPECT_TRUE(ble_advertisement_header.IsValid()); EXPECT_EQ(kVersion, ble_advertisement_header.GetVersion()); @@ -34,11 +34,11 @@ TEST(BleAdvertisementHeaderTest, ConstructionWorks) { TEST(BleAdvertisementHeaderTest, ConstructionFailsWithBadVersion) { auto bad_version = static_cast(666); - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader ble_advertisement_header( - bad_version, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader ble_advertisement_header{ + bad_version, kNumSlots, service_id_bloom_filter, advertisement_hash}; EXPECT_FALSE(ble_advertisement_header.IsValid()); } @@ -47,12 +47,12 @@ TEST(BleAdvertisementHeaderTest, ConstructionFailsWithShortServiceIdBloomFilter) { char short_service_id_bloom_filter[] = "\x01\x02\x03\x04\x05\x06\x07\x08\x09"; - ByteArray short_service_id_bloom_filter_bytes(short_service_id_bloom_filter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray short_service_id_bloom_filter_bytes{short_service_id_bloom_filter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader ble_advertisement_header( + BleAdvertisementHeader ble_advertisement_header{ kVersion, kNumSlots, short_service_id_bloom_filter_bytes, - advertisement_hash); + advertisement_hash}; EXPECT_FALSE(ble_advertisement_header.IsValid()); } @@ -62,11 +62,11 @@ TEST(BleAdvertisementHeaderTest, char long_service_id_bloom_filter[] = "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b"; - ByteArray service_id_bloom_filter(long_service_id_bloom_filter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray service_id_bloom_filter{long_service_id_bloom_filter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; EXPECT_FALSE(ble_advertisement_header.IsValid()); } @@ -74,38 +74,37 @@ TEST(BleAdvertisementHeaderTest, TEST(BleAdvertisementHeaderTest, ConstructionFailsWithShortAdvertisementHash) { char short_advertisement_hash[] = "\x0a\x0b\x0c"; - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(short_advertisement_hash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{short_advertisement_hash}; - BleAdvertisementHeader ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; EXPECT_FALSE(ble_advertisement_header.IsValid()); } TEST(BleAdvertisementHeaderTest, ConstructionFailsWithLongAdvertisementHash) { - char long_advertisement_hash[] = "\x0a\x0b\x0c\x0d\0x0e"; + char long_advertisement_hash[] = "\x0a\x0b\x0c\x0d\x0e"; - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(long_advertisement_hash, - sizeof(long_advertisement_hash) / sizeof(char)); - BleAdvertisementHeader ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{long_advertisement_hash}; + BleAdvertisementHeader ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; EXPECT_FALSE(ble_advertisement_header.IsValid()); } TEST(BleAdvertisementHeaderTest, ConstructionFromSerializedStringWorks) { - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader org_ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader org_ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; auto ble_advertisement_header_string = std::string(org_ble_advertisement_header); - auto ble_advertisement_header = - BleAdvertisementHeader(ble_advertisement_header_string); + BleAdvertisementHeader ble_advertisement_header{ + ble_advertisement_header_string}; EXPECT_TRUE(ble_advertisement_header.IsValid()); EXPECT_EQ(kVersion, ble_advertisement_header.GetVersion()); @@ -117,24 +116,24 @@ TEST(BleAdvertisementHeaderTest, ConstructionFromSerializedStringWorks) { } TEST(BleAdvertisementHeaderTest, ConstructionFromExtraBytesWorks) { - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; auto ble_advertisement_header_string = std::string(ble_advertisement_header); // Base64 decode the string, add a character, and then re-encode it. ByteArray ble_advertisement_header_bytes = Base64Utils::Decode(ble_advertisement_header_string); - ByteArray long_ble_advertisement_header_bytes( - ble_advertisement_header_bytes.size() + 1); + ByteArray long_ble_advertisement_header_bytes{ + ble_advertisement_header_bytes.size() + 1}; long_ble_advertisement_header_bytes.CopyAt(0, ble_advertisement_header_bytes); - std::string long_ble_advertisement_header_string = - Base64Utils::Encode(long_ble_advertisement_header_bytes); + std::string long_ble_advertisement_header_string{ + Base64Utils::Encode(long_ble_advertisement_header_bytes)}; - auto long_ble_advertisement_header = - BleAdvertisementHeader(long_ble_advertisement_header_string); + BleAdvertisementHeader long_ble_advertisement_header{ + long_ble_advertisement_header_string}; EXPECT_TRUE(long_ble_advertisement_header.IsValid()); EXPECT_EQ(kVersion, long_ble_advertisement_header.GetVersion()); @@ -146,25 +145,25 @@ TEST(BleAdvertisementHeaderTest, ConstructionFromExtraBytesWorks) { } TEST(BleAdvertisementHeaderTest, ConstructionFromShortLengthFails) { - ByteArray service_id_bloom_filter(kServiceIDBloomFilter); - ByteArray advertisement_hash(kAdvertisementHash); + ByteArray service_id_bloom_filter{kServiceIDBloomFilter}; + ByteArray advertisement_hash{kAdvertisementHash}; - BleAdvertisementHeader ble_advertisement_header( - kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash); + BleAdvertisementHeader ble_advertisement_header{ + kVersion, kNumSlots, service_id_bloom_filter, advertisement_hash}; auto ble_advertisement_header_string = std::string(ble_advertisement_header); // Base64 decode the string, remove a character, and then re-encode it. ByteArray ble_advertisement_header_bytes = Base64Utils::Decode(ble_advertisement_header_string); - ByteArray short_ble_advertisement_header_bytes( - ble_advertisement_header_bytes.size() - 1); + ByteArray short_ble_advertisement_header_bytes{ + ble_advertisement_header_bytes.size() - 1}; short_ble_advertisement_header_bytes.CopyAt(0, ble_advertisement_header_bytes); - std::string short_ble_advertisement_header_string = - Base64Utils::Encode(short_ble_advertisement_header_bytes); + std::string short_ble_advertisement_header_string{ + Base64Utils::Encode(short_ble_advertisement_header_bytes)}; - auto short_ble_advertisement_header = - BleAdvertisementHeader(short_ble_advertisement_header_string); + BleAdvertisementHeader short_ble_advertisement_header{ + short_ble_advertisement_header_string}; EXPECT_FALSE(short_ble_advertisement_header.IsValid()); } diff --git a/cpp/core_v2/internal/mediums/ble_packet.h b/cpp/core_v2/internal/mediums/ble_packet.h index 159f6349..bbdae131 100644 --- a/cpp/core_v2/internal/mediums/ble_packet.h +++ b/cpp/core_v2/internal/mediums/ble_packet.h @@ -22,12 +22,11 @@ class BlePacket { BlePacket() = default; BlePacket(const ByteArray& service_id_hash, const ByteArray& data); explicit BlePacket(const ByteArray& ble_packet_byte); - ~BlePacket() = default; - BlePacket(const BlePacket&) = default; BlePacket& operator=(const BlePacket&) = default; BlePacket(BlePacket&&) = default; BlePacket& operator=(BlePacket&&) = default; + ~BlePacket() = default; explicit operator ByteArray() const; diff --git a/cpp/core_v2/internal/mediums/ble_packet_test.cc b/cpp/core_v2/internal/mediums/ble_packet_test.cc index b9a1c858..b5e33d45 100644 --- a/cpp/core_v2/internal/mediums/ble_packet_test.cc +++ b/cpp/core_v2/internal/mediums/ble_packet_test.cc @@ -11,10 +11,10 @@ constexpr char kServiceIDHash[] = "\x0a\x0b\x0c"; constexpr char kData[] = "\x01\x02\x03\x04\x05"; TEST(BlePacketTest, ConstructionWorks) { - ByteArray service_id_hash(kServiceIDHash); - ByteArray data(kData); + ByteArray service_id_hash{kServiceIDHash}; + ByteArray data{kData}; - BlePacket ble_packet(service_id_hash, data); + BlePacket ble_packet{service_id_hash, data}; EXPECT_TRUE(ble_packet.IsValid()); EXPECT_EQ(service_id_hash, ble_packet.GetServiceIdHash()); @@ -22,12 +22,12 @@ TEST(BlePacketTest, ConstructionWorks) { } TEST(BlePacketTest, ConstructionWorksWithEmptyData) { - char empty_data[] = {}; + char empty_data[] = ""; - ByteArray service_id_hash(kServiceIDHash); - ByteArray data(empty_data); + ByteArray service_id_hash{kServiceIDHash}; + ByteArray data{empty_data}; - BlePacket ble_packet(service_id_hash, data); + BlePacket ble_packet{service_id_hash, data}; EXPECT_TRUE(ble_packet.IsValid()); EXPECT_EQ(service_id_hash, ble_packet.GetServiceIdHash()); @@ -37,8 +37,8 @@ TEST(BlePacketTest, ConstructionWorksWithEmptyData) { TEST(BlePacketTest, ConstructionFailsWithShortServiceIdHash) { char short_service_id_hash[] = "\x0a\x0b"; - ByteArray service_id_hash(short_service_id_hash); - ByteArray data(kData); + ByteArray service_id_hash{short_service_id_hash}; + ByteArray data{kData}; BlePacket ble_packet(service_id_hash, data); @@ -48,22 +48,22 @@ TEST(BlePacketTest, ConstructionFailsWithShortServiceIdHash) { TEST(BlePacketTest, ConstructionFailsWithLongServiceIdHash) { char long_service_id_hash[] = "\x0a\x0b\x0c\x0d"; - ByteArray service_id_hash(long_service_id_hash); - ByteArray data(kData); + ByteArray service_id_hash{long_service_id_hash}; + ByteArray data{kData}; - BlePacket ble_packet(service_id_hash, data); + BlePacket ble_packet{service_id_hash, data}; EXPECT_FALSE(ble_packet.IsValid()); } TEST(BlePacketTest, ConstructionFromSerializedBytesWorks) { - ByteArray service_id_hash(kServiceIDHash); - ByteArray data(kData); + ByteArray service_id_hash{kServiceIDHash}; + ByteArray data{kData}; - BlePacket org_ble_packet(service_id_hash, data); - ByteArray ble_packet_bytes(org_ble_packet); + BlePacket org_ble_packet{service_id_hash, data}; + ByteArray ble_packet_bytes{org_ble_packet}; - BlePacket ble_packet(ble_packet_bytes); + BlePacket ble_packet{ble_packet_bytes}; EXPECT_TRUE(ble_packet.IsValid()); EXPECT_EQ(service_id_hash, ble_packet.GetServiceIdHash()); @@ -71,22 +71,22 @@ TEST(BlePacketTest, ConstructionFromSerializedBytesWorks) { } TEST(BlePacketTest, ConstructionFromNullBytesFails) { - BlePacket ble_packet(ByteArray{}); + BlePacket ble_packet{ByteArray{}}; EXPECT_FALSE(ble_packet.IsValid()); } TEST(BlePacketTest, ConstructionFromShortLengthDataFails) { - ByteArray service_id_hash(kServiceIDHash); - ByteArray data(kData); + ByteArray service_id_hash{kServiceIDHash}; + ByteArray data{kData}; - BlePacket org_ble_packet(service_id_hash, data); - ByteArray org_ble_packet_bytes(org_ble_packet); + BlePacket org_ble_packet{service_id_hash, data}; + ByteArray org_ble_packet_bytes{org_ble_packet}; // Cut off the packet so that it's too short - ByteArray short_ble_packet_bytes(ByteArray(org_ble_packet_bytes.data(), 2)); + ByteArray short_ble_packet_bytes{ByteArray{org_ble_packet_bytes.data(), 2}}; - BlePacket short_ble_packet(short_ble_packet_bytes); + BlePacket short_ble_packet{short_ble_packet_bytes}; EXPECT_FALSE(short_ble_packet.IsValid()); } diff --git a/cpp/core_v2/internal/mediums/ble_peripheral.h b/cpp/core_v2/internal/mediums/ble_peripheral.h index 01d0b594..520b93ca 100644 --- a/cpp/core_v2/internal/mediums/ble_peripheral.h +++ b/cpp/core_v2/internal/mediums/ble_peripheral.h @@ -12,12 +12,11 @@ class BlePeripheral { public: BlePeripheral() = default; explicit BlePeripheral(const ByteArray& id) : id_(id) {} - ~BlePeripheral() = default; - BlePeripheral(const BlePeripheral&) = default; BlePeripheral& operator=(const BlePeripheral&) = default; BlePeripheral(BlePeripheral&&) = default; BlePeripheral& operator=(BlePeripheral&&) = default; + ~BlePeripheral() = default; bool IsValid() const { return !id_.Empty(); } ByteArray GetId() const { return id_; } diff --git a/cpp/core_v2/internal/mediums/ble_peripheral_test.cc b/cpp/core_v2/internal/mediums/ble_peripheral_test.cc index d43c375a..887e115e 100644 --- a/cpp/core_v2/internal/mediums/ble_peripheral_test.cc +++ b/cpp/core_v2/internal/mediums/ble_peripheral_test.cc @@ -11,9 +11,9 @@ namespace { const char kId[] = "AB12"; TEST(BlePeripheralTest, ConstructionWorks) { - ByteArray id(kId); + ByteArray id{kId}; - BlePeripheral ble_peripheral(id); + BlePeripheral ble_peripheral{id}; EXPECT_TRUE(ble_peripheral.IsValid()); EXPECT_EQ(id, ble_peripheral.GetId()); diff --git a/cpp/core_v2/internal/mediums/bloom_filter.cc b/cpp/core_v2/internal/mediums/bloom_filter.cc new file mode 100644 index 00000000..b2f08fc9 --- /dev/null +++ b/cpp/core_v2/internal/mediums/bloom_filter.cc @@ -0,0 +1,91 @@ +#include "core_v2/internal/mediums/bloom_filter.h" + +#include "absl/numeric/int128.h" +#include "absl/strings/numbers.h" +#include "smhasher/MurmurHash3.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +BloomFilterBase::BloomFilterBase(const ByteArray& bytes, BitSet* bit_set) + : bits_(bit_set) { + const char* bytes_read_ptr = bytes.data(); + for (size_t byte_index = 0; byte_index < bytes.size(); byte_index++) { + for (size_t bit_index = 0; bit_index < 8; bit_index++) { + bits_->Set((byte_index * 8) + bit_index, + (*bytes_read_ptr >> bit_index) & 0x01); + } + bytes_read_ptr++; + } +} + +BloomFilterBase::operator ByteArray() const { + // Gets a binary string representation of the bitset where the leftmost + // character corresponds to bitset position (total size) - 1. + // + // If the bitset's internal representation is: + // [position 0] 0 0 1 1 0 0 0 1 0 1 0 1 [position 11] + // The string representation will be outputted like this: + // "1 0 1 0 1 0 0 0 1 1 0 0" + std::string bitset_binary_string = bits_->ToString(); + + ByteArray result_bytes(GetMinBytesForBits()); + char* result_bytes_write_ptr = result_bytes.data(); + // We go through the string backwards because the rightmost character + // corresponds to position 0 in the bitset. + for (size_t i = bits_->Size(); i > 0; i -= 8) { + std::string byte_binary_string = bitset_binary_string.substr(i - 8, 8); + std::uint32_t byte_value; + absl::numbers_internal::safe_strtou32_base(byte_binary_string, &byte_value, + /* base= */ 2); + *result_bytes_write_ptr = static_cast(byte_value & 0x000000FF); + result_bytes_write_ptr++; + } + return result_bytes; +} + +void BloomFilterBase::Add(const std::string& s) { + std::vector hashes = GetHashes(s); + for (int32_t hash : hashes) { + size_t position = static_cast(hash) % bits_->Size(); + bits_->Set(position, true); + } +} + +bool BloomFilterBase::PossiblyContains(const std::string& s) { + std::vector hashes = GetHashes(s); + for (int32_t hash : hashes) { + size_t position = static_cast(hash) % bits_->Size(); + if (!bits_->Test(position)) { + return false; + } + } + return true; +} + +std::vector BloomFilterBase::GetHashes(const std::string& s) { + std::vector hashes(kHasherNumberOfRepetitions, 0); + + absl::uint128 hash128; + MurmurHash3_x64_128(s.data(), s.size(), 0, &hash128); + std::uint64_t hash64 = + absl::Uint128Low64(hash128); // the lower 64 bits of the 128-bit hash + std::int32_t hash1 = static_cast( + hash64 & 0x00000000FFFFFFFF); // the lower 32 bits of the 64-bit hash + std::int32_t hash2 = static_cast( + (hash64 >> 32) & 0x0FFFFFFFF); // the upper 32 bits of the 64-bit hash + for (size_t i = 1; i <= kHasherNumberOfRepetitions; i++) { + std::int32_t combinedHash = static_cast(hash1 + (i * hash2)); + // Flip all the bits if it's negative (guaranteed positive number) + if (combinedHash < 0) combinedHash = ~combinedHash; + hashes[i - 1] = combinedHash; + } + return hashes; +} + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/bloom_filter.h b/cpp/core_v2/internal/mediums/bloom_filter.h new file mode 100644 index 00000000..da65f652 --- /dev/null +++ b/cpp/core_v2/internal/mediums/bloom_filter.h @@ -0,0 +1,87 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_BLOOM_FILTER_H_ +#define CORE_V2_INTERNAL_MEDIUMS_BLOOM_FILTER_H_ + +#include +#include + +#include "platform_v2/base/byte_array.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +/** + * A bloom filter that gives access to the underlying BitSet. The implementation + * is copied from our Java version of Bloom filter, which in turn copies from + * Guava's BloomFilter. + * + * BloomFilter is templatized on the size of the byte array and not the size of + * the bit set to ensure the bit set's length is a multiple of 8 (and can + * neatly be returned as a ByteArray). + */ +class BloomFilterBase { + public: + explicit operator ByteArray() const; + + void Add(const std::string& s); + bool PossiblyContains(const std::string& s); + + protected: + class BitSet { + public: + virtual ~BitSet() = default; + virtual std::string ToString() const = 0; + virtual void Set(size_t pos, bool value) = 0; + virtual bool Test(size_t pos) const = 0; + virtual size_t Size() const = 0; + }; + + BloomFilterBase(const ByteArray& bytes, BitSet* bit_set); + virtual ~BloomFilterBase() = default; + + constexpr static int kHasherNumberOfRepetitions = 5; + std::vector GetHashes(const std::string& s); + + private: + int GetMinBytesForBits() const { return (bits_->Size() + 7) >> 3; } + + BitSet* bits_; +}; + +template +class BloomFilter final : public BloomFilterBase { + public: + BloomFilter() : BloomFilterBase(ByteArray{}, &bits_) {} + explicit BloomFilter(const ByteArray& bytes) + : BloomFilterBase(bytes, &bits_) {} + BloomFilter(const BloomFilter&) = default; + BloomFilter& operator=(const BloomFilter&) = default; + BloomFilter(BloomFilter&& other) : BloomFilterBase(ByteArray{}, &bits_) { + *this = std::move(other); + } + BloomFilter& operator=(BloomFilter&& other) { + std::swap((*this).bits_, other.bits_); + return *this; + } + ~BloomFilter() override = default; + + private: + class BitSetImpl final : public BitSet { + public: + std::string ToString() const override { return bits_.to_string(); } + void Set(size_t pos, bool value) override { bits_.set(pos, value); } + bool Test(size_t pos) const override { return bits_.test(pos); } + size_t Size() const override { return bits_.size(); } + + private: + std::bitset bits_; + } bits_; +}; + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_BLOOM_FILTER_H_ diff --git a/cpp/core_v2/internal/mediums/bloom_filter_test.cc b/cpp/core_v2/internal/mediums/bloom_filter_test.cc new file mode 100644 index 00000000..b839d499 --- /dev/null +++ b/cpp/core_v2/internal/mediums/bloom_filter_test.cc @@ -0,0 +1,193 @@ +#include "core_v2/internal/mediums/bloom_filter.h" + +#include + +#include "gtest/gtest.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { +namespace { + +const size_t kByteArrayLength = 100; + +TEST(BloomFilterTest, EmptyFilterReturnsEmptyArray) { + BloomFilter bloom_filter; + + ByteArray bloom_filter_bytes(bloom_filter); + std::string empty_string(kByteArrayLength, '\0'); + + EXPECT_EQ(empty_string, std::string(bloom_filter_bytes)); +} + +TEST(BloomFilterTest, EmptyFilterNeverContains) { + BloomFilter bloom_filter; + + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_1")); + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_2")); + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_3")); +} + +TEST(BloomFilterTest, AddSuccess) { + BloomFilter bloom_filter; + + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_1")); + + bloom_filter.Add("ELEMENT_1"); + + EXPECT_TRUE(bloom_filter.PossiblyContains("ELEMENT_1")); +} + +TEST(BloomFilterTest, AddOnlyGivenArg) { + BloomFilter bloom_filter; + + bloom_filter.Add("ELEMENT_1"); + + EXPECT_TRUE(bloom_filter.PossiblyContains("ELEMENT_1")); + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_2")); + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_3")); +} + +TEST(BloomFilterTest, AddMultipleArgs) { + BloomFilter bloom_filter; + + bloom_filter.Add("ELEMENT_1"); + bloom_filter.Add("ELEMENT_2"); + + EXPECT_TRUE(bloom_filter.PossiblyContains("ELEMENT_1")); + EXPECT_TRUE(bloom_filter.PossiblyContains("ELEMENT_2")); + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_3")); +} + +TEST(BloomFilterTest, AddMultipleArgsReturnsNonemptyArray) { + BloomFilter<10> bloom_filter; + + bloom_filter.Add("ELEMENT_1"); + bloom_filter.Add("ELEMENT_2"); + bloom_filter.Add("ELEMENT_3"); + + ByteArray bloom_filter_bytes(bloom_filter); + std::string empty_string(kByteArrayLength, '\0'); + + EXPECT_NE(std::string(bloom_filter_bytes), empty_string); +} + +TEST(BloomFilterTest, CopyConstructorAndAssignmentSuccess) { + BloomFilter bloom_filter; + + EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_1")); + + bloom_filter.Add("ELEMENT_1"); + + BloomFilter bloom_filter_copy_1{bloom_filter}; + BloomFilter bloom_filter_copy_2 = bloom_filter; + + EXPECT_TRUE(bloom_filter.PossiblyContains("ELEMENT_1")); + EXPECT_TRUE(bloom_filter_copy_1.PossiblyContains("ELEMENT_1")); + EXPECT_TRUE(bloom_filter_copy_2.PossiblyContains("ELEMENT_1")); +} + +TEST(BloomFilterTest, MoveConstructorSuccess) { + BloomFilter bloom_filter; + + bloom_filter.Add("ELEMENT_1"); + + BloomFilter bloom_filter_move{std::move(bloom_filter)}; + + EXPECT_TRUE(bloom_filter_move.PossiblyContains("ELEMENT_1")); +} + +TEST(BloomFilterTest, MoveAssignmentSuccess) { + BloomFilter bloom_filter; + + bloom_filter.Add("ELEMENT_1"); + + BloomFilter bloom_filter_move = std::move(bloom_filter); + + EXPECT_TRUE(bloom_filter_move.PossiblyContains("ELEMENT_1")); +} + +/** + * This test was added because of a bug where the BloomFilter doesn't utilize + * all bits given. Functionally, the filter still works, but we just have a much + * higher false positive rate. The bug was caused by confusing bit length and + * byte length, which made our BloomFilter only set bits on the first byteLength + * (bitLength / 8) bits rather than the whole bitLength bits. + * + *

Here, we're verifying that the bits set are somewhat scattered. So instead + * of something like [ 0, 1, 1, 0, 0, 0, 0, ..., 0 ], we should be getting + * something like [ 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, ..., 1, 0]. + */ +TEST(BloomFilterTest, RandomnessNoEndBias) { + BloomFilter bloom_filter; + + // Add one element to our BloomFilter. + bloom_filter.Add("ELEMENT_1"); + + std::int32_t non_zero_count = 0; + std::int32_t longest_zero_streak = 0; + std::int32_t current_zero_streak = 0; + + // Record the amount of non-zero bytes and the longest streak of zero bytes in + // the resulting BloomFilter. This is an approximation of reasonable + // distribution since we're recording by bytes instead of bits. + ByteArray bloom_filter_bytes(bloom_filter); + const char* bloom_filter_bytes_read_ptr = bloom_filter_bytes.data(); + for (int i = 0; i < bloom_filter_bytes.size(); i++) { + if (*bloom_filter_bytes_read_ptr == '\0') { + current_zero_streak++; + } else { + // Increment the number of non-zero bytes we've seen, update the longest + // zero streak, and then reset the current zero streak. + non_zero_count++; + longest_zero_streak = std::max(longest_zero_streak, current_zero_streak); + current_zero_streak = 0; + } + bloom_filter_bytes_read_ptr++; + } + // Update the longest zero streak again for the tail case. + longest_zero_streak = std::min(longest_zero_streak, current_zero_streak); + + // Since randomness is hard to measure within one unit test, we instead do a + // sanity check. All non-zero bytes should not be packed into one end of the + // array. + // + // In this case, the size of one end is approximated to be: + // kByteArrayLength / nonZeroCount. + // Therefore, the longest zero streak should be less than: + // kByteArrayLength - one end of the array. + std::int32_t longest_acceptable_zero_streak = + kByteArrayLength - (kByteArrayLength / non_zero_count); + + EXPECT_TRUE(longest_zero_streak <= longest_acceptable_zero_streak); +} + +TEST(BloomFilterTest, RandomnessFalsePositiveRate) { + BloomFilter<10> bloom_filter; + + // Add 5 distinct elements to the BloomFilter. + bloom_filter.Add("ELEMENT_1"); + bloom_filter.Add("ELEMENT_2"); + bloom_filter.Add("ELEMENT_3"); + bloom_filter.Add("ELEMENT_4"); + bloom_filter.Add("ELEMENT_5"); + + std::int32_t false_positives = 0; + // Now test 100 other elements and record the number of false positives. + for (int i = 5; i < 105; i++) { + false_positives += + bloom_filter.PossiblyContains("ELEMENT_" + std::to_string(i)) ? 1 : 0; + } + + // We expect the false positive rate to be 3% with 5 elements in a 10 byte + // filter. Thus, we give a little leeway and verify that the false positive + // rate is no more than 5%. + EXPECT_LE(false_positives, 5); +} + +} // namespace +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/webrtc/BUILD b/cpp/core_v2/internal/mediums/webrtc/BUILD index 9e8cc9e8..9805da7e 100644 --- a/cpp/core_v2/internal/mediums/webrtc/BUILD +++ b/cpp/core_v2/internal/mediums/webrtc/BUILD @@ -1,29 +1,41 @@ cc_library( name = "webrtc", srcs = [ + "connection_flow.cc", + "peer_connection_observer_impl.cc", "webrtc_socket.cc", ], hdrs = [ + "connection_flow.h", + "data_channel_listener.h", + "local_ice_candidate_listener.h", + "peer_connection_observer_impl.h", "webrtc_socket.h", ], deps = [ "//core_v2:core_types", "//platform_v2/base", - "//platform_v2/public", + "//platform_v2/public:comm", "//platform_v2/public:logging", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//platform_v2/public:types", + "//absl/memory", + "//webrtc/api:libjingle_peerconnection_api", ], ) cc_test( name = "webrtc_test", - srcs = ["webrtc_socket_test.cc"], + srcs = [ + "connection_flow_test.cc", + "webrtc_socket_test.cc", + ], deps = [ ":webrtc", "//platform_v2/base", "//platform_v2/impl/g3", # buildcleaner: keep + "//platform_v2/public:comm", "//testing/base/public:gunit_main", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:libjingle_peerconnection_api", ], ) @@ -34,7 +46,8 @@ cc_test( ":peer_id", "//platform_v2/base", "//platform_v2/impl/g3", #buildcleaner: keep - "//platform_v2/public", + "//platform_v2/public:comm", + "//platform_v2/public:types", "//testing/base/public:gunit_main", ], ) @@ -48,7 +61,7 @@ cc_test( "//platform_v2/impl/g3", # buildcleaner: keep "//net/proto2/public:proto2", "//testing/base/public:gunit_main", - "//webrtc/files/stable/webrtc/pc:peerconnection", # buildcleaner: keep + "//webrtc/pc:peerconnection", # buildcleaner: keep ], ) @@ -71,6 +84,6 @@ cc_library( ":peer_id", "//platform_v2/base", "//location/nearby/mediums/proto:web_rtc_signaling_frames_cc_proto", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:libjingle_peerconnection_api", ], ) diff --git a/cpp/core_v2/internal/mediums/webrtc/connection_flow.cc b/cpp/core_v2/internal/mediums/webrtc/connection_flow.cc new file mode 100644 index 00000000..6a673574 --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/connection_flow.cc @@ -0,0 +1,134 @@ +#include "core_v2/internal/mediums/webrtc/connection_flow.h" + +#include + +#include "platform_v2/public/mutex_lock.h" +#include "platform_v2/public/webrtc.h" +#include "absl/memory/memory.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +std::unique_ptr ConnectionFlow::Create( + LocalIceCandidateListener local_ice_candidate_listener, + DataChannelListener data_channel_listener, + SingleThreadExecutor* single_threaded_executor, + WebRtcMedium& webrtc_medium) { + auto connection_flow = absl::WrapUnique(new ConnectionFlow( + std::move(local_ice_candidate_listener), std::move(data_channel_listener), + single_threaded_executor)); + if (connection_flow->InitPeerConnection(webrtc_medium)) { + return connection_flow; + } + + return nullptr; +} + +ConnectionFlow::ConnectionFlow( + LocalIceCandidateListener local_ice_candidate_listener, + DataChannelListener data_channel_listener, + SingleThreadExecutor* single_threaded_executor) + : data_channel_listener_(std::move(data_channel_listener)), + peer_connection_observer_(this, std::move(local_ice_candidate_listener), + single_threaded_executor) {} + +std::unique_ptr +ConnectionFlow::CreateOffer() { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement + + return std::unique_ptr(); +} + +std::unique_ptr +ConnectionFlow::CreateAnswer() { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement + + return std::unique_ptr(); +} + +bool ConnectionFlow::SetLocalSessionDescription( + std::unique_ptr sdp) { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement + + return false; +} + +void ConnectionFlow::OnOfferReceived( + std::unique_ptr offer) { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement +} + +void ConnectionFlow::OnAnswerReceived( + std::unique_ptr answer) { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement +} + +bool ConnectionFlow::OnRemoteIceCandidatesReceived( + std::vector ice_candidates) { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement + + return false; +} + +api::ListenableFuture>* +ConnectionFlow::GetDataChannel() { + return static_cast< + api::ListenableFuture>*>( + &data_channel_future_); +} + +bool ConnectionFlow::Close() { + MutexLock lock(&mutex_); + + // TODO(bfranz): Implement + + return false; +} + +bool ConnectionFlow::InitPeerConnection(WebRtcMedium& webrtc_medium) { + Future success_future; + webrtc_medium.CreatePeerConnection( + &peer_connection_observer_, + [this, &success_future]( + rtc::scoped_refptr peer_connection) { + peer_connection_ = peer_connection; + success_future.Set(true); + }); + + ExceptionOr result = success_future.Get(kTimeout); + return result.ok() && result.result(); +} + +void ConnectionFlow::OnSignalingStable() { + // TODO(bfranz): Implement +} + +void ConnectionFlow::ProcessOnPeerConnectionChange( + webrtc::PeerConnectionInterface::PeerConnectionState new_state) { + // TODO(bfranz): Implement +} + +webrtc::DataChannelObserver* ConnectionFlow::CreateDataChannelObserver( + rtc::scoped_refptr data_channel) { + // TODO(bfranz): Implement + + return nullptr; +} +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/webrtc/connection_flow.h b/cpp/core_v2/internal/mediums/webrtc/connection_flow.h new file mode 100644 index 00000000..7f5ca6dc --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/connection_flow.h @@ -0,0 +1,133 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_ +#define CORE_V2_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_ + +#include + +#include "core_v2/internal/mediums/webrtc/data_channel_listener.h" +#include "core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h" +#include "core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h" +#include "platform_v2/base/runnable.h" +#include "platform_v2/public/future.h" +#include "platform_v2/public/single_thread_executor.h" +#include "platform_v2/public/webrtc.h" +#include "webrtc/api/data_channel_interface.h" +#include "webrtc/api/peer_connection_interface.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +/** + * Flow for an offerer: + * + *

    + *
  • INITIALIZED: After construction. + *
  • CREATING_OFFER: After CreateOffer(). Local ice candidate collection + * begins. + *
  • WAITING_FOR_ANSWER: Until the remote peer sends their answer. + *
  • WAITING_TO_CONNECT: Until the data channel actually connects. Remote + * ice candidates should be added with OnRemoteIceCandidatesReceived as they are + * gathered. + *
  • CONNECTED: We successfully connected to the remote data + * channel. + *
  • ENDED: The final state that can occur from any of the previous + * states if we disconnect at any point in the flow. + *
+ * + *

Flow for an answerer: + * + *

    + *
  • INITIALIZED: After construction. + *
  • RECEIVED_OFFER: After onOfferReceived(). + *
  • CREATING_ANSWER: After CreateAnswer(). Local ice candidate collection + * begins. + *
  • WAITING_TO_CONNECT: Until the data channel actually connects. + * Remote ice candidates should be added with OnRemoteIceCandidatesReceived as + * they are gathered. + *
  • CONNECTED: We successfully connected to the remote + * data channel. + *
  • ENDED: The final state that can occur from any of the + * previous states if we disconnect at any point in the flow. + *
+ */ +class ConnectionFlow { + public: + // This method blocks on the creation of the peer connection object. + static std::unique_ptr Create( + LocalIceCandidateListener local_ice_candidate_listener, + DataChannelListener data_channel_listener, + SingleThreadExecutor* single_threaded_executor, + WebRtcMedium& webrtc_medium); + ~ConnectionFlow() = default; + + // Create the offer that will be sent to the remote. Mirrors the behaviour of + // PeerConnectionInterface::CreateOffer. + std::unique_ptr CreateOffer() + ABSL_LOCKS_EXCLUDED(mutex_); + // Create the answer that will be sent to the remote. Mirrors the behaviour of + // PeerConnectionInterface::CreateAnswer. + std::unique_ptr CreateAnswer() + ABSL_LOCKS_EXCLUDED(mutex_); + // Set the local session description. |sdp| was created via CreateOffer() + // or CreateAnswer(). + bool SetLocalSessionDescription( + std::unique_ptr sdp) + ABSL_LOCKS_EXCLUDED(mutex_); + // Invoked when an offer was received from a remote; this will set the remote + // session description on the peer connection. + void OnOfferReceived( + std::unique_ptr offer) + ABSL_LOCKS_EXCLUDED(mutex_); + // Invoked when an answer was received from a remote; this will set the remote + // session description on the peer connection. + void OnAnswerReceived( + std::unique_ptr answer) + ABSL_LOCKS_EXCLUDED(mutex_); + // Invoked when an ice candidate was received from a remote; this will add the + // ice candidate to the peer connection if ready or cache it otherwise. + bool OnRemoteIceCandidatesReceived( + std::vector ice_candidates) + ABSL_LOCKS_EXCLUDED(mutex_); + // Get a future for the data channel. + api::ListenableFuture>* + GetDataChannel(); + // Close the peer connection and data channel. + bool Close() ABSL_LOCKS_EXCLUDED(mutex_); + + // Invoked when the peer connection indicates that signaling is stable. + void OnSignalingStable(); + webrtc::DataChannelObserver* CreateDataChannelObserver( + rtc::scoped_refptr data_channel); + + // Invoked upon changes in the state of peer connection, e.g. react to + // disconnect. + void ProcessOnPeerConnectionChange( + webrtc::PeerConnectionInterface::PeerConnectionState new_state); + + private: + ConnectionFlow(LocalIceCandidateListener local_ice_candidate_listener, + DataChannelListener data_channel_listener, + SingleThreadExecutor* single_threaded_executor); + + // TODO(bfranz): Consider whether this needs to be configurable per platform + static constexpr absl::Duration kTimeout = absl::Milliseconds(250); + + bool InitPeerConnection(WebRtcMedium& webrtc_medium); + + DataChannelListener data_channel_listener_; + + Future> data_channel_future_; + + PeerConnectionObserverImpl peer_connection_observer_; + rtc::scoped_refptr peer_connection_; + + Mutex mutex_; +}; + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_ diff --git a/cpp/core_v2/internal/mediums/webrtc/connection_flow_test.cc b/cpp/core_v2/internal/mediums/webrtc/connection_flow_test.cc new file mode 100644 index 00000000..3b0895bf --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/connection_flow_test.cc @@ -0,0 +1,32 @@ +#include "core_v2/internal/mediums/webrtc/connection_flow.h" + +#include + +#include "platform_v2/public/webrtc.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { +namespace { + +TEST(ConnectionFlowTest, Create) { + LocalIceCandidateListener local_ice_candidate_listener; + DataChannelListener data_channel_listener; + SingleThreadExecutor executor; + WebRtcMedium webrtc_medium; + + std::unique_ptr connection_flow = ConnectionFlow::Create( + std::move(local_ice_candidate_listener), std::move(data_channel_listener), + &executor, webrtc_medium); + + EXPECT_NE(connection_flow, nullptr); +} + +} // namespace +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/webrtc/data_channel_listener.h b/cpp/core_v2/internal/mediums/webrtc/data_channel_listener.h new file mode 100644 index 00000000..2c4cec68 --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/data_channel_listener.h @@ -0,0 +1,31 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_ +#define CORE_V2_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_ + +#include "core_v2/listeners.h" +#include "platform_v2/base/byte_array.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +// Callbacks from the data channel. +struct DataChannelListener { + std::function data_channel_closed_cb = DefaultCallback<>(); + + // Called when a new message was received on the data channel. + std::function data_channel_message_received_cb = + DefaultCallback(); + + // Called when the data channel indicates that the buffered amount has + // changed. + std::function data_channel_buffered_amount_changed_cb = + DefaultCallback<>(); +}; + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_WEBRTC_DATA_CHANNEL_LISTENER_H_ diff --git a/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h b/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h new file mode 100644 index 00000000..62adf483 --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h @@ -0,0 +1,25 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ +#define CORE_V2_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ + +#include "core_v2/listeners.h" +#include "webrtc/api/peer_connection_interface.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +// Callbacks from local ice candidate collection. +struct LocalIceCandidateListener { + // Called when a new local ice candidate has been found. + std::function + local_ice_candidate_found_cb = location::nearby::DefaultCallback< + const webrtc::IceCandidateInterface*>(); +}; + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_WEBRTC_LOCAL_ICE_CANDIDATE_LISTENER_H_ diff --git a/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.cc b/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.cc new file mode 100644 index 00000000..e6c5980d --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.cc @@ -0,0 +1,68 @@ +#include "core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h" + +#include "core_v2/internal/mediums/webrtc/connection_flow.h" +#include "platform_v2/public/logging.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +PeerConnectionObserverImpl::PeerConnectionObserverImpl( + ConnectionFlow* connection_flow, + LocalIceCandidateListener local_ice_candidate_listener, + SingleThreadExecutor* executor) + : connection_flow_(connection_flow), + local_ice_candidate_listener_(std::move(local_ice_candidate_listener)), + single_threaded_signaling_offloader_(executor) {} + +void PeerConnectionObserverImpl::OnIceCandidate( + const webrtc::IceCandidateInterface* candidate) { + NEARBY_LOG(INFO, "OnIceCandidate"); + local_ice_candidate_listener_.local_ice_candidate_found_cb(candidate); +} + +void PeerConnectionObserverImpl::OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) { + NEARBY_LOG(INFO, "OnSignalingChange: %d", new_state); + + OffloadFromSignalingThread([this, new_state]() { + if (new_state == webrtc::PeerConnectionInterface::SignalingState::kStable) + connection_flow_->OnSignalingStable(); + }); +} + +void PeerConnectionObserverImpl::OnDataChannel( + rtc::scoped_refptr data_channel) { + NEARBY_LOG(INFO, "OnDataChannel"); + + data_channel->RegisterObserver( + connection_flow_->CreateDataChannelObserver(data_channel)); +} + +void PeerConnectionObserverImpl::OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) { + NEARBY_LOG(INFO, "OnIceGatheringChange: %d", new_state); +} + +void PeerConnectionObserverImpl::OnConnectionChange( + webrtc::PeerConnectionInterface::PeerConnectionState new_state) { + NEARBY_LOG(INFO, "OnConnectionChange: %d", new_state); + + OffloadFromSignalingThread([this, new_state]() { + connection_flow_->ProcessOnPeerConnectionChange(new_state); + }); +} + +void PeerConnectionObserverImpl ::OnRenegotiationNeeded() { + NEARBY_LOG(INFO, "OnRenegotiationNeeded"); +} + +void PeerConnectionObserverImpl::OffloadFromSignalingThread(Runnable runnable) { + single_threaded_signaling_offloader_->Execute(std::move(runnable)); +} + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location diff --git a/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h b/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h new file mode 100644 index 00000000..7c30ef7b --- /dev/null +++ b/cpp/core_v2/internal/mediums/webrtc/peer_connection_observer_impl.h @@ -0,0 +1,48 @@ +#ifndef CORE_V2_INTERNAL_MEDIUMS_WEBRTC_PEER_CONNECTION_OBSERVER_IMPL_H_ +#define CORE_V2_INTERNAL_MEDIUMS_WEBRTC_PEER_CONNECTION_OBSERVER_IMPL_H_ + +#include "core_v2/internal/mediums/webrtc/local_ice_candidate_listener.h" +#include "platform_v2/public/single_thread_executor.h" +#include "webrtc/api/peer_connection_interface.h" + +namespace location { +namespace nearby { +namespace connections { +namespace mediums { + +class ConnectionFlow; + +class PeerConnectionObserverImpl : public webrtc::PeerConnectionObserver { + public: + ~PeerConnectionObserverImpl() override = default; + PeerConnectionObserverImpl( + ConnectionFlow* connection_flow, + LocalIceCandidateListener local_ice_candidate_listener, + SingleThreadExecutor* executor); + + // webrtc::PeerConnectionObserver: + void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override; + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) override; + void OnDataChannel( + rtc::scoped_refptr data_channel) override; + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) override; + void OnConnectionChange( + webrtc::PeerConnectionInterface::PeerConnectionState new_state) override; + void OnRenegotiationNeeded() override; + + private: + void OffloadFromSignalingThread(Runnable runnable); + + ConnectionFlow* connection_flow_; + LocalIceCandidateListener local_ice_candidate_listener_; + SingleThreadExecutor* single_threaded_signaling_offloader_; +}; + +} // namespace mediums +} // namespace connections +} // namespace nearby +} // namespace location + +#endif // CORE_V2_INTERNAL_MEDIUMS_WEBRTC_PEER_CONNECTION_OBSERVER_IMPL_H_ diff --git a/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h b/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h index 63a92718..78fe328a 100644 --- a/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h +++ b/cpp/core_v2/internal/mediums/webrtc/signaling_frames.h @@ -6,7 +6,7 @@ #include "core_v2/internal/mediums/webrtc/peer_id.h" #include "platform_v2/base/byte_array.h" #include "location/nearby/mediums/proto/web_rtc_signaling_frames.pb.h" -#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" +#include "webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h index e5d90939..c0268f65 100644 --- a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h +++ b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket.h @@ -11,7 +11,7 @@ #include "platform_v2/public/condition_variable.h" #include "platform_v2/public/mutex.h" #include "platform_v2/public/pipe.h" -#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" +#include "webrtc/api/data_channel_interface.h" namespace location { namespace nearby { namespace connections { diff --git a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc index 89184569..423b06ed 100644 --- a/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc +++ b/cpp/core_v2/internal/mediums/webrtc/webrtc_socket_test.cc @@ -5,7 +5,7 @@ #include "platform_v2/base/byte_array.h" #include "gmock/gmock.h" #include "gtest/gtest.h" -#include "webrtc/files/stable/webrtc/api/data_channel_interface.h" +#include "webrtc/api/data_channel_interface.h" namespace location { namespace nearby { diff --git a/cpp/core_v2/internal/offline_frames.cc b/cpp/core_v2/internal/offline_frames.cc index 792922bb..fc5c5572 100644 --- a/cpp/core_v2/internal/offline_frames.cc +++ b/cpp/core_v2/internal/offline_frames.cc @@ -3,7 +3,7 @@ #include #include -#include "core/internal/message_lite.h" +#include "google/protobuf/message_lite.h" #include "platform_v2/base/byte_array.h" namespace location { diff --git a/cpp/core_v2/internal/pcp_handler.h b/cpp/core_v2/internal/pcp_handler.h index 3666360d..dd753ee7 100644 --- a/cpp/core_v2/internal/pcp_handler.h +++ b/cpp/core_v2/internal/pcp_handler.h @@ -30,10 +30,10 @@ class PcpHandler { virtual ~PcpHandler() = default; // Return strategy supported by this protocol. - virtual Strategy GetStrategy() = 0; + virtual Strategy GetStrategy() const = 0; // Return concrete variant of protocol. - virtual Pcp GetPcp() = 0; + virtual Pcp GetPcp() const = 0; // We have been asked by the client to start advertising. Once we successfully // start advertising, we'll change the ClientProxy's state. diff --git a/cpp/core_v2/internal/service_controller_router.cc b/cpp/core_v2/internal/service_controller_router.cc index dd1c044b..9b4a3d25 100644 --- a/cpp/core_v2/internal/service_controller_router.cc +++ b/cpp/core_v2/internal/service_controller_router.cc @@ -190,24 +190,25 @@ void ServiceControllerRouter::SendPayload( // We have to capture it by value inside the lambda, and pass it over to // the executor as an std::function instance. // Lambda must be copyable, in order ot satisfy std::function<> requirements. - // To make it so, we need Payload wrapped by a copyable wrapper. + // To make it so, we need Payload wrapped by a copyable wrapper. // std::shared_ptr<> is used, because it is copyable. auto shared_payload = std::make_shared(std::move(payload)); + const std::vector endpoints = + std::vector(endpoint_ids.begin(), endpoint_ids.end()); + RouteToServiceController( - [this, client, shared_payload, - endpoint_ids = std::vector(endpoint_ids.begin(), endpoint_ids.end()), - &callback]() { + [this, client, shared_payload, endpoints, &callback]() { if (!ClientHasAcquiredServiceController(client)) { callback.result_cb({Status::kOutOfOrderApiCall}); return; } - if (!ClientHasConnectionToAtLeastOneEndpoint(client, endpoint_ids)) { + if (!ClientHasConnectionToAtLeastOneEndpoint(client, endpoints)) { callback.result_cb({Status::kEndpointUnknown}); return; } - service_controller_->SendPayload(client, endpoint_ids, + service_controller_->SendPayload(client, endpoints, std::move(*shared_payload)); // At this point, we've queued up the send Payload request with the diff --git a/cpp/core_v2/internal/wifi_lan_service_info.cc b/cpp/core_v2/internal/wifi_lan_service_info.cc index f034eeea..398840d9 100644 --- a/cpp/core_v2/internal/wifi_lan_service_info.cc +++ b/cpp/core_v2/internal/wifi_lan_service_info.cc @@ -33,7 +33,7 @@ WifiLanServiceInfo::WifiLanServiceInfo(Version version, Pcp pcp, version_ = version; pcp_ = pcp; service_id_hash_ = service_id_hash; - endpoint_id_ = endpoint_id; + endpoint_id_ = std::string(endpoint_id); } WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { @@ -41,14 +41,14 @@ WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { if (service_info_bytes.Empty()) { NEARBY_LOG( - ERROR, + INFO, "Cannot deserialize WifiLanServiceInfo: failed Base64 decoding of %s", std::string(service_info_string).c_str()); return; } if (service_info_bytes.size() > kMaxLanServiceNameLength) { - NEARBY_LOG(ERROR, + NEARBY_LOG(INFO, "Cannot deserialize WifiLanServiceInfo: expecting max %d raw " "bytes, got %" PRIu64, kMaxLanServiceNameLength, service_info_bytes.size()); @@ -56,7 +56,7 @@ WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { } if (service_info_bytes.size() < kMinLanServiceNameLength) { - NEARBY_LOG(ERROR, + NEARBY_LOG(INFO, "Cannot deserialize WifiLanServiceInfo: expecting min %d raw " "bytes, got %" PRIu64, kMinLanServiceNameLength, service_info_bytes.size()); @@ -96,7 +96,7 @@ WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { // the air, or older versions of GmsCore intermingling with newer // ones. NEARBY_LOG( - ERROR, + INFO, "Cannot deserialize WifiLanServiceInfo: unsupported V1 PCP %d", pcp_); break; @@ -107,8 +107,7 @@ WifiLanServiceInfo::WifiLanServiceInfo(absl::string_view service_info_string) { // TODO(edwinwu): [ANALYTICIZE] This either represents corruption over // the air, or older versions of GmsCore intermingling with newer ones. NEARBY_LOG( - ERROR, - "Cannot deserialize WifiLanServiceInfo: unsupported Version %d", + INFO, "Cannot deserialize WifiLanServiceInfo: unsupported Version %d", version_); break; } @@ -119,9 +118,7 @@ WifiLanServiceInfo::operator std::string() const { return ""; } - ByteArray wifi_lan_service_info_name_bytes(kMinLanServiceNameLength); - auto* wifi_lan_service_info_name_bytes_write_ptr = - wifi_lan_service_info_name_bytes.data(); + std::string out; // The upper 3 bits are the Version. auto version_and_pcp_byte = static_cast( @@ -129,50 +126,15 @@ WifiLanServiceInfo::operator std::string() const { // The lower 5 bits are the PCP. version_and_pcp_byte |= static_cast(static_cast(pcp_) & kPcpBitmask); - *wifi_lan_service_info_name_bytes_write_ptr = version_and_pcp_byte; - wifi_lan_service_info_name_bytes_write_ptr++; - switch (pcp_) { - case Pcp::kP2pCluster: // Fall through - case Pcp::kP2pStar: // Fall through - case Pcp::kP2pPointToPoint: - // The next 32 bits are the endpoint_id. - if (endpoint_id_.size() != kEndpointIdLength) { - NEARBY_LOG( - ERROR, - "Cannot serialize WifiLanServiceInfo: V1 Endpoint ID %s (%" PRIu64 - " bytes) should be exactly %d bytes", - endpoint_id_.c_str(), endpoint_id_.size(), kEndpointIdLength); - return ""; - } - memcpy(wifi_lan_service_info_name_bytes_write_ptr, endpoint_id_.data(), - kEndpointIdLength); - wifi_lan_service_info_name_bytes_write_ptr += kEndpointIdLength; + out.reserve(kMinLanServiceNameLength); + out.append(1, version_and_pcp_byte); + out.append(endpoint_id_); + out.append(std::string(service_id_hash_)); + // The last byte is reserved to fit the kMinLanServiceNameLength. + out.append(" "); - // The next 24 bits are the service_id_hash. - if (service_id_hash_.size() != kServiceIdHashLength) { - NEARBY_LOG( - ERROR, - "Cannot serialize WifiLanServiceInfo: V1 ServiceID hash (%" PRIu64 - " bytes) should be exactly %d bytes", - service_id_hash_.size(), kServiceIdHashLength); - return ""; - } - memcpy(wifi_lan_service_info_name_bytes_write_ptr, - service_id_hash_.data(), kServiceIdHashLength); - wifi_lan_service_info_name_bytes_write_ptr += kServiceIdHashLength; - - // The next bits are the endpoint_name. - // TODO(edwinwu): Implements to parse endpoint_name. - break; - default: - NEARBY_LOG(ERROR, - "Cannot serialize WifiLanServiceInfo: unsupported V1 PCP %d", - pcp_); - return ""; - } - - return Base64Utils::Encode(wifi_lan_service_info_name_bytes); + return Base64Utils::Encode(ByteArray{std::move(out)}); } } // namespace connections diff --git a/cpp/core_v2/internal/wifi_lan_service_info.h b/cpp/core_v2/internal/wifi_lan_service_info.h index 21f1f1bb..b841e7bd 100644 --- a/cpp/core_v2/internal/wifi_lan_service_info.h +++ b/cpp/core_v2/internal/wifi_lan_service_info.h @@ -30,21 +30,20 @@ class WifiLanServiceInfo { const ByteArray& service_id_hash, absl::string_view endpoint_name); explicit WifiLanServiceInfo(absl::string_view service_info_string); - ~WifiLanServiceInfo() = default; - WifiLanServiceInfo(const WifiLanServiceInfo&) = default; WifiLanServiceInfo& operator=(const WifiLanServiceInfo&) = default; WifiLanServiceInfo(WifiLanServiceInfo&&) = default; WifiLanServiceInfo& operator=(WifiLanServiceInfo&&) = default; + ~WifiLanServiceInfo() = default; explicit operator std::string() const; - inline bool IsValid() const { return !endpoint_id_.empty(); } - inline Version GetVersion() const { return version_; } - inline Pcp GetPcp() const { return pcp_; } - inline std::string GetEndpointId() const { return endpoint_id_; } - inline std::string GetEndpointName() const { return endpoint_name_; } - inline ByteArray GetServiceIdHash() const { return service_id_hash_; } + bool IsValid() const { return !endpoint_id_.empty(); } + 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 GetServiceIdHash() const { return service_id_hash_; } private: // The maximum length of encrypted WifiLanServiceInfo string. 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 b5aee9aa..5589089f 100644 --- a/cpp/core_v2/internal/wifi_lan_service_info_test.cc +++ b/cpp/core_v2/internal/wifi_lan_service_info_test.cc @@ -14,18 +14,16 @@ namespace { const WifiLanServiceInfo::Version kVersion = WifiLanServiceInfo::Version::kV1; const Pcp kPcp = Pcp::kP2pCluster; const char kEndPointID[] = "AB12"; -const char kServiceIDHashBytes[] = {0x0A, 0x0B, 0x0C}; +const char kServiceIDHashBytes[] = "\x0a\x0b\x0c"; // TODO(edwinwu): Temp to set empty string for endpoint_name. const char kEndPointName[] = ""; TEST(WifiLanServiceInfoTest, ConstructionWorks) { - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - kVersion, kPcp, kEndPointID, service_id_hash, kEndPointName); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, kEndPointID, + service_id_hash, kEndPointName}; - EXPECT_TRUE(is_valid); + 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()); @@ -33,16 +31,14 @@ TEST(WifiLanServiceInfoTest, ConstructionWorks) { } TEST(WifiLanServiceInfoTest, ConstructionFromSerializedStringWorks) { - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto org_wifi_lan_service_info = WifiLanServiceInfo( - kVersion, kPcp, kEndPointID, service_id_hash, kEndPointName); - auto wifi_lan_service_info_string = std::string(org_wifi_lan_service_info); + ByteArray service_id_hash{kServiceIDHashBytes}; + WifiLanServiceInfo org_wifi_lan_service_info{kVersion, kPcp, kEndPointID, + service_id_hash, kEndPointName}; + std::string wifi_lan_service_info_string{org_wifi_lan_service_info}; - auto wifi_lan_service_info = WifiLanServiceInfo(wifi_lan_service_info_string); - auto is_valid = wifi_lan_service_info.IsValid(); + WifiLanServiceInfo wifi_lan_service_info{wifi_lan_service_info_string}; - EXPECT_TRUE(is_valid); + 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()); @@ -52,89 +48,71 @@ TEST(WifiLanServiceInfoTest, ConstructionFromSerializedStringWorks) { TEST(WifiLanServiceInfoTest, ConstructionFailsWithBadVersion) { auto bad_version = static_cast(666); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - bad_version, kPcp, kEndPointID, service_id_hash, kEndPointName); + ByteArray service_id_hash{kServiceIDHashBytes}; + WifiLanServiceInfo wifi_lan_service_info{bad_version, kPcp, kEndPointID, + service_id_hash, kEndPointName}; - auto is_valid = wifi_lan_service_info.IsValid(); - - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithBadPCP) { auto bad_pcp = static_cast(666); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - kVersion, bad_pcp, kEndPointID, service_id_hash, kEndPointName); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + WifiLanServiceInfo wifi_lan_service_info{kVersion, bad_pcp, kEndPointID, + service_id_hash, kEndPointName}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithShortEndpointId) { std::string short_endpoint_id("AB1"); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - kVersion, kPcp, short_endpoint_id, service_id_hash, kEndPointName); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, short_endpoint_id, + service_id_hash, kEndPointName}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithLongEndpointId) { std::string long_endpoint_id("AB12X"); - auto service_id_hash = ByteArray(kServiceIDHashBytes, - sizeof(kServiceIDHashBytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - kVersion, kPcp, long_endpoint_id, service_id_hash, kEndPointName); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray service_id_hash{kServiceIDHashBytes}; + WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, long_endpoint_id, + service_id_hash, kEndPointName}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithShortServiceIdHash) { - char short_service_id_hash_bytes[] = {0x0A, 0x0B}; + char short_service_id_hash_bytes[] = "\x0a\x0b"; - auto short_service_id_hash = - ByteArray(short_service_id_hash_bytes, - sizeof(short_service_id_hash_bytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - kVersion, kPcp, kEndPointID, short_service_id_hash, kEndPointName); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray short_service_id_hash{short_service_id_hash_bytes}; + WifiLanServiceInfo wifi_lan_service_info{ + kVersion, kPcp, kEndPointID, short_service_id_hash, kEndPointName}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithLongServiceIdHash) { - char long_service_id_hash_bytes[] = {0x0A, 0x0B, 0x0C, 0x0D}; + char long_service_id_hash_bytes[] = "\x0a\x0b\x0c\x0d"; - auto long_service_id_hash = - ByteArray(long_service_id_hash_bytes, - sizeof(long_service_id_hash_bytes) / sizeof(char)); - auto wifi_lan_service_info = WifiLanServiceInfo( - kVersion, kPcp, kEndPointID, long_service_id_hash, kEndPointName); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray long_service_id_hash{long_service_id_hash_bytes}; + WifiLanServiceInfo wifi_lan_service_info{kVersion, kPcp, kEndPointID, + long_service_id_hash, kEndPointName}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } TEST(WifiLanServiceInfoTest, ConstructionFailsWithShortStringLength) { - char wifi_lan_service_info_string[] = {'X'}; + char wifi_lan_service_info_string[] = {'X', '\0'}; - auto wifi_lan_service_info_bytes = - ByteArray(wifi_lan_service_info_string, - sizeof(wifi_lan_service_info_string) / sizeof(char)); - auto wifi_lan_service_info = - WifiLanServiceInfo(Base64Utils::Encode(wifi_lan_service_info_bytes)); - auto is_valid = wifi_lan_service_info.IsValid(); + ByteArray wifi_lan_service_info_bytes{wifi_lan_service_info_string}; + WifiLanServiceInfo wifi_lan_service_info{ + Base64Utils::Encode(wifi_lan_service_info_bytes)}; - EXPECT_FALSE(is_valid); + EXPECT_FALSE(wifi_lan_service_info.IsValid()); } } // namespace diff --git a/cpp/core_v2/payload_test.cc b/cpp/core_v2/payload_test.cc index 498efb7b..a839320f 100644 --- a/cpp/core_v2/payload_test.cc +++ b/cpp/core_v2/payload_test.cc @@ -28,7 +28,7 @@ TEST(PayloadTest, SupportsByteArrayType) { } TEST(PayloadTest, SupportsFileType) { - InputFile* raw_file = new InputFile("/path/to/file", 0); + InputFile* raw_file = new InputFile(/*payload_id=*/23, 0); std::unique_ptr file(raw_file); Payload payload(std::move(file)); EXPECT_EQ(payload.GetType(), Payload::Type::kFile); @@ -38,7 +38,7 @@ TEST(PayloadTest, SupportsFileType) { } TEST(PayloadTest, SupportsStreamType) { - InputFile* raw_file = new InputFile("/path/to/file", 0); + InputFile* raw_file = new InputFile(/*payload_id=*/17, 0); std::unique_ptr stream(raw_file); Payload payload(std::move(stream)); EXPECT_EQ(payload.GetType(), Payload::Type::kStream); diff --git a/cpp/platform/BUILD b/cpp/platform/BUILD index a0d279b6..2e9bcb30 100644 --- a/cpp/platform/BUILD +++ b/cpp/platform/BUILD @@ -3,7 +3,6 @@ cc_library( srcs = [ "base64_utils.cc", "cancelable_alarm.cc", - "file_impl.cc", "pipe.cc", "prng.cc", "reliability_utils.cc", @@ -11,7 +10,6 @@ cc_library( hdrs = [ "base64_utils.h", "cancelable_alarm.h", - "file_impl.h", "pipe.h", "prng.h", "reliability_utils.h", @@ -63,7 +61,7 @@ cc_library( visibility = [ "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", "//core:__subpackages__", - "//platform_v2/public:__pkg__", + "//platform_v2/base:__pkg__", ], deps = [ "//absl/base", @@ -78,7 +76,6 @@ cc_test( "atomic_reference_test.cc", "byte_array_test.cc", "container_of_test.cc", - "file_impl_test.cc", "pipe_test.cc", "prng_test.cc", "ptr_test.cc", @@ -86,7 +83,6 @@ cc_test( ], deps = [ ":utils", - "//file/util:temp_path", "//platform:types", "//platform/api", "//platform/impl/g3", diff --git a/cpp/platform/api/BUILD b/cpp/platform/api/BUILD index 1b155f0c..62c38942 100644 --- a/cpp/platform/api/BUILD +++ b/cpp/platform/api/BUILD @@ -47,7 +47,7 @@ cc_library( "//platform/port:string", "//absl/strings", "//absl/types:any", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", + "//webrtc/api:libjingle_peerconnection_api", ], ) diff --git a/cpp/platform/api/platform.h b/cpp/platform/api/platform.h index 70260c7f..080b5ce0 100644 --- a/cpp/platform/api/platform.h +++ b/cpp/platform/api/platform.h @@ -12,7 +12,9 @@ #include "platform/api/condition_variable.h" #include "platform/api/count_down_latch.h" #include "platform/api/hash_utils.h" +#include "platform/api/input_file.h" #include "platform/api/lock.h" +#include "platform/api/output_file.h" #include "platform/api/scheduled_executor.h" #include "platform/api/server_sync.h" #include "platform/api/settable_future_def.h" @@ -71,6 +73,9 @@ class ImplementationPlatform { static Ptr createHashUtils(); static Ptr createThreadUtils(); static Ptr createSystemClock(); + static Ptr createInputFile(std::int64_t payload_id, + std::int64_t total_size); + static Ptr createOutputFile(std::int64_t payload_id); // Java-like Executors // Type aliases used to API 1.0 compatibility. @@ -96,7 +101,6 @@ class ImplementationPlatform { static Ptr createWebRtcSignalingMessenger( const std::string& self_id); static std::string getDeviceId(); - static std::string getPayloadPath(int64_t payload_id); }; } // namespace platform diff --git a/cpp/platform/api/webrtc.h b/cpp/platform/api/webrtc.h index c428c0cb..39e09515 100644 --- a/cpp/platform/api/webrtc.h +++ b/cpp/platform/api/webrtc.h @@ -5,7 +5,7 @@ #include "platform/byte_array.h" #include "platform/ptr.h" -#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" +#include "webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { diff --git a/cpp/platform/impl/g3/BUILD b/cpp/platform/impl/g3/BUILD index e043b58e..c231beae 100644 --- a/cpp/platform/impl/g3/BUILD +++ b/cpp/platform/impl/g3/BUILD @@ -15,6 +15,7 @@ cc_library( "//platform:types", "//platform/api", "//platform/impl/shared:atomic_boolean", + "//platform/impl/shared:file", "//platform/impl/shared:posix_condition_variable", "//platform/impl/shared:posix_lock", "//platform/port:string", diff --git a/cpp/platform/impl/g3/platform.cc b/cpp/platform/impl/g3/platform.cc index b261cbe0..abd700f6 100644 --- a/cpp/platform/impl/g3/platform.cc +++ b/cpp/platform/impl/g3/platform.cc @@ -25,6 +25,7 @@ #include "platform/impl/g3/settable_future_impl.h" #include "platform/impl/g3/system_clock_impl.h" #include "platform/impl/shared/atomic_boolean_impl.h" +#include "platform/impl/shared/file_impl.h" #include "platform/impl/shared/posix_condition_variable.h" #include "platform/impl/shared/posix_lock.h" #include "platform/port/string.h" @@ -37,6 +38,12 @@ namespace location { namespace nearby { namespace platform { +namespace { +std::string getPayloadPath(std::int64_t payload_id) { + return "/tmp/" + std::to_string(payload_id); +} +} // namespace + Ptr ImplementationPlatform::createSingleThreadExecutor() { return Ptr(/*new SingleThreadExecutorImpl()*/); } @@ -87,6 +94,16 @@ Ptr ImplementationPlatform::createAtomicBoolean( return Ptr(new AtomicBooleanImpl(initial_value)); } +Ptr ImplementationPlatform::createInputFile( + std::int64_t payload_id, std::int64_t total_size) { + return MakePtr(new InputFileImpl(getPayloadPath(payload_id), total_size)); +} + +Ptr ImplementationPlatform::createOutputFile( + std::int64_t payload_id) { + return MakePtr(new OutputFileImpl(getPayloadPath(payload_id))); +} + Ptr ImplementationPlatform::createBluetoothClassicMedium() { return Ptr(); @@ -128,10 +145,6 @@ std::string ImplementationPlatform::getDeviceId() { return "google3"; } -std::string ImplementationPlatform::getPayloadPath(int64_t payload_id) { - return "/tmp/" + std::to_string(payload_id); -} - } // namespace platform } // namespace nearby } // namespace location diff --git a/cpp/platform/impl/sample/BUILD b/cpp/platform/impl/sample/BUILD index 1ace2e42..dc3922f7 100644 --- a/cpp/platform/impl/sample/BUILD +++ b/cpp/platform/impl/sample/BUILD @@ -14,6 +14,7 @@ cc_library( "//platform:types", "//platform:utils", "//platform/api", + "//platform/impl/shared:file", "//platform/impl/shared/sample:sample_wifi_medium", "//platform/port:string", "//absl/time", diff --git a/cpp/platform/impl/sample/sample_platform.cc b/cpp/platform/impl/sample/sample_platform.cc index dc660c46..4ea9a98d 100644 --- a/cpp/platform/impl/sample/sample_platform.cc +++ b/cpp/platform/impl/sample/sample_platform.cc @@ -20,6 +20,7 @@ #include "platform/cancelable.h" #include "platform/impl/sample/atomic_reference_impl.h" #include "platform/impl/sample/settable_future_impl.h" +#include "platform/impl/shared/file_impl.h" #include "platform/impl/shared/sample/sample_wifi_medium.h" #include "platform/port/string.h" #include "platform/ptr.h" @@ -30,6 +31,12 @@ namespace location { namespace nearby { namespace platform { +namespace { +std::string getPayloadPath(std::int64_t payload_id) { + return "/tmp/sample-" + std::to_string(payload_id); +} +} // namespace + Ptr ImplementationPlatform::createScheduledExecutor() { return Ptr{}; } @@ -80,6 +87,16 @@ Ptr ImplementationPlatform::createAtomicBoolean( return Ptr{}; } +Ptr ImplementationPlatform::createInputFile( + std::int64_t payload_id, std::int64_t total_size) { + return MakePtr(new InputFileImpl(getPayloadPath(payload_id), total_size)); +} + +Ptr ImplementationPlatform::createOutputFile( + std::int64_t payload_id) { + return MakePtr(new OutputFileImpl(getPayloadPath(payload_id))); +} + Ptr ImplementationPlatform::createBluetoothClassicMedium() { return Ptr(); @@ -116,10 +133,6 @@ Ptr ImplementationPlatform::createHashUtils() { std::string ImplementationPlatform::getDeviceId() { return "sample"; } -std::string ImplementationPlatform::getPayloadPath(int64_t payload_id) { - return "/tmp/sample-" + std::to_string(payload_id); -} - } // namespace platform } // namespace nearby } // namespace location diff --git a/cpp/platform/impl/shared/BUILD b/cpp/platform/impl/shared/BUILD index fb1850b2..c65c458d 100644 --- a/cpp/platform/impl/shared/BUILD +++ b/cpp/platform/impl/shared/BUILD @@ -43,3 +43,31 @@ cc_library( ], deps = ["//platform/api"], ) + +cc_library( + name = "file", + srcs = ["file_impl.cc"], + hdrs = ["file_impl.h"], + visibility = [ + "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", + "//core:__subpackages__", + "//platform/impl:__subpackages__", + ], + deps = [ + "//platform:types", + "//platform/api", + ], +) + +cc_test( + name = "file_test", + timeout = "short", + srcs = [ + "file_impl_test.cc", + ], + deps = [ + ":file", + "//file/util:temp_path", + "//testing/base/public:gunit_main", + ], +) diff --git a/cpp/platform/file_impl.cc b/cpp/platform/impl/shared/file_impl.cc similarity index 97% rename from cpp/platform/file_impl.cc rename to cpp/platform/impl/shared/file_impl.cc index 67bab338..2a21e8ea 100644 --- a/cpp/platform/file_impl.cc +++ b/cpp/platform/impl/shared/file_impl.cc @@ -1,4 +1,4 @@ -#include "platform/file_impl.h" +#include "platform/impl/shared/file_impl.h" #include #include diff --git a/cpp/platform/file_impl.h b/cpp/platform/impl/shared/file_impl.h similarity index 88% rename from cpp/platform/file_impl.h rename to cpp/platform/impl/shared/file_impl.h index 702cf7d0..5c2f33f7 100644 --- a/cpp/platform/file_impl.h +++ b/cpp/platform/impl/shared/file_impl.h @@ -1,5 +1,5 @@ -#ifndef PLATFORM_FILE_IMPL_H_ -#define PLATFORM_FILE_IMPL_H_ +#ifndef PLATFORM_IMPL_SHARED_FILE_IMPL_H_ +#define PLATFORM_IMPL_SHARED_FILE_IMPL_H_ #include #include @@ -43,4 +43,4 @@ class OutputFileImpl final : public OutputFile { } // namespace nearby } // namespace location -#endif // PLATFORM_FILE_IMPL_H_ +#endif // PLATFORM_IMPL_SHARED_FILE_IMPL_H_ diff --git a/cpp/platform/file_impl_test.cc b/cpp/platform/impl/shared/file_impl_test.cc similarity index 98% rename from cpp/platform/file_impl_test.cc rename to cpp/platform/impl/shared/file_impl_test.cc index d4a5b339..b2ce22e1 100644 --- a/cpp/platform/file_impl_test.cc +++ b/cpp/platform/impl/shared/file_impl_test.cc @@ -1,4 +1,4 @@ -#include "platform/file_impl.h" +#include "platform/impl/shared/file_impl.h" #include #include diff --git a/cpp/platform_v2/api/BUILD b/cpp/platform_v2/api/BUILD index c9b0e5a4..a0d9013f 100644 --- a/cpp/platform_v2/api/BUILD +++ b/cpp/platform_v2/api/BUILD @@ -1,12 +1,8 @@ cc_library( - name = "api", + name = "types", hdrs = [ "atomic_boolean.h", "atomic_reference.h", - "ble.h", - "ble_v2.h", - "bluetooth_adapter.h", - "bluetooth_classic.h", "cancelable.h", "condition_variable.h", "count_down_latch.h", @@ -17,12 +13,32 @@ cc_library( "listenable_future.h", "mutex.h", "output_file.h", - "platform.h", "scheduled_executor.h", - "server_sync.h", "settable_future.h", "submittable_executor.h", "system_clock.h", + ], + visibility = [ + "//platform_v2/base:__pkg__", + "//platform_v2/impl:__subpackages__", + "//platform_v2/public:__pkg__", + ], + deps = [ + "//platform_v2/base", + "//absl/base:core_headers", + "//absl/strings", + "//absl/time", + ], +) + +cc_library( + name = "comm", + hdrs = [ + "ble.h", + "ble_v2.h", + "bluetooth_adapter.h", + "bluetooth_classic.h", + "server_sync.h", "webrtc.h", "wifi.h", "wifi_lan.h", @@ -30,14 +46,29 @@ cc_library( visibility = [ "//platform_v2/base:__pkg__", "//platform_v2/impl:__subpackages__", - "//platform_v2/public:__subpackages__", + "//platform_v2/public:__pkg__", ], deps = [ "//platform_v2/base", - "//absl/base:core_headers", "//absl/strings", - "//absl/time", + "//absl/types:optional", + "//webrtc/api:libjingle_peerconnection_api", + ], +) + +cc_library( + name = "platform", + hdrs = [ + "platform.h", + ], + visibility = [ + "//platform_v2/impl:__subpackages__", + "//platform_v2/public:__pkg__", + ], + deps = [ + ":comm", + ":types", + "//absl/strings", "//absl/types:any", - "//webrtc/files/stable/webrtc/api:libjingle_peerconnection_api", ], ) diff --git a/cpp/platform_v2/api/ble_v2.h b/cpp/platform_v2/api/ble_v2.h index 8858037e..ae094d1e 100644 --- a/cpp/platform_v2/api/ble_v2.h +++ b/cpp/platform_v2/api/ble_v2.h @@ -5,13 +5,13 @@ #include #include #include -#include #include #include #include "platform_v2/base/byte_array.h" #include "platform_v2/base/exception.h" #include "absl/strings/string_view.h" +#include "absl/types/optional.h" namespace location { namespace nearby { @@ -119,7 +119,7 @@ class ClientGattConnection { // // It is okay for duplicate services to exist, as long as the specified // characteristic UUID is unique among all services of the same UUID. - virtual std::optional GetCharacteristic( + virtual absl::optional GetCharacteristic( absl::string_view service_uuid, absl::string_view characteristic_uuid) = 0; @@ -127,7 +127,7 @@ class ClientGattConnection { // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#getValue() // // Reads a GATT characteristic. No value is returned upon error. - virtual std::optional ReadCharacteristic( + virtual absl::optional ReadCharacteristic( const GattCharacteristic& characteristic) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothGattCharacteristic.html#setValue(byte[]) @@ -209,7 +209,7 @@ class GattServer { // descriptor and subscribe for characteristic changes. For more information // about this descriptor, please go to: // https://www.bluetooth.com/specifications/Gatt/viewer?attributeXmlFile=org.bluetooth.descriptor.Gatt.client_characteristic_configuration.xml - virtual std::optional CreateCharacteristic( + virtual absl::optional CreateCharacteristic( absl::string_view service_uuid, absl::string_view characteristic_uuid, const std::set& permissions, const std::set& properties) = 0; diff --git a/cpp/platform_v2/api/bluetooth_classic.h b/cpp/platform_v2/api/bluetooth_classic.h index 8919dc8b..fa3a6061 100644 --- a/cpp/platform_v2/api/bluetooth_classic.h +++ b/cpp/platform_v2/api/bluetooth_classic.h @@ -7,8 +7,8 @@ #include "platform_v2/base/byte_array.h" #include "platform_v2/base/exception.h" #include "platform_v2/base/input_stream.h" +#include "platform_v2/base/listeners.h" #include "platform_v2/base/output_stream.h" -#include "absl/strings/string_view.h" namespace location { namespace nearby { @@ -17,7 +17,7 @@ namespace api { // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html. class BluetoothDevice { public: - virtual ~BluetoothDevice() {} + virtual ~BluetoothDevice() = default; // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() virtual std::string GetName() const = 0; @@ -26,32 +26,45 @@ class BluetoothDevice { // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html. class BluetoothSocket { public: - virtual ~BluetoothSocket() {} + virtual ~BluetoothSocket() = default; - // Returns the InputStream of the BluetoothSocket. + // NOTE: + // It is an undefined behavior if GetInputStream() or GetOutputStream() is + // called for a not-connected BluetoothSocket, i.e. any object that is not + // returned by BluetoothClassicMedium::ConnectToService() for client side or + // BluetoothServerSocket::Accept() for server side of connection. + + // Returns the InputStream of this connected BluetoothSocket. virtual InputStream& GetInputStream() = 0; - // Returns the OutputStream of the BluetoothSocket. + // Returns the OutputStream of this connected BluetoothSocket. virtual OutputStream& GetOutputStream() = 0; - // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#close() - // + // Closes both input and output streams, marks Socket as closed. + // After this call object should be treated as not connected. // Returns Exception::kIo on error, Exception::kSuccess otherwise. virtual Exception Close() = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothSocket.html#getRemoteDevice() - virtual BluetoothDevice& GetRemoteDevice() = 0; + // Returns valid BluetoothDevice pointer if there is a connection, and + // nullptr otherwise. + virtual BluetoothDevice* GetRemoteDevice() = 0; }; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html. class BluetoothServerSocket { public: - virtual ~BluetoothServerSocket() {} + virtual ~BluetoothServerSocket() = default; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#accept() // - // returns Exception::kIo on error. - virtual ExceptionOr> Accept() = 0; + // 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. + virtual std::unique_ptr Accept() = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothServerSocket.html#close() // @@ -63,31 +76,33 @@ class BluetoothServerSocket { // medium. class BluetoothClassicMedium { public: - virtual ~BluetoothClassicMedium() {} + virtual ~BluetoothClassicMedium() = default; - class DiscoveryCallback { - public: - virtual ~DiscoveryCallback() {} - - // BluetoothDevice* is not owned by callbacks. - // Pointer is guaranteed to remain valid for the duration of a call. - virtual void OnDeviceDiscovered(BluetoothDevice* device) = 0; - virtual void OnDeviceNameChanged(BluetoothDevice* device) = 0; - virtual void OnDeviceLost(BluetoothDevice* device) = 0; + struct DiscoveryCallback { + // BluetoothDevice is a proxy object created as a result of BT discovery. + // Its lifetime spans between calls to device_discovered_cb and + // device_lost_cb. + // It is safe to use BluetoothDevice in device_discovered_cb() callback + // and at any time afterwards, until device_lost_cb() is called. + // It is not safe to use BluetoothDevice after returning from + // device_lost_cb() callback. + std::function device_discovered_cb = + DefaultCallback(); + std::function device_name_changed_cb = + DefaultCallback(); + std::function device_lost_cb = + DefaultCallback(); }; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#startDiscovery() // // Returns true once the process of discovery has been initiated. - // - // Does not take ownership of the passed-in discovery_callback -- destroying - // that is up to the caller. - virtual bool StartDiscovery(const DiscoveryCallback& discovery_callback) = 0; + virtual bool StartDiscovery(DiscoveryCallback discovery_callback) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#cancelDiscovery() // // Returns true once discovery is well and truly stopped; after this returns, // there must be no more invocations of the DiscoveryCallback passed in to - // startDiscovery(). + // StartDiscovery(). virtual bool StopDiscovery() = 0; // A combination of @@ -101,10 +116,10 @@ class BluetoothClassicMedium { // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) // UUID. // - // On success, returns a new BluetoothSocket, wrapped in a ExceptionOr object. - // On error, returns Exception object. - virtual ExceptionOr> ConnectToService( - BluetoothDevice* remote_device, absl::string_view service_uuid) = 0; + // On success, returns a new BluetoothSocket. + // On error, returns nullptr. + virtual std::unique_ptr ConnectToService( + BluetoothDevice& remote_device, const std::string& service_uuid) = 0; // https://developer.android.com/reference/android/bluetooth/BluetoothAdapter.html#listenUsingInsecureRfcommWithServiceRecord // @@ -114,9 +129,9 @@ class BluetoothClassicMedium { // (https://en.wikipedia.org/wiki/Universally_unique_identifier#Versions_3_and_5_(namespace_name-based)) // UUID. // - // Returns Exception::kIo on error. - virtual ExceptionOr> ListenForService( - absl::string_view service_name, absl::string_view service_uuid) = 0; + // Returns nullptr error. + virtual std::unique_ptr ListenForService( + const std::string& service_name, const std::string& service_uuid) = 0; }; } // namespace api diff --git a/cpp/platform_v2/api/platform.h b/cpp/platform_v2/api/platform.h index ef217692..f710897f 100644 --- a/cpp/platform_v2/api/platform.h +++ b/cpp/platform_v2/api/platform.h @@ -14,7 +14,9 @@ #include "platform_v2/api/condition_variable.h" #include "platform_v2/api/count_down_latch.h" #include "platform_v2/api/crypto.h" +#include "platform_v2/api/input_file.h" #include "platform_v2/api/mutex.h" +#include "platform_v2/api/output_file.h" #include "platform_v2/api/scheduled_executor.h" #include "platform_v2/api/server_sync.h" #include "platform_v2/api/settable_future.h" @@ -41,6 +43,7 @@ class ImplementationPlatform { // - condition variable (must work with regular mutex only) // - Future : to synchronize on Callable schduled to execute. // - CountDownLatch : to ensure at least N threads are waiting. + // - file I/O static std::unique_ptr> CreateAtomicReferenceAny( absl::any initial_value); static std::unique_ptr> CreateSettableFutureAny(); @@ -50,6 +53,9 @@ class ImplementationPlatform { static std::unique_ptr CreateMutex(Mutex::Mode mode); static std::unique_ptr CreateConditionVariable( Mutex* mutex); + static std::unique_ptr CreateInputFile(std::int64_t payload_id, + std::int64_t total_size); + static std::unique_ptr CreateOutputFile(std::int64_t payload_id); // Java-like Executors static std::unique_ptr CreateSingleThreadExecutor(); @@ -65,10 +71,8 @@ class ImplementationPlatform { static std::unique_ptr CreateServerSyncMedium(); static std::unique_ptr CreateWifiMedium(); static std::unique_ptr CreateWifiLanMedium(); - static std::unique_ptr - CreateWebRtcSignalingMessenger(absl::string_view self_id); + static std::unique_ptr CreateWebRtcMedium(); static std::string GetDeviceId(); - static std::string GetPayloadPath(std::int64_t payload_id); }; } // namespace api diff --git a/cpp/platform_v2/api/webrtc.h b/cpp/platform_v2/api/webrtc.h index ee507e9d..d07bc699 100644 --- a/cpp/platform_v2/api/webrtc.h +++ b/cpp/platform_v2/api/webrtc.h @@ -1,10 +1,11 @@ #ifndef PLATFORM_V2_API_WEBRTC_H_ #define PLATFORM_V2_API_WEBRTC_H_ -#include +#include #include "platform_v2/base/byte_array.h" -#include "webrtc/files/stable/webrtc/api/peer_connection_interface.h" +#include "absl/strings/string_view.h" +#include "webrtc/api/peer_connection_interface.h" namespace location { namespace nearby { @@ -12,33 +13,32 @@ namespace api { class WebRtcSignalingMessenger { public: + using OnSignalingMessageCallback = std::function; + virtual ~WebRtcSignalingMessenger() = default; - /** Called whenever we receive an inbox message from tachyon. */ - class SignalingMessageListener { - public: - virtual ~SignalingMessageListener() = default; - - virtual void OnSignalingMessage(const ByteArray& message) = 0; - }; - - class IceServersListener { - public: - virtual ~IceServersListener() = default; - - virtual void OnIceServersFetched( - std::vector - ice_servers) = 0; - }; - - virtual bool RegisterSignaling() = 0; - virtual bool UnregisterSignaling() = 0; - virtual bool SendMessage(std::string_view peer_id, + virtual bool SendMessage(absl::string_view peer_id, const ByteArray& message) = 0; - virtual bool StartReceivingMessages( - const SignalingMessageListener& listener) = 0; - virtual void GetIceServers( - const IceServersListener& ice_servers_listener) = 0; + + virtual bool StartReceivingMessages(OnSignalingMessageCallback listener) = 0; + virtual void StopReceivingMessages() = 0; +}; + +class WebRtcMedium { + public: + using PeerConnectionCallback = + std::function)>; + + virtual ~WebRtcMedium() = default; + + // Creates and returns a new webrtc::PeerConnectionInterface object via + // |callback|. + virtual void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, + PeerConnectionCallback callback) = 0; + + // Returns a signaling messenger for sending WebRTC signaling messages. + virtual std::unique_ptr GetSignalingMessenger( + absl::string_view self_id) = 0; }; } // namespace api diff --git a/cpp/platform_v2/base/BUILD b/cpp/platform_v2/base/BUILD index d11245eb..81c320fa 100644 --- a/cpp/platform_v2/base/BUILD +++ b/cpp/platform_v2/base/BUILD @@ -24,6 +24,7 @@ cc_library( "//platform_v2/api:__subpackages__", ], deps = [ + "//absl/meta:type_traits", "//absl/strings", "//absl/time", ], @@ -32,9 +33,11 @@ cc_library( cc_library( name = "util", srcs = [ + "base_input_stream.cc", "base_pipe.cc", ], hdrs = [ + "base_input_stream.h", "base_mutex_lock.h", "base_pipe.h", ], @@ -44,11 +47,47 @@ cc_library( ], deps = [ ":base", - "//platform_v2/api", + "//platform_v2/api:types", "//absl/base:core_headers", ], ) +cc_library( + name = "logging", + hdrs = [ + "logging.h", + ], + visibility = [ + "//platform_v2:__subpackages__", + ], + deps = [ + "//platform:logging", + ], +) + +cc_library( + name = "test_util", + testonly = True, + srcs = [ + "medium_environment.cc", + ], + hdrs = [ + "medium_environment.h", + ], + visibility = [ + "//core_v2:__subpackages__", + "//platform_v2/impl:__subpackages__", + "//platform_v2/public:__pkg__", + ], + deps = [ + ":base", + ":logging", + "//platform_v2/api:comm", + "//platform_v2/public:types", + "//absl/container:flat_hash_map", + ], +) + cc_test( name = "platform_base_test", srcs = [ diff --git a/cpp/platform_v2/base/base_input_stream.cc b/cpp/platform_v2/base/base_input_stream.cc new file mode 100644 index 00000000..7c78bb36 --- /dev/null +++ b/cpp/platform_v2/base/base_input_stream.cc @@ -0,0 +1,85 @@ +#include "platform_v2/base/base_input_stream.h" + +namespace location { +namespace nearby { + +ExceptionOr BaseInputStream::Read(std::int64_t size) { + if (!IsAvailable(size)) { + return ExceptionOr{Exception::kIo}; + } + + ByteArray read_bytes{static_cast(size)}; + if (read_bytes.CopyAt(/*offset=*/0, buffer_, + /*source_offset=*/position_)) { + position_ += size; + return ExceptionOr{read_bytes}; + } else { + return ExceptionOr{Exception::kIo}; + } +} + +std::uint8_t BaseInputStream::ReadUint8() { + constexpr int byte_size = sizeof(std::uint8_t); + ByteArray read_bytes = ReadBytes(byte_size); + if (read_bytes.Empty() || read_bytes.size() != byte_size) { + return -1; + } + + return read_bytes.data()[0]; +} + +std::uint16_t BaseInputStream::ReadUint16() { + constexpr int byte_size = sizeof(std::uint16_t); + ByteArray read_bytes = ReadBytes(byte_size); + if (read_bytes.Empty() || read_bytes.size() != byte_size) { + return -1; + } + + // Convert from network order. + const char *data = read_bytes.data(); + return static_cast(data[0]) << 8 | static_cast(data[1]); +} + +std::uint32_t BaseInputStream::ReadUint32() { + constexpr int byte_size = sizeof(std::uint32_t); + ByteArray read_bytes = ReadBytes(byte_size); + if (read_bytes.Empty() || read_bytes.size() != byte_size) { + return -1; + } + + // Convert from network order. + const char *data = read_bytes.data(); + return static_cast(data[0]) << 24 | + static_cast(data[1]) << 16 | + static_cast(data[2]) << 8 | static_cast(data[3]); +} + +std::uint64_t BaseInputStream::ReadUint64() { + constexpr int byte_size = sizeof(std::uint64_t); + ByteArray read_bytes = ReadBytes(byte_size); + if (read_bytes.Empty() || read_bytes.size() != byte_size) { + return -1; + } + + // Convert from network order. + const char *data = read_bytes.data(); + return static_cast(data[0]) << 56 | + static_cast(data[1]) << 48 | + static_cast(data[2]) << 40 | + static_cast(data[3]) << 32 | + static_cast(data[4]) << 24 | + static_cast(data[5]) << 16 | + static_cast(data[6]) << 8 | static_cast(data[7]); +} + +ByteArray BaseInputStream::ReadBytes(int size) { + ExceptionOr read_bytes_result = Read(size); + if (!read_bytes_result.ok()) { + return ByteArray{}; + } + + return read_bytes_result.GetResult(); +} + +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/base/base_input_stream.h b/cpp/platform_v2/base/base_input_stream.h new file mode 100644 index 00000000..12044b4d --- /dev/null +++ b/cpp/platform_v2/base/base_input_stream.h @@ -0,0 +1,44 @@ +#ifndef PLATFORM_V2_BASE_BASE_INPUT_STREAM_H_ +#define PLATFORM_V2_BASE_BASE_INPUT_STREAM_H_ + +#include "platform_v2/base/byte_array.h" +#include "platform_v2/base/exception.h" +#include "platform_v2/base/input_stream.h" + +namespace location { +namespace nearby { + +// A base {@link InputStream } for reading the contents of a byte array. +class BaseInputStream : public InputStream { + public: + explicit BaseInputStream(ByteArray &buffer) : buffer_{buffer} {} + BaseInputStream(const BaseInputStream &) = delete; + BaseInputStream &operator=(const BaseInputStream &) = delete; + ~BaseInputStream() override { Close(); } + + ExceptionOr Read(std::int64_t size) override; + + Exception Close() override { + // Do nothing. + return {Exception::kSuccess}; + } + + std::uint8_t ReadUint8(); + std::uint16_t ReadUint16(); + std::uint32_t ReadUint32(); + std::uint64_t ReadUint64(); + bool IsAvailable(int size) const { + return buffer_.size() - position_ >= size; + } + + private: + ByteArray ReadBytes(int size); + + ByteArray &buffer_; + int position_{0}; +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_BASE_BASE_INPUT_STREAM_H_ diff --git a/cpp/platform_v2/base/base_pipe.cc b/cpp/platform_v2/base/base_pipe.cc index e97ace56..bd20f935 100644 --- a/cpp/platform_v2/base/base_pipe.cc +++ b/cpp/platform_v2/base/base_pipe.cc @@ -1,6 +1,5 @@ #include "platform_v2/base/base_pipe.h" -#include "platform_v2/api/platform.h" #include "platform_v2/base/base_mutex_lock.h" #include "platform_v2/base/input_stream.h" #include "platform_v2/base/output_stream.h" diff --git a/cpp/platform_v2/base/byte_array.h b/cpp/platform_v2/base/byte_array.h index 81036f24..19063505 100644 --- a/cpp/platform_v2/base/byte_array.h +++ b/cpp/platform_v2/base/byte_array.h @@ -19,7 +19,9 @@ class ByteArray { ByteArray& operator=(ByteArray&&) = default; // Create ByteArray from string. - explicit ByteArray(absl::string_view source) { data_ = source; } + explicit ByteArray(absl::string_view source) { + SetData(source.data(), source.size()); + } // Create default-initialized ByteArray of a given size. explicit ByteArray(size_t size) { SetData(size); } diff --git a/cpp/platform_v2/base/exception.h b/cpp/platform_v2/base/exception.h index c9e73425..382c5728 100644 --- a/cpp/platform_v2/base/exception.h +++ b/cpp/platform_v2/base/exception.h @@ -1,9 +1,10 @@ #ifndef PLATFORM_V2_BASE_EXCEPTION_H_ #define PLATFORM_V2_BASE_EXCEPTION_H_ -#include #include +#include "absl/meta/type_traits.h" + namespace location { namespace nearby { @@ -64,7 +65,7 @@ class ExceptionOr { ExceptionOr(Exception exception) : exception_{exception} {} // NOLINT // If there exists explicit conversion from from U to T, // then allow explicit conversion from ExceptionOr to ExceptionOr. - template ()})>> + template ()})>> explicit ExceptionOr(ExceptionOr value) { if (!value.ok()) { exception_ = value.GetException(); diff --git a/cpp/platform_v2/base/logging.h b/cpp/platform_v2/base/logging.h new file mode 100644 index 00000000..f86e1a2e --- /dev/null +++ b/cpp/platform_v2/base/logging.h @@ -0,0 +1,6 @@ +#ifndef PLATFORM_V2_BASE_LOGGING_H_ +#define PLATFORM_V2_BASE_LOGGING_H_ + +#include "platform/logging.h" + +#endif // PLATFORM_V2_BASE_LOGGING_H_ diff --git a/cpp/platform_v2/base/medium_environment.cc b/cpp/platform_v2/base/medium_environment.cc new file mode 100644 index 00000000..6c47cc33 --- /dev/null +++ b/cpp/platform_v2/base/medium_environment.cc @@ -0,0 +1,191 @@ +#include "platform_v2/base/medium_environment.h" + +#include +#include +#include +#include + +#include "platform_v2/api/bluetooth_adapter.h" +#include "platform_v2/api/bluetooth_classic.h" +#include "platform_v2/base/logging.h" +#include "platform_v2/public/count_down_latch.h" + +namespace location { +namespace nearby { + +MediumEnvironment& MediumEnvironment::Instance() { + static std::aligned_storage_t + storage; + static MediumEnvironment* env = new (&storage) MediumEnvironment(); + return *env; +} + +void MediumEnvironment::Reset() { + RunOnMediumEnvironmentThread([this]() { + bluetooth_adapters_.clear(); + bluetooth_mediums_.clear(); + }); + Sync(); +} + +void MediumEnvironment::Sync(bool enable_notifications) { + enable_notifications_ = enable_notifications; + int count = 0; + do { + CountDownLatch latch(1); + count = job_count_ + 1; + // We are about to schedule one last job. + // When it is done, counter must be equal to count. + // However, if pending jobs schedule anything else, + // it will be pending after us. + // If we want to ensure we are completely idle, then we have to + // repeat sync, until this becomes true. + RunOnMediumEnvironmentThread([&latch]() { latch.CountDown(); }); + latch.Await(); + } while (count < job_count_); + NEARBY_LOG(INFO, "MediumEnvironment::Sync(): done [count=%d]", count); +} + +void MediumEnvironment::OnBluetoothAdapterChangedState( + api::BluetoothAdapter& adapter, api::BluetoothDevice& adapter_device, + std::string name, bool enabled, api::BluetoothAdapter::ScanMode mode) { + RunOnMediumEnvironmentThread([this, &adapter, &adapter_device, + name = std::move(name), enabled, mode]() { + NEARBY_LOG(INFO, + "[adapter=%p, device=%p] update: name=%s, enabled=%d, mode=%d", + &adapter, &adapter_device, name.c_str(), enabled, mode); + for (auto& [medium, info] : bluetooth_mediums_) { + // Do not send notification to medium that owns this adapter. + if (info.adapter == &adapter) continue; + NEARBY_LOG(INFO, "[adapter=%p, device=%p] notify: adapter=%p", &adapter, + &adapter_device, info.adapter); + OnDeviceStateChanged(info, adapter_device, name, mode, enabled); + } + // We don't care if there is an adapter already since all we store is a + // pointer. Pointer must remain valid for the duration of a Core session + // (since it is owned by the correspoinding Medium, and mediums lifetime + // matches Core lifetime). + bluetooth_adapters_.emplace(&adapter, &adapter_device); + }); +} + +void MediumEnvironment::OnDeviceStateChanged( + BluetoothMediumContext& info, api::BluetoothDevice& device, + const std::string& name, api::BluetoothAdapter::ScanMode mode, + bool enabled) { + auto item = info.devices.find(&device); + if (item == info.devices.end()) { + NEARBY_LOG( + INFO, "G3 OnDeviceStateChanged [device impl=%p]: new device; notify=%d", + &device, enable_notifications_.load()); + if (mode == api::BluetoothAdapter::ScanMode::kConnectableDiscoverable && + enabled) { + // New device is turned on, and is in discoverable state. + // Store device name, and report it as discovered. + info.devices.emplace(&device, name); + if (enable_notifications_) { + RunOnMediumEnvironmentThread( + [&info, &device]() { info.callback.device_discovered_cb(device); }); + } + } + } else { + NEARBY_LOG( + INFO, + "G3 OnDeviceStateChanged [device impl=%p]: exisitng device; notify=%d", + &device, enable_notifications_.load()); + auto& discovered_name = item->second; + if (mode == api::BluetoothAdapter::ScanMode::kConnectableDiscoverable && + enabled) { + if (name != discovered_name) { + // Known device is turned on, and is in discoverable state. + // Store device name, and report it as renamed. + item->second = name; + if (enable_notifications_) { + RunOnMediumEnvironmentThread([&info, &device]() { + info.callback.device_name_changed_cb(device); + }); + } + } else { + // Device is in discovery mode, so we are reporting it anyway. + if (enable_notifications_) { + RunOnMediumEnvironmentThread([&info, &device]() { + info.callback.device_discovered_cb(device); + }); + } + } + } + if (!enabled) { + // Known device is turned off. + // Erase it from the map, and report as lost. + if (enable_notifications_) { + RunOnMediumEnvironmentThread( + [&info, &device]() { info.callback.device_lost_cb(device); }); + } + info.devices.erase(item); + } + } +} + +void MediumEnvironment::RunOnMediumEnvironmentThread( + std::function runnable) { + job_count_++; + executor_.Execute(std::move(runnable)); +} + +void MediumEnvironment::RegisterBluetoothMedium( + api::BluetoothClassicMedium& medium, + api::BluetoothAdapter& medium_adapter) { + RunOnMediumEnvironmentThread([this, &medium, &medium_adapter]() { + auto& context = bluetooth_mediums_ + .insert({&medium, + BluetoothMediumContext{ + .adapter = &medium_adapter, + }}) + .first->second; + auto* owned_adapter = context.adapter; + NEARBY_LOG(INFO, "Registered: medium=%p; adapter=%p", &medium, + owned_adapter); + for (auto& [adapter, device] : bluetooth_adapters_) { + if (adapter == nullptr) continue; + OnDeviceStateChanged(context, *device, adapter->GetName(), + adapter->GetScanMode(), adapter->IsEnabled()); + } + }); +} + +void MediumEnvironment::UpdateBluetoothMedium( + api::BluetoothClassicMedium& medium, BluetoothDiscoveryCallback callback) { + RunOnMediumEnvironmentThread([this, &medium, + callback = std::move(callback)]() { + auto item = bluetooth_mediums_.find(&medium); + if (item == bluetooth_mediums_.end()) return; + auto& context = item->second; + context.callback = std::move(callback); + auto* owned_adapter = context.adapter; + NEARBY_LOG( + INFO, + "Updated: this=%p; medium=%p; adapter=%p; name=%s; enabled=%d; mode=%d", + this, &medium, owned_adapter, owned_adapter->GetName().c_str(), + owned_adapter->IsEnabled(), owned_adapter->GetScanMode()); + for (auto& [adapter, device] : bluetooth_adapters_) { + if (adapter == nullptr) continue; + OnDeviceStateChanged(context, *device, adapter->GetName(), + adapter->GetScanMode(), adapter->IsEnabled()); + } + }); +} + +void MediumEnvironment::UnregisterBluetoothMedium( + api::BluetoothClassicMedium& medium) { + RunOnMediumEnvironmentThread([this, &medium]() { + auto item = bluetooth_mediums_.extract(&medium); + if (item.empty()) return; + auto& context = item.mapped(); + NEARBY_LOG(INFO, "Unregistered medium for device=%s", + context.adapter->GetName().c_str()); + }); +} + +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/base/medium_environment.h b/cpp/platform_v2/base/medium_environment.h new file mode 100644 index 00000000..b00eafc2 --- /dev/null +++ b/cpp/platform_v2/base/medium_environment.h @@ -0,0 +1,113 @@ +#ifndef PLATFORM_V2_BASE_MEDIUM_ENVIRONMENT_H_ +#define PLATFORM_V2_BASE_MEDIUM_ENVIRONMENT_H_ + +#include + +#include "platform_v2/api/bluetooth_adapter.h" +#include "platform_v2/api/bluetooth_classic.h" +#include "platform_v2/base/listeners.h" +#include "platform_v2/public/single_thread_executor.h" +#include "absl/container/flat_hash_map.h" + +namespace location { +namespace nearby { + +// MediumEnvironment is a simulated environment which allows multiple instances +// of simulated HW devices to "work" together as if they are physical. +// For each medium type it provides necessary methods to implement +// advertising, discovery and establishment of a data link. +// NOTE: this code depends on public:types target. +class MediumEnvironment { + public: + using BluetoothDiscoveryCallback = + api::BluetoothClassicMedium::DiscoveryCallback; + MediumEnvironment(const MediumEnvironment&) = delete; + MediumEnvironment& operator=(const MediumEnvironment&) = delete; + + // Creates and returns a reference to the global test environment instance. + static MediumEnvironment& Instance(); + + // Clears state. No notifications are sent. + void Reset(); + + // Waits for all previously scheduled jobs to finish. + // This method works as a barrier that guarantees that after it returns, all + // the activities that started before it was called, or while it was running + // are ended. This means that system is at the state of relaxation when this + // code returns. It requires external stimulus to get out of relaxation state. + // + // If enable_notifications is true (default), simulation environment + // will send all future notification events to all registered objects, + // whenever protocol requires that. This is expected behavior. + // If enabled_notifications is false, future event notifications will not be + // sent to registered instances. This is useful for protocol shutdown, + // where we no longer care about notifications, and where notifications may + // otherwise be delivered after the notification source or target lifeteme has + // ended, and cause undefined behavior. + void Sync(bool enable_notifications = true); + + // Adds an adapter to internal container. + // Notify BluetoothClassicMediums if any that adapter state has changed. + void OnBluetoothAdapterChangedState(api::BluetoothAdapter& adapter, + api::BluetoothDevice& adapter_device, + std::string name, bool enabled, + api::BluetoothAdapter::ScanMode mode); + + // Adds medium-related info to allow for adapter discovery to work. + // This provides acccess to this medium from other mediums, when protocol + // expects they should communicate. + void RegisterBluetoothMedium(api::BluetoothClassicMedium& medium, + api::BluetoothAdapter& medium_adapter); + + // Updates 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 UpdateBluetoothMedium(api::BluetoothClassicMedium& medium, + BluetoothDiscoveryCallback callback); + + // Removes medium-related info. This should correspond to device power off. + void UnregisterBluetoothMedium(api::BluetoothClassicMedium& medium); + + private: + struct BluetoothMediumContext { + BluetoothDiscoveryCallback callback; + api::BluetoothAdapter* adapter = nullptr; + // discovered device vs device name map. + absl::flat_hash_map devices; + }; + + // This is a singleton object, for which destructor will never be called. + // Constructor will be invoked once from Instance() static method. + // Object is create in-place (with a placement new) to guarantee that + // destructor is not scheduled for execution at exit. + MediumEnvironment() = default; + ~MediumEnvironment() = default; + + void OnDeviceStateChanged(BluetoothMediumContext& info, + api::BluetoothDevice& device, + const std::string& name, + api::BluetoothAdapter::ScanMode mode, bool enabled); + void RunOnMediumEnvironmentThread(std::function runnable); + + std::atomic_int job_count_ = 0; + std::atomic_bool enable_notifications_ = false; + SingleThreadExecutor executor_; + + // The following data members are accessed in the context of a private + // executor_ thread. + absl::flat_hash_map + bluetooth_adapters_; + absl::flat_hash_map + bluetooth_mediums_; +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_BASE_MEDIUM_ENVIRONMENT_H_ diff --git a/cpp/platform_v2/impl/g3/BUILD b/cpp/platform_v2/impl/g3/BUILD index 8f99b81f..58b31cb9 100644 --- a/cpp/platform_v2/impl/g3/BUILD +++ b/cpp/platform_v2/impl/g3/BUILD @@ -1,38 +1,32 @@ cc_library( - name = "g3", + name = "types", + testonly = True, srcs = [ + "scheduled_executor.cc", + "system_clock.cc", + ], + hdrs = [ "atomic_boolean.h", "atomic_reference_any.h", - "bluetooth_adapter.cc", - "bluetooth_adapter.h", "condition_variable.h", "count_down_latch.h", - "medium_environment.cc", - "medium_environment.h", "multi_thread_executor.h", "mutex.h", - "platform.cc", - "scheduled_executor.cc", + "pipe.h", "scheduled_executor.h", "settable_future_any.h", "single_thread_executor.h", - "system_clock.cc", ], visibility = [ - "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", - "//core_v2:__subpackages__", - "//platform_v2:__subpackages__", + "//platform_v2/impl/g3:__pkg__", ], deps = [ - ":crypto", # build_cleaner: keep - "//platform_v2/api", + "//platform_v2/api:platform", + "//platform_v2/api:types", "//platform_v2/base", + "//platform_v2/base:util", "//platform_v2/impl/shared:posix_mutex", "//absl/base:core_headers", - "//absl/container:flat_hash_map", - "//absl/container:flat_hash_set", - "//absl/memory", - "//absl/strings", "//absl/synchronization", "//absl/time", "//absl/types:any", @@ -40,8 +34,36 @@ cc_library( ], ) +cc_library( + name = "comm", + testonly = True, + srcs = [ + "bluetooth_adapter.cc", + "webrtc.cc", + ], + hdrs = [ + "bluetooth_adapter.h", + "webrtc.h", + ], + visibility = [ + "//platform_v2/impl/g3:__pkg__", + ], + deps = [ + ":types", + "//platform_v2/api:comm", + "//platform_v2/base:test_util", + "//absl/base:core_headers", + "//absl/strings", + "//absl/synchronization", + "//webrtc/api:create_peerconnection_factory", #buildcleaner: keep + "//webrtc/api:libjingle_peerconnection_api", + "//webrtc/api/task_queue:default_task_queue_factory", + ], +) + cc_library( name = "crypto", + testonly = True, srcs = [ "crypto.cc", ], @@ -49,9 +71,34 @@ cc_library( "//platform_v2/g3:__pkg__", ], deps = [ - "//platform_v2/api", + "//platform_v2/api:types", "//platform_v2/base", "//absl/strings", "//openssl:crypto", ], ) + +cc_library( + name = "g3", + testonly = True, + srcs = [ + "platform.cc", + ], + visibility = [ + "//googlemac/iPhone/Shared/Nearby/Connections:__subpackages__", + "//core_v2:__subpackages__", + "//platform_v2:__subpackages__", + ], + deps = [ + ":comm", + ":crypto", # build_cleaner: keep + ":types", + "//platform_v2/api:comm", + "//platform_v2/api:platform", + "//platform_v2/api:types", + "//platform_v2/impl/shared:file", + "//absl/base:core_headers", + "//absl/memory", + "//absl/time", + ], +) diff --git a/cpp/platform_v2/impl/g3/bluetooth_adapter.cc b/cpp/platform_v2/impl/g3/bluetooth_adapter.cc index 16059f53..505ead84 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_adapter.cc +++ b/cpp/platform_v2/impl/g3/bluetooth_adapter.cc @@ -2,7 +2,7 @@ #include -#include "platform_v2/impl/g3/medium_environment.h" +#include "platform_v2/base/medium_environment.h" namespace location { namespace nearby { @@ -11,15 +11,22 @@ namespace g3 { BluetoothDevice::BluetoothDevice(BluetoothAdapter* adapter) : adapter_(*adapter) {} +BluetoothAdapter::~BluetoothAdapter() { SetStatus(Status::kDisabled); } + std::string BluetoothDevice::GetName() const { return adapter_.GetName(); } -bool BluetoothAdapter::SetStatus(Status status) ABSL_LOCKS_EXCLUDED(mutex_) { - absl::MutexLock lock(&mutex_); - enabled_ = (status == Status::kEnabled); - RunOnCallbackThread([this]() { - auto& env = MediumEnvironment::Instance(); - env.OnBluetoothAdapterChangedState(*this); - }); +bool BluetoothAdapter::SetStatus(Status status) { + BluetoothAdapter::ScanMode mode; + bool enabled = status == Status::kEnabled; + std::string name; + { + absl::MutexLock lock(&mutex_); + enabled_ = enabled; + name = name_; + mode = mode_; + } + auto& env = MediumEnvironment::Instance(); + env.OnBluetoothAdapterChangedState(*this, device_, name, enabled, mode); return true; } @@ -34,13 +41,17 @@ BluetoothAdapter::ScanMode BluetoothAdapter::GetScanMode() const { } bool BluetoothAdapter::SetScanMode(BluetoothAdapter::ScanMode mode) { - absl::MutexLock lock(&mutex_); - if (enabled_) return false; - mode_ = mode; - RunOnCallbackThread([this]() { - auto& env = MediumEnvironment::Instance(); - env.OnBluetoothAdapterChangedState(*this); - }); + bool enabled; + std::string name; + { + absl::MutexLock lock(&mutex_); + mode_ = mode; + name = name_; + enabled = enabled_; + } + auto& env = MediumEnvironment::Instance(); + env.OnBluetoothAdapterChangedState(*this, device_, std::move(name), enabled, + mode); return true; } @@ -50,13 +61,17 @@ std::string BluetoothAdapter::GetName() const { } bool BluetoothAdapter::SetName(absl::string_view name) { - absl::MutexLock lock(&mutex_); - if (enabled_) return false; - name_ = name; - RunOnCallbackThread([this]() { - auto& env = MediumEnvironment::Instance(); - env.OnBluetoothAdapterChangedState(*this); - }); + BluetoothAdapter::ScanMode mode; + bool enabled; + { + absl::MutexLock lock(&mutex_); + name_ = name; + enabled = enabled_; + mode = mode_; + } + auto& env = MediumEnvironment::Instance(); + env.OnBluetoothAdapterChangedState(*this, device_, std::string(name), enabled, + mode); return true; } diff --git a/cpp/platform_v2/impl/g3/bluetooth_adapter.h b/cpp/platform_v2/impl/g3/bluetooth_adapter.h index 2654df4b..9747d7e0 100644 --- a/cpp/platform_v2/impl/g3/bluetooth_adapter.h +++ b/cpp/platform_v2/impl/g3/bluetooth_adapter.h @@ -24,7 +24,7 @@ class BluetoothDevice : public api::BluetoothDevice { // https://developer.android.com/reference/android/bluetooth/BluetoothDevice.html#getName() std::string GetName() const override; - BluetoothAdapter& GetAdapter(); + BluetoothAdapter& GetAdapter() { return adapter_; } private: // Only BluetoothAdapter may instantiate BluetoothDevice. @@ -41,8 +41,8 @@ class BluetoothAdapter : public api::BluetoothAdapter { using Status = api::BluetoothAdapter::Status; using ScanMode = api::BluetoothAdapter::ScanMode; - BluetoothAdapter() = default; - ~BluetoothAdapter() override = default; + explicit BluetoothAdapter() = default; + ~BluetoothAdapter() override; // Synchronously sets the status of the BluetoothAdapter to 'status', and // returns true if the operation was a success. @@ -71,16 +71,11 @@ class BluetoothAdapter : public api::BluetoothAdapter { BluetoothDevice& GetDevice() { return device_; } private: - void RunOnCallbackThread(std::function runnable) { - serial_executor_.Execute(std::move(runnable)); - } - mutable absl::Mutex mutex_; BluetoothDevice device_{this}; 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; - SingleThreadExecutor serial_executor_; }; } // namespace g3 diff --git a/cpp/platform_v2/impl/g3/medium_environment.cc b/cpp/platform_v2/impl/g3/medium_environment.cc deleted file mode 100644 index 5512a4fc..00000000 --- a/cpp/platform_v2/impl/g3/medium_environment.cc +++ /dev/null @@ -1,32 +0,0 @@ -#include "platform_v2/impl/g3/medium_environment.h" - -namespace location { -namespace nearby { -namespace g3 { - -MediumEnvironment& MediumEnvironment::Instance() { - static std::aligned_storage_t - storage; - static MediumEnvironment* env = new (&storage) MediumEnvironment(); - return *env; -} - -void MediumEnvironment::Reset() { - absl::MutexLock lock(&mutex_); - bluetooth_adapters_.clear(); -} - -void MediumEnvironment::OnBluetoothAdapterChangedState( - BluetoothAdapter& adapter) { - absl::MutexLock lock(&mutex_); - // We don't care if there is an adapter already since all we store is a - // pointer. - bluetooth_adapters_.emplace(&adapter); - // TODO(apolyudov): Add event propagation code when Medium registration is - // implemented. -} - -} // namespace g3 -} // namespace nearby -} // namespace location diff --git a/cpp/platform_v2/impl/g3/medium_environment.h b/cpp/platform_v2/impl/g3/medium_environment.h deleted file mode 100644 index 3f3f73c6..00000000 --- a/cpp/platform_v2/impl/g3/medium_environment.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_ -#define PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_ - -#include -#include -#include - -#include "platform_v2/api/bluetooth_classic.h" -#include "platform_v2/impl/g3/bluetooth_adapter.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" -#include "absl/synchronization/mutex.h" - -namespace location { -namespace nearby { -namespace g3 { - -// MediumEnvironment is a simulated environment which allowes multiple instances -// of simulated HW devices to "work" together as if they are physical. -// For each medium type it provides necessary methods to implement -// advertising, discovery and establishment of a data link. -class MediumEnvironment { - public: - ~MediumEnvironment() = default; - // Singleton constructor/accessor. - static MediumEnvironment& Instance(); - - // Clear state. No notifications are sent. - void Reset() ABSL_LOCKS_EXCLUDED(mutex_); - - // Add an adapter to internal container. - // Notify BluetoothClassicMediums if any that adapter state has changed. - void OnBluetoothAdapterChangedState(BluetoothAdapter& adapter) - ABSL_LOCKS_EXCLUDED(mutex_); - - private: - MediumEnvironment() = default; - absl::Mutex mutex_; - absl::flat_hash_set bluetooth_adapters_ - ABSL_GUARDED_BY(mutex_); -}; - -} // namespace g3 -} // namespace nearby -} // namespace location - -#endif // PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_ diff --git a/cpp/platform_v2/impl/g3/platform.cc b/cpp/platform_v2/impl/g3/platform.cc index 412dbacb..73673f77 100644 --- a/cpp/platform_v2/impl/g3/platform.cc +++ b/cpp/platform_v2/impl/g3/platform.cc @@ -28,6 +28,8 @@ #include "platform_v2/impl/g3/scheduled_executor.h" #include "platform_v2/impl/g3/settable_future_any.h" #include "platform_v2/impl/g3/single_thread_executor.h" +#include "platform_v2/impl/g3/webrtc.h" +#include "platform_v2/impl/shared/file.h" #include "absl/base/integral_types.h" #include "absl/memory/memory.h" #include "absl/time/time.h" @@ -36,6 +38,12 @@ namespace location { namespace nearby { namespace api { +namespace { +std::string GetPayloadPath(std::int64_t payload_id) { + return "/tmp/" + std::to_string(payload_id); +} +} // namespace + std::unique_ptr ImplementationPlatform::CreateSingleThreadExecutor() { return absl::make_unique(); @@ -76,6 +84,17 @@ std::unique_ptr ImplementationPlatform::CreateAtomicBoolean( return absl::make_unique(initial_value); } +std::unique_ptr ImplementationPlatform::CreateInputFile( + std::int64_t payload_id, std::int64_t total_size) { + return absl::make_unique(GetPayloadPath(payload_id), + total_size); +} + +std::unique_ptr ImplementationPlatform::CreateOutputFile( + std::int64_t payload_id) { + return absl::make_unique(GetPayloadPath(payload_id)); +} + std::unique_ptr ImplementationPlatform::CreateBluetoothClassicMedium() { return std::unique_ptr(); @@ -102,11 +121,8 @@ std::unique_ptr ImplementationPlatform::CreateWifiLanMedium() { return std::unique_ptr(); } -std::unique_ptr -ImplementationPlatform::CreateWebRtcSignalingMessenger( - absl::string_view self_id) { - return std::unique_ptr( - /*new FCMSignalingMessenger()*/); +std::unique_ptr ImplementationPlatform::CreateWebRtcMedium() { + return absl::make_unique(); } std::unique_ptr ImplementationPlatform::CreateMutex(Mutex::Mode mode) { @@ -127,10 +143,6 @@ std::string ImplementationPlatform::GetDeviceId() { return "google3"; } -std::string ImplementationPlatform::GetPayloadPath(int64_t payload_id) { - return "/tmp/" + std::to_string(payload_id); -} - } // namespace api } // namespace nearby } // namespace location diff --git a/cpp/platform_v2/impl/g3/webrtc.cc b/cpp/platform_v2/impl/g3/webrtc.cc new file mode 100644 index 00000000..6e70be50 --- /dev/null +++ b/cpp/platform_v2/impl/g3/webrtc.cc @@ -0,0 +1,36 @@ +#include "platform_v2/impl/g3/webrtc.h" + +#include "webrtc/api/task_queue/default_task_queue_factory.h" + +namespace location { +namespace nearby { +namespace g3 { + +void WebRtcMedium::CreatePeerConnection( + webrtc::PeerConnectionObserver* observer, PeerConnectionCallback callback) { + webrtc::PeerConnectionInterface::RTCConfiguration rtc_config; + webrtc::PeerConnectionDependencies dependencies(observer); + + std::unique_ptr signaling_thread = rtc::Thread::Create(); + signaling_thread->SetName("signaling_thread", nullptr); + RTC_CHECK(signaling_thread->Start()) << "Failed to start thread"; + + webrtc::PeerConnectionFactoryDependencies factory_dependencies; + factory_dependencies.task_queue_factory = + webrtc::CreateDefaultTaskQueueFactory(); + factory_dependencies.signaling_thread = signaling_thread.release(); + + callback(webrtc::CreateModularPeerConnectionFactory( + std::move(factory_dependencies)) + ->CreatePeerConnection(rtc_config, std::move(dependencies))); +} + +std::unique_ptr +WebRtcMedium::GetSignalingMessenger(absl::string_view self_id) { + // TODO(bfranz): Implement + return nullptr; +} + +} // namespace g3 +} // namespace nearby +} // namespace location diff --git a/cpp/platform_v2/impl/g3/webrtc.h b/cpp/platform_v2/impl/g3/webrtc.h new file mode 100644 index 00000000..053a30b8 --- /dev/null +++ b/cpp/platform_v2/impl/g3/webrtc.h @@ -0,0 +1,35 @@ +#ifndef PLATFORM_V2_IMPL_G3_WEBRTC_H_ +#define PLATFORM_V2_IMPL_G3_WEBRTC_H_ + +#include + +#include "platform_v2/api/webrtc.h" +#include "absl/strings/string_view.h" +#include "webrtc/api/peer_connection_interface.h" + +namespace location { +namespace nearby { +namespace g3 { + +class WebRtcMedium : public api::WebRtcMedium { + public: + using PeerConnectionCallback = api::WebRtcMedium::PeerConnectionCallback; + + WebRtcMedium() = default; + ~WebRtcMedium() override = default; + + // Creates and returns a new webrtc::PeerConnectionInterface object via + // |callback|. + void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, + PeerConnectionCallback callback) override; + + // Returns a signaling messenger for sending WebRTC signaling messages. + std::unique_ptr GetSignalingMessenger( + absl::string_view self_id) override; +}; + +} // namespace g3 +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_IMPL_G3_WEBRTC_H_ diff --git a/cpp/platform_v2/impl/shared/BUILD b/cpp/platform_v2/impl/shared/BUILD index 013a0192..83d54e78 100644 --- a/cpp/platform_v2/impl/shared/BUILD +++ b/cpp/platform_v2/impl/shared/BUILD @@ -9,10 +9,7 @@ cc_library( visibility = [ "//platform_v2/impl:__subpackages__", ], - deps = [ - "//platform_v2/api", - "//platform_v2/base", - ], + deps = ["//platform_v2/api:types"], ) cc_library( @@ -28,7 +25,31 @@ cc_library( ], deps = [ ":posix_mutex", - "//platform_v2/api", + "//platform_v2/api:types", + ], +) + +cc_library( + name = "file", + srcs = ["file.cc"], + hdrs = ["file.h"], + visibility = [ + "//platform_v2/impl:__subpackages__", + ], + deps = [ + "//platform_v2/api:types", "//platform_v2/base", + "//absl/strings", + ], +) + +cc_test( + name = "file_test", + srcs = ["file_test.cc"], + deps = [ + ":file", + "//file/util:temp_path", + "//platform_v2/base", + "//testing/base/public:gunit_main", ], ) diff --git a/cpp/platform_v2/public/file.cc b/cpp/platform_v2/impl/shared/file.cc similarity index 91% rename from cpp/platform_v2/public/file.cc rename to cpp/platform_v2/impl/shared/file.cc index 63e5bc8c..50571b02 100644 --- a/cpp/platform_v2/public/file.cc +++ b/cpp/platform_v2/impl/shared/file.cc @@ -1,4 +1,4 @@ -#include "platform_v2/public/file.h" +#include "platform_v2/impl/shared/file.h" #include #include @@ -8,6 +8,7 @@ namespace location { namespace nearby { +namespace shared { // InputFile @@ -47,7 +48,7 @@ Exception InputFile::Close() { // OutputFile -OutputFile::OutputFile(absl::string_view path) : file_(path) {} +OutputFile::OutputFile(absl::string_view path) : file_(std::string(path)) {} Exception OutputFile::Write(const ByteArray& data) { if (!file_.is_open()) { @@ -75,5 +76,6 @@ Exception OutputFile::Close() { return {Exception::kSuccess}; } +} // namespace shared } // namespace nearby } // namespace location diff --git a/cpp/platform_v2/impl/shared/file.h b/cpp/platform_v2/impl/shared/file.h new file mode 100644 index 00000000..69e491ce --- /dev/null +++ b/cpp/platform_v2/impl/shared/file.h @@ -0,0 +1,53 @@ +#ifndef PLATFORM_V2_IMPL_SHARED_FILE_H_ +#define PLATFORM_V2_IMPL_SHARED_FILE_H_ + +#include +#include + +#include "platform_v2/api/input_file.h" +#include "platform_v2/api/output_file.h" +#include "platform_v2/base/exception.h" +#include "absl/strings/string_view.h" + +namespace location { +namespace nearby { +namespace shared { + +class InputFile final : public api::InputFile { + public: + explicit InputFile(const std::string& path, std::int64_t size); + ~InputFile() override = default; + InputFile(InputFile&&) = default; + InputFile& operator=(InputFile&&) = default; + + ExceptionOr Read(std::int64_t size) override; + std::string GetFilePath() const override { return path_; } + std::int64_t GetTotalSize() const override { return total_size_; } + Exception Close() override; + + private: + std::ifstream file_; + std::string path_; + std::int64_t total_size_; +}; + +class OutputFile final : public api::OutputFile { + public: + explicit OutputFile(absl::string_view path); + ~OutputFile() override = default; + OutputFile(OutputFile&&) = default; + OutputFile& operator=(OutputFile&&) = default; + + Exception Write(const ByteArray& data) override; + Exception Flush() override; + Exception Close() override; + + private: + std::ofstream file_; +}; + +} // namespace shared +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_IMPL_SHARED_FILE_H_ diff --git a/cpp/platform_v2/public/file_test.cc b/cpp/platform_v2/impl/shared/file_test.cc similarity index 97% rename from cpp/platform_v2/public/file_test.cc rename to cpp/platform_v2/impl/shared/file_test.cc index d7d0a77d..69f7975e 100644 --- a/cpp/platform_v2/public/file_test.cc +++ b/cpp/platform_v2/impl/shared/file_test.cc @@ -1,4 +1,4 @@ -#include "platform_v2/public/file.h" +#include "platform_v2/impl/shared/file.h" #include #include @@ -11,6 +11,7 @@ namespace location { namespace nearby { +namespace shared { class FileTest : public ::testing::Test { protected: @@ -127,5 +128,6 @@ TEST_F(FileTest, OutputFile_Close) { EXPECT_EQ(output_file.Write(bytes), Exception{Exception::kIo}); } +} // namespace shared } // namespace nearby } // namespace location diff --git a/cpp/platform_v2/public/BUILD b/cpp/platform_v2/public/BUILD index 5e260224..204d713a 100644 --- a/cpp/platform_v2/public/BUILD +++ b/cpp/platform_v2/public/BUILD @@ -1,13 +1,11 @@ cc_library( - name = "public", + name = "types", srcs = [ - "file.cc", "pipe.cc", ], hdrs = [ "atomic_boolean.h", "atomic_reference.h", - "bluetooth_adapter.h", "cancelable.h", "cancelable_alarm.h", "condition_variable.h", @@ -26,19 +24,38 @@ cc_library( ], visibility = [ "//core_v2:__subpackages__", - "//platform_v2/impl:__subpackages__", + "//platform_v2/base:__pkg__", + "//platform_v2/public:__pkg__", ], deps = [ - "//platform_v2/api", + "//platform_v2/api:platform", + "//platform_v2/api:types", "//platform_v2/base", "//platform_v2/base:util", "//absl/base:core_headers", - "//absl/strings", "//absl/time", "//absl/types:any", ], ) +cc_library( + name = "comm", + hdrs = [ + "bluetooth_adapter.h", + "webrtc.h", + ], + visibility = [ + "//core_v2:__subpackages__", + "//platform_v2/public:__pkg__", + ], + deps = [ + "//platform_v2/api:comm", + "//platform_v2/api:platform", + "//absl/strings", + "//webrtc/api:libjingle_peerconnection_api", + ], +) + cc_library( name = "logging", hdrs = [ @@ -50,7 +67,7 @@ cc_library( "//platform_v2:__subpackages__", ], deps = [ - "//platform:logging", + "//platform_v2/base:logging", ], ) @@ -62,7 +79,6 @@ cc_test( "bluetooth_adapter_test.cc", "count_down_latch_test.cc", "crypto_test.cc", - "file_test.cc", "future_test.cc", "logging_test.cc", "multi_thread_executor_test.cc", @@ -73,13 +89,12 @@ cc_test( ], shard_count = 16, deps = [ + ":comm", ":logging", - ":public", - "//file/util:temp_path", + ":types", "//platform_v2/base", - "//platform_v2/impl/g3", + "//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/file.h b/cpp/platform_v2/public/file.h index 1f8dbce3..59a46282 100644 --- a/cpp/platform_v2/public/file.h +++ b/cpp/platform_v2/public/file.h @@ -2,47 +2,53 @@ #define PLATFORM_V2_PUBLIC_FILE_H_ #include -#include +#include +#include #include "platform_v2/api/input_file.h" #include "platform_v2/api/output_file.h" +#include "platform_v2/api/platform.h" +#include "platform_v2/base/byte_array.h" #include "platform_v2/base/exception.h" -#include "absl/strings/string_view.h" namespace location { namespace nearby { class InputFile final : public api::InputFile { public: - explicit InputFile(const std::string& path, std::int64_t size); + using Platform = api::ImplementationPlatform; + InputFile(std::int64_t payload_id, std::int64_t size) + : impl_(Platform::CreateInputFile(payload_id, size)) {} ~InputFile() override = default; InputFile(InputFile&&) = default; InputFile& operator=(InputFile&&) = default; - ExceptionOr Read(std::int64_t size) override; - std::string GetFilePath() const override { return path_; } - std::int64_t GetTotalSize() const override { return total_size_; } - Exception Close() override; + ExceptionOr Read(std::int64_t size) override { + return impl_->Read(size); + } + std::string GetFilePath() const override { return impl_->GetFilePath(); } + std::int64_t GetTotalSize() const override { return impl_->GetTotalSize(); } + Exception Close() override { return impl_->Close(); } private: - std::ifstream file_; - std::string path_; - std::int64_t total_size_; + std::unique_ptr impl_; }; class OutputFile final : public api::OutputFile { public: - explicit OutputFile(absl::string_view path); + using Platform = api::ImplementationPlatform; + explicit OutputFile(std::int64_t payload_id) + : impl_(Platform::CreateOutputFile(payload_id)) {} ~OutputFile() override = default; OutputFile(OutputFile&&) = default; OutputFile& operator=(OutputFile&&) = default; - Exception Write(const ByteArray& data) override; - Exception Flush() override; - Exception Close() override; + Exception Write(const ByteArray& data) override { return impl_->Write(data); } + Exception Flush() override { return impl_->Flush(); } + Exception Close() override { return impl_->Close(); } private: - std::ofstream file_; + std::unique_ptr impl_; }; } // namespace nearby diff --git a/cpp/platform_v2/public/future.h b/cpp/platform_v2/public/future.h index aca9975f..fcd7b0ba 100644 --- a/cpp/platform_v2/public/future.h +++ b/cpp/platform_v2/public/future.h @@ -33,7 +33,7 @@ class Future final : public api::SettableFuture { ExceptionOr Get() override { auto ret_val = impl_->Get(); if (ret_val.ok()) { - T result = std::any_cast(ret_val.result()); + T result = absl::any_cast(ret_val.result()); return ExceptionOr{result}; } else { return ExceptionOr{ret_val.exception()}; @@ -46,7 +46,7 @@ class Future final : public api::SettableFuture { ExceptionOr Get(absl::Duration timeout) override { auto ret_val = impl_->Get(timeout); if (ret_val.ok()) { - T result = std::any_cast(ret_val.result()); + T result = absl::any_cast(ret_val.result()); return ExceptionOr{result}; } else { return ExceptionOr{ret_val.exception()}; diff --git a/cpp/platform_v2/public/logging.h b/cpp/platform_v2/public/logging.h index 5a9b4767..cde3df05 100644 --- a/cpp/platform_v2/public/logging.h +++ b/cpp/platform_v2/public/logging.h @@ -1,6 +1,6 @@ #ifndef PLATFORM_V2_PUBLIC_LOGGING_H_ #define PLATFORM_V2_PUBLIC_LOGGING_H_ -#include "platform/logging.h" +#include "platform_v2/base/logging.h" #endif // PLATFORM_V2_PUBLIC_LOGGING_H_ diff --git a/cpp/platform_v2/public/webrtc.h b/cpp/platform_v2/public/webrtc.h new file mode 100644 index 00000000..a5bc50de --- /dev/null +++ b/cpp/platform_v2/public/webrtc.h @@ -0,0 +1,44 @@ +#ifndef PLATFORM_V2_PUBLIC_WEBRTC_H_ +#define PLATFORM_V2_PUBLIC_WEBRTC_H_ + +#include + +#include "platform_v2/api/platform.h" +#include "platform_v2/api/webrtc.h" +#include "webrtc/api/peer_connection_interface.h" + +namespace location { +namespace nearby { + +class WebRtcMedium final { + public: + using PeerConnectionCallback = api::WebRtcMedium::PeerConnectionCallback; + + WebRtcMedium() : impl_(api::ImplementationPlatform::CreateWebRtcMedium()) {} + ~WebRtcMedium() = default; + WebRtcMedium(WebRtcMedium&&) = delete; + WebRtcMedium& operator=(WebRtcMedium&&) = delete; + + // Creates and returns a new webrtc::PeerConnectionInterface object via + // |callback|. + void CreatePeerConnection(webrtc::PeerConnectionObserver* observer, + PeerConnectionCallback callback) { + impl_->CreatePeerConnection(observer, std::move(callback)); + } + + // Returns a signaling messenger for sending WebRTC signaling messages. + std::unique_ptr GetSignalingMessenger( + absl::string_view self_id) { + return impl_->GetSignalingMessenger(self_id); + } + + bool IsValid() const { return impl_ != nullptr; } + + private: + std::unique_ptr impl_; +}; + +} // namespace nearby +} // namespace location + +#endif // PLATFORM_V2_PUBLIC_WEBRTC_H_ diff --git a/proto/connections/offline_wire_formats.proto b/proto/connections/offline_wire_formats.proto index 04f99cb7..e44e11b2 100644 --- a/proto/connections/offline_wire_formats.proto +++ b/proto/connections/offline_wire_formats.proto @@ -200,11 +200,15 @@ message BandwidthUpgradeNegotiationFrame { optional BluetoothCredentials bluetooth_credentials = 4; optional WifiAwareCredentials wifi_aware_credentials = 5; optional WifiDirectCredentials wifi_direct_credentials = 6; + + // Disable Encryption for this upgrade medium to improve throughput. + optional bool supports_disabling_encryption = 7; } // Accompanies CLIENT_INTRODUCTION events. message ClientIntroduction { optional string endpoint_id = 1; + optional bool supports_disabling_encryption = 2; } optional EventType event_type = 1; diff --git a/proto/sharing_enums.proto b/proto/sharing_enums.proto index 98e5df0d..6e87d13d 100644 --- a/proto/sharing_enums.proto +++ b/proto/sharing_enums.proto @@ -224,6 +224,8 @@ enum LogSource { // Represents the OEM partners (like Samsung) that we're working with to // verify functionality on their devices. OEM_DEVICES = 4; + // Represents the device for debugging. + DEBUG_DEVICES = 5; } // The Fast Share server action name.