Roll forward to cl/314549634

Change-Id: I4d1e7eacd5fa4078a094571ad6d5f51e422535ff
This commit is contained in:
Himanshu Jaju
2020-06-03 11:18:32 -07:00
committed by Alexey Polyudov
parent ae1c427b99
commit cffbc04508
92 changed files with 3804 additions and 697 deletions
+1
View File
@@ -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",
],
+1 -1
View File
@@ -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"
@@ -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<InternalPayload> InternalPayloadFactory<Platform>::createIncoming(
}
case PayloadTransferFrame::PayloadHeader::FILE: {
const std::string payload_path = Platform::getPayloadPath(payload_id);
Ptr<InputFile> input_file = MakePtr(new InputFileImpl(
payload_path, payload_transfer_frame.payload_header().total_size()));
Ptr<OutputFile> output_file = MakePtr(new OutputFileImpl(payload_path));
Ptr<OutputFile> output_file = Platform::createOutputFile(payload_id);
Ptr<InputFile> input_file = Platform::createInputFile(
payload_id, payload_transfer_frame.payload_header().total_size());
ConstPtr<Payload> payload = MakeConstPtr(
new Payload(payload_id, MakeConstPtr(new Payload::File(input_file))));
return MakePtr(new IncomingFileInternalPayload(
+3 -3
View File
@@ -17,17 +17,17 @@ namespace connections {
template <typename Platform>
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
+4 -4
View File
@@ -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
],
)
@@ -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 {
@@ -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 {
@@ -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 {
+1 -1
View File
@@ -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) {
+7 -4
View File
@@ -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",
+7 -2
View File
@@ -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",
+895 -1
View File
@@ -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<Status> 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<UKey2Handshake> 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<UKey2Handshake>(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<UKey2Handshake> 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<Status> 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<EndpointChannel> 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<UKey2Handshake> 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<proto::connections::Medium>& 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<Status>* 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<Status> 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<EndpointChannel> 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<Status> 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<EndpointChannel> 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<BasePcpHandler::DiscoveredEndpoint> 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<proto::connections::Medium> 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<EndpointChannel> 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<OfflineFrame> 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<proto::connections::Medium>& 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<proto::connections::Medium>& their_supported_mediums) {
// If the remote side did not report their supported mediums, choose an
// appropriate default.
std::vector<proto::connections::Medium> their_mediums =
their_supported_mediums;
if (their_supported_mediums.empty()) {
their_mediums.push_back(GetDefaultUpgradeMedium());
}
// Otherwise, pick the best medium we support.
std::vector<proto::connections::Medium> 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<OfflineFrame> BasePcpHandler::ReadConnectionRequestFrame(
EndpointChannel* endpoint_channel) {
if (endpoint_channel == nullptr) {
return ExceptionOr<OfflineFrame>(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<ByteArray> wrapped_bytes = endpoint_channel->Read();
timeout_alarm.Cancel();
if (!wrapped_bytes.ok()) {
return ExceptionOr<OfflineFrame>(wrapped_bytes.exception());
}
ByteArray bytes = std::move(wrapped_bytes.result());
ExceptionOr<OfflineFrame> wrapped_frame = parser::FromBytes(bytes);
if (wrapped_frame.GetException().Raised(Exception::kInvalidProtocolBuffer)) {
return ExceptionOr<OfflineFrame>(Exception::kIo);
}
OfflineFrame& frame = wrapped_frame.result();
if (V1Frame::CONNECTION_REQUEST != parser::GetFrameType(frame)) {
return ExceptionOr<OfflineFrame>(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
+124 -35
View File
@@ -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<EndpointChannel> 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<securegcm::UKey2Handshake> 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<Future<Status>> result = nullptr;
// Only (possibly) vector for incoming connections.
std::vector<proto::connections::Medium> supported_mediums;
// Keep track of a channel before we pass it to EndpointChannelManager.
std::unique_ptr<EndpointChannel> 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<securegcm::UKey2Handshake> ukey2;
};
// @EncryptionRunnerThread
// Called internally when DH session has negotiated a key successfully.
void OnEncryptionSuccessImpl(const std::string& endpoint_id,
std::unique_ptr<securegcm::UKey2Handshake> 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<securegcm::UKey2Handshake> 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<std::string, PendingConnectionInfo> pending_connections_;
// A map of endpoint id -> DiscoveredEndpoint.
absl::flat_hash_map<std::string, std::unique_ptr<DiscoveredEndpoint>>
discovered_endpoints_;
+160 -14
View File
@@ -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<DiscoveredEndpoint> 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<MockEndpointChannel> 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<EncryptionRunner>();
pcp_handler->OnEndpointFound(
client, std::make_unique<MockDiscoveredEndpoint>(MockDiscoveredEndpoint{
.endpoint_id = endpoint_id,
.endpoint_name = info.name,
.service_id = "service",
.medium = Medium::BLE,
}));
auto other_client = std::make_unique<ClientProxy>();
// 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<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(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<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(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<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(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<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(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<ClientProxy>();
auto ecm = std::make_unique<EndpointChannelManager>();
auto em = std::make_unique<EndpointManager>(ecm.get());
auto pcp_handler = std::make_unique<MockPcpHandler>(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
+8 -11
View File
@@ -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(
+72 -104
View File
@@ -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<BleAdvertisement::Version>(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<Pcp>(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
@@ -0,0 +1,187 @@
#include "core_v2/internal/bluetooth_device_name.h"
#include <inttypes.h>
#include <cstring>
#include <utility>
#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<Version>(
(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<Pcp>(*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<std::uint32_t>(*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<char>(
(static_cast<uint32_t>(Version::kV1) << 5) & kVersionBitmask);
// The lower 5 bits are the PCP.
version_and_pcp_byte |=
static_cast<char>(static_cast<uint32_t>(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
@@ -0,0 +1,73 @@
#ifndef CORE_V2_INTERNAL_BLUETOOTH_DEVICE_NAME_H_
#define CORE_V2_INTERNAL_BLUETOOTH_DEVICE_NAME_H_
#include <cstdint>
#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.
//
// <p>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_
@@ -0,0 +1,149 @@
#include "core_v2/internal/bluetooth_device_name.h"
#include <cstring>
#include <memory>
#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<BluetoothDeviceName::Version>(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<Pcp>(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
+11 -3
View File
@@ -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",
],
@@ -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
@@ -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<BleAdvertisementHeader::Version>(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());
}
+1 -2
View File
@@ -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;
+24 -24
View File
@@ -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());
}
@@ -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_; }
@@ -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());
@@ -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<char>(byte_value & 0x000000FF);
result_bytes_write_ptr++;
}
return result_bytes;
}
void BloomFilterBase::Add(const std::string& s) {
std::vector<std::int32_t> hashes = GetHashes(s);
for (int32_t hash : hashes) {
size_t position = static_cast<size_t>(hash) % bits_->Size();
bits_->Set(position, true);
}
}
bool BloomFilterBase::PossiblyContains(const std::string& s) {
std::vector<std::int32_t> hashes = GetHashes(s);
for (int32_t hash : hashes) {
size_t position = static_cast<size_t>(hash) % bits_->Size();
if (!bits_->Test(position)) {
return false;
}
}
return true;
}
std::vector<std::int32_t> BloomFilterBase::GetHashes(const std::string& s) {
std::vector<std::int32_t> 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<std::int32_t>(
hash64 & 0x00000000FFFFFFFF); // the lower 32 bits of the 64-bit hash
std::int32_t hash2 = static_cast<std::int32_t>(
(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<std::int32_t>(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
@@ -0,0 +1,87 @@
#ifndef CORE_V2_INTERNAL_MEDIUMS_BLOOM_FILTER_H_
#define CORE_V2_INTERNAL_MEDIUMS_BLOOM_FILTER_H_
#include <bitset>
#include <vector>
#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<std::int32_t> GetHashes(const std::string& s);
private:
int GetMinBytesForBits() const { return (bits_->Size() + 7) >> 3; }
BitSet* bits_;
};
template <size_t CapacityInBytes>
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<CapacityInBytes * 8> bits_;
} bits_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_MEDIUMS_BLOOM_FILTER_H_
@@ -0,0 +1,193 @@
#include "core_v2/internal/mediums/bloom_filter.h"
#include <algorithm>
#include "gtest/gtest.h"
namespace location {
namespace nearby {
namespace connections {
namespace mediums {
namespace {
const size_t kByteArrayLength = 100;
TEST(BloomFilterTest, EmptyFilterReturnsEmptyArray) {
BloomFilter<kByteArrayLength> 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<kByteArrayLength> 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<kByteArrayLength> 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<kByteArrayLength> 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<kByteArrayLength> 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<kByteArrayLength> bloom_filter;
EXPECT_FALSE(bloom_filter.PossiblyContains("ELEMENT_1"));
bloom_filter.Add("ELEMENT_1");
BloomFilter<kByteArrayLength> bloom_filter_copy_1{bloom_filter};
BloomFilter<kByteArrayLength> 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<kByteArrayLength> bloom_filter;
bloom_filter.Add("ELEMENT_1");
BloomFilter<kByteArrayLength> bloom_filter_move{std::move(bloom_filter)};
EXPECT_TRUE(bloom_filter_move.PossiblyContains("ELEMENT_1"));
}
TEST(BloomFilterTest, MoveAssignmentSuccess) {
BloomFilter<kByteArrayLength> bloom_filter;
bloom_filter.Add("ELEMENT_1");
BloomFilter<kByteArrayLength> 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.
*
* <p>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<kByteArrayLength> 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
+20 -7
View File
@@ -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",
],
)
@@ -0,0 +1,134 @@
#include "core_v2/internal/mediums/webrtc/connection_flow.h"
#include <memory>
#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> 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<webrtc::SessionDescriptionInterface>
ConnectionFlow::CreateOffer() {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
return std::unique_ptr<webrtc::SessionDescriptionInterface>();
}
std::unique_ptr<webrtc::SessionDescriptionInterface>
ConnectionFlow::CreateAnswer() {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
return std::unique_ptr<webrtc::SessionDescriptionInterface>();
}
bool ConnectionFlow::SetLocalSessionDescription(
std::unique_ptr<webrtc::SessionDescriptionInterface> sdp) {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
return false;
}
void ConnectionFlow::OnOfferReceived(
std::unique_ptr<webrtc::SessionDescriptionInterface> offer) {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
}
void ConnectionFlow::OnAnswerReceived(
std::unique_ptr<webrtc::SessionDescriptionInterface> answer) {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
}
bool ConnectionFlow::OnRemoteIceCandidatesReceived(
std::vector<webrtc::IceCandidateInterface*> ice_candidates) {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
return false;
}
api::ListenableFuture<rtc::scoped_refptr<webrtc::DataChannelInterface>>*
ConnectionFlow::GetDataChannel() {
return static_cast<
api::ListenableFuture<rtc::scoped_refptr<webrtc::DataChannelInterface>>*>(
&data_channel_future_);
}
bool ConnectionFlow::Close() {
MutexLock lock(&mutex_);
// TODO(bfranz): Implement
return false;
}
bool ConnectionFlow::InitPeerConnection(WebRtcMedium& webrtc_medium) {
Future<bool> success_future;
webrtc_medium.CreatePeerConnection(
&peer_connection_observer_,
[this, &success_future](
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection) {
peer_connection_ = peer_connection;
success_future.Set(true);
});
ExceptionOr<bool> 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<webrtc::DataChannelInterface> data_channel) {
// TODO(bfranz): Implement
return nullptr;
}
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
@@ -0,0 +1,133 @@
#ifndef CORE_V2_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_
#define CORE_V2_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_
#include <memory>
#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:
*
* <ul>
* <li>INITIALIZED: After construction.
* <li>CREATING_OFFER: After CreateOffer(). Local ice candidate collection
* begins.
* <li>WAITING_FOR_ANSWER: Until the remote peer sends their answer.
* <li>WAITING_TO_CONNECT: Until the data channel actually connects. Remote
* ice candidates should be added with OnRemoteIceCandidatesReceived as they are
* gathered.
* <li>CONNECTED: We successfully connected to the remote data
* channel.
* <li>ENDED: The final state that can occur from any of the previous
* states if we disconnect at any point in the flow.
* </ul>
*
* <p>Flow for an answerer:
*
* <ul>
* <li>INITIALIZED: After construction.
* <li>RECEIVED_OFFER: After onOfferReceived().
* <li>CREATING_ANSWER: After CreateAnswer(). Local ice candidate collection
* begins.
* <li>WAITING_TO_CONNECT: Until the data channel actually connects.
* Remote ice candidates should be added with OnRemoteIceCandidatesReceived as
* they are gathered.
* <li>CONNECTED: We successfully connected to the remote
* data channel.
* <li>ENDED: The final state that can occur from any of the
* previous states if we disconnect at any point in the flow.
* </ul>
*/
class ConnectionFlow {
public:
// This method blocks on the creation of the peer connection object.
static std::unique_ptr<ConnectionFlow> 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<webrtc::SessionDescriptionInterface> CreateOffer()
ABSL_LOCKS_EXCLUDED(mutex_);
// Create the answer that will be sent to the remote. Mirrors the behaviour of
// PeerConnectionInterface::CreateAnswer.
std::unique_ptr<webrtc::SessionDescriptionInterface> CreateAnswer()
ABSL_LOCKS_EXCLUDED(mutex_);
// Set the local session description. |sdp| was created via CreateOffer()
// or CreateAnswer().
bool SetLocalSessionDescription(
std::unique_ptr<webrtc::SessionDescriptionInterface> 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<webrtc::SessionDescriptionInterface> 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<webrtc::SessionDescriptionInterface> 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<webrtc::IceCandidateInterface*> ice_candidates)
ABSL_LOCKS_EXCLUDED(mutex_);
// Get a future for the data channel.
api::ListenableFuture<rtc::scoped_refptr<webrtc::DataChannelInterface>>*
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<webrtc::DataChannelInterface> 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<rtc::scoped_refptr<webrtc::DataChannelInterface>> data_channel_future_;
PeerConnectionObserverImpl peer_connection_observer_;
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_;
Mutex mutex_;
};
} // namespace mediums
} // namespace connections
} // namespace nearby
} // namespace location
#endif // CORE_V2_INTERNAL_MEDIUMS_WEBRTC_CONNECTION_FLOW_H_
@@ -0,0 +1,32 @@
#include "core_v2/internal/mediums/webrtc/connection_flow.h"
#include <memory>
#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<ConnectionFlow> 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
@@ -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<void()> data_channel_closed_cb = DefaultCallback<>();
// Called when a new message was received on the data channel.
std::function<void(ByteArray)> data_channel_message_received_cb =
DefaultCallback<ByteArray>();
// Called when the data channel indicates that the buffered amount has
// changed.
std::function<void()> 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_
@@ -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<void(const webrtc::IceCandidateInterface*)>
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_
@@ -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<webrtc::DataChannelInterface> 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
@@ -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<webrtc::DataChannelInterface> 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_
@@ -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 {
@@ -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 {
@@ -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 {
+1 -1
View File
@@ -3,7 +3,7 @@
#include <memory>
#include <utility>
#include "core/internal/message_lite.h"
#include "google/protobuf/message_lite.h"
#include "platform_v2/base/byte_array.h"
namespace location {
+2 -2
View File
@@ -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.
@@ -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<void()> 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<Payload>(std::move(payload));
const std::vector<std::string> endpoints =
std::vector<std::string>(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
+14 -52
View File
@@ -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<char>(
@@ -129,50 +126,15 @@ WifiLanServiceInfo::operator std::string() const {
// The lower 5 bits are the PCP.
version_and_pcp_byte |=
static_cast<char>(static_cast<uint32_t>(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
+7 -8
View File
@@ -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.
@@ -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<WifiLanServiceInfo::Version>(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<Pcp>(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
+2 -2
View File
@@ -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<InputFile> 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<InputStream> stream(raw_file);
Payload payload(std::move(stream));
EXPECT_EQ(payload.GetType(), Payload::Type::kStream);
+1 -5
View File
@@ -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",
+1 -1
View File
@@ -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",
],
)
+5 -1
View File
@@ -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<HashUtils> createHashUtils();
static Ptr<ThreadUtils> createThreadUtils();
static Ptr<SystemClock> createSystemClock();
static Ptr<InputFile> createInputFile(std::int64_t payload_id,
std::int64_t total_size);
static Ptr<OutputFile> 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<WebRtcSignalingMessenger> createWebRtcSignalingMessenger(
const std::string& self_id);
static std::string getDeviceId();
static std::string getPayloadPath(int64_t payload_id);
};
} // namespace platform
+1 -1
View File
@@ -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 {
+1
View File
@@ -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",
+17 -4
View File
@@ -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<SubmittableExecutor> ImplementationPlatform::createSingleThreadExecutor() {
return Ptr<SubmittableExecutor>(/*new SingleThreadExecutorImpl()*/);
}
@@ -87,6 +94,16 @@ Ptr<AtomicBoolean> ImplementationPlatform::createAtomicBoolean(
return Ptr<AtomicBoolean>(new AtomicBooleanImpl(initial_value));
}
Ptr<InputFile> ImplementationPlatform::createInputFile(
std::int64_t payload_id, std::int64_t total_size) {
return MakePtr(new InputFileImpl(getPayloadPath(payload_id), total_size));
}
Ptr<OutputFile> ImplementationPlatform::createOutputFile(
std::int64_t payload_id) {
return MakePtr(new OutputFileImpl(getPayloadPath(payload_id)));
}
Ptr<BluetoothClassicMedium>
ImplementationPlatform::createBluetoothClassicMedium() {
return Ptr<BluetoothClassicMedium>();
@@ -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
+1
View File
@@ -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",
+17 -4
View File
@@ -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<ScheduledExecutor> ImplementationPlatform::createScheduledExecutor() {
return Ptr<ScheduledExecutor>{};
}
@@ -80,6 +87,16 @@ Ptr<AtomicBoolean> ImplementationPlatform::createAtomicBoolean(
return Ptr<AtomicBoolean>{};
}
Ptr<InputFile> ImplementationPlatform::createInputFile(
std::int64_t payload_id, std::int64_t total_size) {
return MakePtr(new InputFileImpl(getPayloadPath(payload_id), total_size));
}
Ptr<OutputFile> ImplementationPlatform::createOutputFile(
std::int64_t payload_id) {
return MakePtr(new OutputFileImpl(getPayloadPath(payload_id)));
}
Ptr<BluetoothClassicMedium>
ImplementationPlatform::createBluetoothClassicMedium() {
return Ptr<BluetoothClassicMedium>();
@@ -116,10 +133,6 @@ Ptr<HashUtils> 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
+28
View File
@@ -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",
],
)
@@ -1,4 +1,4 @@
#include "platform/file_impl.h"
#include "platform/impl/shared/file_impl.h"
#include <cstddef>
#include <memory>
@@ -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 <cstdint>
#include <fstream>
@@ -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_
@@ -1,4 +1,4 @@
#include "platform/file_impl.h"
#include "platform/impl/shared/file_impl.h"
#include <cstring>
#include <fstream>
+42 -11
View File
@@ -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",
],
)
+4 -4
View File
@@ -5,13 +5,13 @@
#include <limits>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#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<GattCharacteristic> GetCharacteristic(
virtual absl::optional<GattCharacteristic> 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<ByteArray> ReadCharacteristic(
virtual absl::optional<ByteArray> 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<GattCharacteristic> CreateCharacteristic(
virtual absl::optional<GattCharacteristic> CreateCharacteristic(
absl::string_view service_uuid, absl::string_view characteristic_uuid,
const std::set<GattCharacteristic::Permission>& permissions,
const std::set<GattCharacteristic::Property>& properties) = 0;
+48 -33
View File
@@ -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<std::unique_ptr<BluetoothSocket>> 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<BluetoothSocket> 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<void(BluetoothDevice& device)> device_discovered_cb =
DefaultCallback<BluetoothDevice&>();
std::function<void(BluetoothDevice& device)> device_name_changed_cb =
DefaultCallback<BluetoothDevice&>();
std::function<void(BluetoothDevice& device)> device_lost_cb =
DefaultCallback<BluetoothDevice&>();
};
// 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<std::unique_ptr<BluetoothSocket>> ConnectToService(
BluetoothDevice* remote_device, absl::string_view service_uuid) = 0;
// On success, returns a new BluetoothSocket.
// On error, returns nullptr.
virtual std::unique_ptr<BluetoothSocket> 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<std::unique_ptr<BluetoothServerSocket>> ListenForService(
absl::string_view service_name, absl::string_view service_uuid) = 0;
// Returns nullptr error.
virtual std::unique_ptr<BluetoothServerSocket> ListenForService(
const std::string& service_name, const std::string& service_uuid) = 0;
};
} // namespace api
+7 -3
View File
@@ -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<T> : to synchronize on Callable<T> schduled to execute.
// - CountDownLatch : to ensure at least N threads are waiting.
// - file I/O
static std::unique_ptr<AtomicReference<absl::any>> CreateAtomicReferenceAny(
absl::any initial_value);
static std::unique_ptr<SettableFuture<absl::any>> CreateSettableFutureAny();
@@ -50,6 +53,9 @@ class ImplementationPlatform {
static std::unique_ptr<Mutex> CreateMutex(Mutex::Mode mode);
static std::unique_ptr<ConditionVariable> CreateConditionVariable(
Mutex* mutex);
static std::unique_ptr<InputFile> CreateInputFile(std::int64_t payload_id,
std::int64_t total_size);
static std::unique_ptr<OutputFile> CreateOutputFile(std::int64_t payload_id);
// Java-like Executors
static std::unique_ptr<SubmittableExecutor> CreateSingleThreadExecutor();
@@ -65,10 +71,8 @@ class ImplementationPlatform {
static std::unique_ptr<ServerSyncMedium> CreateServerSyncMedium();
static std::unique_ptr<WifiMedium> CreateWifiMedium();
static std::unique_ptr<WifiLanMedium> CreateWifiLanMedium();
static std::unique_ptr<WebRtcSignalingMessenger>
CreateWebRtcSignalingMessenger(absl::string_view self_id);
static std::unique_ptr<WebRtcMedium> CreateWebRtcMedium();
static std::string GetDeviceId();
static std::string GetPayloadPath(std::int64_t payload_id);
};
} // namespace api
+26 -26
View File
@@ -1,10 +1,11 @@
#ifndef PLATFORM_V2_API_WEBRTC_H_
#define PLATFORM_V2_API_WEBRTC_H_
#include <vector>
#include <memory>
#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<void(const ByteArray&)>;
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<webrtc::PeerConnectionInterface::IceServer>
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<void(rtc::scoped_refptr<webrtc::PeerConnectionInterface>)>;
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<WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id) = 0;
};
} // namespace api
+40 -1
View File
@@ -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 = [
+85
View File
@@ -0,0 +1,85 @@
#include "platform_v2/base/base_input_stream.h"
namespace location {
namespace nearby {
ExceptionOr<ByteArray> BaseInputStream::Read(std::int64_t size) {
if (!IsAvailable(size)) {
return ExceptionOr<ByteArray>{Exception::kIo};
}
ByteArray read_bytes{static_cast<size_t>(size)};
if (read_bytes.CopyAt(/*offset=*/0, buffer_,
/*source_offset=*/position_)) {
position_ += size;
return ExceptionOr<ByteArray>{read_bytes};
} else {
return ExceptionOr<ByteArray>{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<uint16_t>(data[0]) << 8 | static_cast<uint16_t>(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<uint32_t>(data[0]) << 24 |
static_cast<uint32_t>(data[1]) << 16 |
static_cast<uint32_t>(data[2]) << 8 | static_cast<uint32_t>(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<uint64_t>(data[0]) << 56 |
static_cast<uint64_t>(data[1]) << 48 |
static_cast<uint64_t>(data[2]) << 40 |
static_cast<uint64_t>(data[3]) << 32 |
static_cast<uint64_t>(data[4]) << 24 |
static_cast<uint64_t>(data[5]) << 16 |
static_cast<uint64_t>(data[6]) << 8 | static_cast<uint64_t>(data[7]);
}
ByteArray BaseInputStream::ReadBytes(int size) {
ExceptionOr<ByteArray> read_bytes_result = Read(size);
if (!read_bytes_result.ok()) {
return ByteArray{};
}
return read_bytes_result.GetResult();
}
} // namespace nearby
} // namespace location
+44
View File
@@ -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<ByteArray> 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_
-1
View File
@@ -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"
+3 -1
View File
@@ -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); }
+3 -2
View File
@@ -1,9 +1,10 @@
#ifndef PLATFORM_V2_BASE_EXCEPTION_H_
#define PLATFORM_V2_BASE_EXCEPTION_H_
#include <type_traits>
#include <utility>
#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<U> to ExceptionOr<T>.
template <typename U, typename = std::void_t<decltype(T{std::declval<U>()})>>
template <typename U, typename = absl::void_t<decltype(T{std::declval<U>()})>>
explicit ExceptionOr<T>(ExceptionOr<U> value) {
if (!value.ok()) {
exception_ = value.GetException();
+6
View File
@@ -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_
+191
View File
@@ -0,0 +1,191 @@
#include "platform_v2/base/medium_environment.h"
#include <atomic>
#include <cinttypes>
#include <new>
#include <type_traits>
#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<sizeof(MediumEnvironment),
alignof(MediumEnvironment)>
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<void()> 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
+113
View File
@@ -0,0 +1,113 @@
#ifndef PLATFORM_V2_BASE_MEDIUM_ENVIRONMENT_H_
#define PLATFORM_V2_BASE_MEDIUM_ENVIRONMENT_H_
#include <atomic>
#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<api::BluetoothDevice*, std::string> 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<void()> 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<api::BluetoothAdapter*, api::BluetoothDevice*>
bluetooth_adapters_;
absl::flat_hash_map<api::BluetoothClassicMedium*, BluetoothMediumContext>
bluetooth_mediums_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_BASE_MEDIUM_ENVIRONMENT_H_
+65 -18
View File
@@ -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",
],
)
+37 -22
View File
@@ -2,7 +2,7 @@
#include <string>
#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;
}
+3 -8
View File
@@ -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<void()> 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
@@ -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<sizeof(MediumEnvironment),
alignof(MediumEnvironment)>
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
@@ -1,47 +0,0 @@
#ifndef PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_
#define PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_
#include <new>
#include <string>
#include <type_traits>
#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<BluetoothAdapter*> bluetooth_adapters_
ABSL_GUARDED_BY(mutex_);
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_MEDIUM_ENVIRONMENT_H_
+21 -9
View File
@@ -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<SubmittableExecutor>
ImplementationPlatform::CreateSingleThreadExecutor() {
return absl::make_unique<g3::SingleThreadExecutor>();
@@ -76,6 +84,17 @@ std::unique_ptr<AtomicBoolean> ImplementationPlatform::CreateAtomicBoolean(
return absl::make_unique<g3::AtomicBoolean>(initial_value);
}
std::unique_ptr<InputFile> ImplementationPlatform::CreateInputFile(
std::int64_t payload_id, std::int64_t total_size) {
return absl::make_unique<shared::InputFile>(GetPayloadPath(payload_id),
total_size);
}
std::unique_ptr<OutputFile> ImplementationPlatform::CreateOutputFile(
std::int64_t payload_id) {
return absl::make_unique<shared::OutputFile>(GetPayloadPath(payload_id));
}
std::unique_ptr<BluetoothClassicMedium>
ImplementationPlatform::CreateBluetoothClassicMedium() {
return std::unique_ptr<BluetoothClassicMedium>();
@@ -102,11 +121,8 @@ std::unique_ptr<WifiLanMedium> ImplementationPlatform::CreateWifiLanMedium() {
return std::unique_ptr<WifiLanMedium>();
}
std::unique_ptr<WebRtcSignalingMessenger>
ImplementationPlatform::CreateWebRtcSignalingMessenger(
absl::string_view self_id) {
return std::unique_ptr<WebRtcSignalingMessenger>(
/*new FCMSignalingMessenger()*/);
std::unique_ptr<WebRtcMedium> ImplementationPlatform::CreateWebRtcMedium() {
return absl::make_unique<g3::WebRtcMedium>();
}
std::unique_ptr<Mutex> 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
+36
View File
@@ -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<rtc::Thread> 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<api::WebRtcSignalingMessenger>
WebRtcMedium::GetSignalingMessenger(absl::string_view self_id) {
// TODO(bfranz): Implement
return nullptr;
}
} // namespace g3
} // namespace nearby
} // namespace location
+35
View File
@@ -0,0 +1,35 @@
#ifndef PLATFORM_V2_IMPL_G3_WEBRTC_H_
#define PLATFORM_V2_IMPL_G3_WEBRTC_H_
#include <memory>
#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<api::WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id) override;
};
} // namespace g3
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_IMPL_G3_WEBRTC_H_
+26 -5
View File
@@ -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",
],
)
@@ -1,4 +1,4 @@
#include "platform_v2/public/file.h"
#include "platform_v2/impl/shared/file.h"
#include <cstddef>
#include <memory>
@@ -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
+53
View File
@@ -0,0 +1,53 @@
#ifndef PLATFORM_V2_IMPL_SHARED_FILE_H_
#define PLATFORM_V2_IMPL_SHARED_FILE_H_
#include <cstdint>
#include <fstream>
#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<ByteArray> 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_
@@ -1,4 +1,4 @@
#include "platform_v2/public/file.h"
#include "platform_v2/impl/shared/file.h"
#include <cstring>
#include <fstream>
@@ -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
+27 -12
View File
@@ -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",
],
+21 -15
View File
@@ -2,47 +2,53 @@
#define PLATFORM_V2_PUBLIC_FILE_H_
#include <cstdint>
#include <fstream>
#include <memory>
#include <string>
#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<ByteArray> 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<ByteArray> 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<api::InputFile> 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<api::OutputFile> impl_;
};
} // namespace nearby
+2 -2
View File
@@ -33,7 +33,7 @@ class Future final : public api::SettableFuture<T> {
ExceptionOr<T> Get() override {
auto ret_val = impl_->Get();
if (ret_val.ok()) {
T result = std::any_cast<T>(ret_val.result());
T result = absl::any_cast<T>(ret_val.result());
return ExceptionOr<T>{result};
} else {
return ExceptionOr<T>{ret_val.exception()};
@@ -46,7 +46,7 @@ class Future final : public api::SettableFuture<T> {
ExceptionOr<T> Get(absl::Duration timeout) override {
auto ret_val = impl_->Get(timeout);
if (ret_val.ok()) {
T result = std::any_cast<T>(ret_val.result());
T result = absl::any_cast<T>(ret_val.result());
return ExceptionOr<T>{result};
} else {
return ExceptionOr<T>{ret_val.exception()};
+1 -1
View File
@@ -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_
+44
View File
@@ -0,0 +1,44 @@
#ifndef PLATFORM_V2_PUBLIC_WEBRTC_H_
#define PLATFORM_V2_PUBLIC_WEBRTC_H_
#include <memory>
#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<api::WebRtcSignalingMessenger> GetSignalingMessenger(
absl::string_view self_id) {
return impl_->GetSignalingMessenger(self_id);
}
bool IsValid() const { return impl_ != nullptr; }
private:
std::unique_ptr<api::WebRtcMedium> impl_;
};
} // namespace nearby
} // namespace location
#endif // PLATFORM_V2_PUBLIC_WEBRTC_H_