mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-14 14:46:12 -04:00
Merge branch 'google3'
Change-Id: I8b28b1d3a5280581f20e4edf9144641a12df8879
This commit is contained in:
@@ -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",
|
||||
],
|
||||
|
||||
@@ -49,6 +49,7 @@ target_link_libraries(core_build_test
|
||||
core
|
||||
core_types
|
||||
platform_impl_g3
|
||||
platform_impl_shared_file
|
||||
platform_impl_shared_posix_lock
|
||||
platform_impl_shared_sample
|
||||
platform_port_string
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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_;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -1,27 +1,39 @@
|
||||
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",
|
||||
"//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/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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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_
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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",
|
||||
|
||||
@@ -5,14 +5,12 @@ target_sources(platform_utils
|
||||
PRIVATE
|
||||
base64_utils.cc
|
||||
cancelable_alarm.cc
|
||||
file_impl.cc
|
||||
pipe.cc
|
||||
prng.cc
|
||||
reliability_utils.cc
|
||||
PUBLIC
|
||||
base64_utils.h
|
||||
cancelable_alarm.h
|
||||
file_impl.h
|
||||
pipe.h
|
||||
prng.h
|
||||
reliability_utils.h
|
||||
@@ -53,7 +51,6 @@ add_executable(platform_test
|
||||
byte_array_test.cc
|
||||
container_of_test.cc
|
||||
exception_test.cc
|
||||
file_impl_test.cc
|
||||
pipe_test.cc
|
||||
prng_test.cc
|
||||
ptr_test.cc
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -18,6 +18,7 @@ target_link_libraries(platform_impl_g3
|
||||
"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"
|
||||
|
||||
@@ -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"
|
||||
@@ -36,6 +37,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()*/);
|
||||
}
|
||||
@@ -86,6 +93,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>();
|
||||
@@ -131,10 +148,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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -12,6 +12,7 @@ target_link_libraries(platform_impl_sample
|
||||
PUBLIC
|
||||
absl::time
|
||||
platform_api
|
||||
platform_impl_shared_file
|
||||
platform_port_string
|
||||
platform_types
|
||||
platform_utils
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -41,6 +41,26 @@ target_link_libraries(platform_impl_shared_posix_condition_variable
|
||||
absl::raw_logging_internal
|
||||
)
|
||||
|
||||
add_library(platform_impl_shared_file STATIC)
|
||||
|
||||
target_sources(platform_impl_shared_file
|
||||
PRIVATE
|
||||
"file_impl.cc"
|
||||
PUBLIC
|
||||
"file_impl.h"
|
||||
)
|
||||
|
||||
target_include_directories(platform_impl_shared_file
|
||||
PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(platform_impl_shared_file
|
||||
PUBLIC
|
||||
platform_api
|
||||
platform_types
|
||||
)
|
||||
|
||||
add_library(platform_impl_shared_atomic_boolean STATIC)
|
||||
|
||||
target_sources(platform_impl_shared_atomic_boolean
|
||||
|
||||
@@ -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 <cstdio>
|
||||
#include <cstring>
|
||||
+42
-11
@@ -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:any",
|
||||
"//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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
#ifndef PLATFORM_V2_API_WEBRTC_H_
|
||||
#define PLATFORM_V2_API_WEBRTC_H_
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include "platform_v2/base/byte_array.h"
|
||||
#include "absl/strings/string_view.h"
|
||||
#include "webrtc/api/peer_connection_interface.h"
|
||||
|
||||
namespace location {
|
||||
@@ -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
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -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
|
||||
@@ -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,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"
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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_
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,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_
|
||||
|
||||
@@ -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_
|
||||
@@ -200,11 +200,15 @@ message BandwidthUpgradeNegotiationFrame {
|
||||
optional BluetoothCredentials bluetooth_credentials = 4;
|
||||
optional WifiAwareCredentials wifi_aware_credentials = 5;
|
||||
optional WifiDirectCredentials wifi_direct_credentials = 6;
|
||||
|
||||
// Disable Encryption for this upgrade medium to improve throughput.
|
||||
optional bool supports_disabling_encryption = 7;
|
||||
}
|
||||
|
||||
// Accompanies CLIENT_INTRODUCTION events.
|
||||
message ClientIntroduction {
|
||||
optional string endpoint_id = 1;
|
||||
optional bool supports_disabling_encryption = 2;
|
||||
}
|
||||
|
||||
optional EventType event_type = 1;
|
||||
|
||||
@@ -219,6 +219,8 @@ enum LogSource {
|
||||
// Represents the OEM partners (like Samsung) that we're working with to
|
||||
// verify functionality on their devices.
|
||||
OEM_DEVICES = 4;
|
||||
// Represents the device for debugging.
|
||||
DEBUG_DEVICES = 5;
|
||||
}
|
||||
|
||||
// The Fast Share server action name.
|
||||
|
||||
Reference in New Issue
Block a user